react-native-flatlist-collapsible-header 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MomenMustafa45
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,119 @@
1
+ # react-native-flatlist-collapsible-header
2
+
3
+ A reusable **React Native FlatList with a smooth collapsible animated header**, built using **react-native-reanimated**.
4
+ The header smoothly fades, translates, and optionally scales away as the user scrolls.
5
+
6
+ Ideal for feeds, profile screens, dashboards, and modern mobile UIs.
7
+
8
+ ---
9
+
10
+ ## ✨ Features
11
+
12
+ - ⚡ Smooth animations powered by **react-native-reanimated**
13
+ - 🎯 Fully typed with **TypeScript**
14
+ - 🧩 Drop-in replacement for `FlatList`
15
+ - 🎛 Configurable animation behavior
16
+ - 📱 Optimized for performance
17
+ - 🧠 Clean and simple API
18
+
19
+ ---
20
+
21
+ ## 📸 Preview
22
+
23
+ Demo of the collapsible header behavior:
24
+
25
+ ```md
26
+ https://github.com/your-username/react-native-flatlist-collapsible-header/assets/demo.mp4
27
+ ```
28
+
29
+ ## 📥 Installation
30
+
31
+ Using npm:
32
+
33
+ ```sh
34
+ npm install react-native-flatlist-collapsible-header
35
+ or
36
+ yarn add react-native-flatlist-collapsible-header
37
+ ```
38
+
39
+ ## 🔧 Peer Dependencies
40
+
41
+ This library depends on the following peer dependency:
42
+
43
+ - **react-native-reanimated** `v2+`
44
+
45
+ If you haven't installed or configured it yet, follow the official installation guide:
46
+ https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation
47
+
48
+ ## 🚀 Usage
49
+
50
+ ```tsx
51
+ import { AnimatedHeaderFlatList } from 'react-native-flatlist-collapsible-header';
52
+
53
+ <AnimatedHeaderFlatList
54
+ data={data}
55
+ headerHeight={400}
56
+ renderHeader={<ListHeader />}
57
+ renderItem={({ item }) => <ListItem item={item} />}
58
+ keyExtractor={(item) => item.id}
59
+ />;
60
+ ```
61
+
62
+ ## 🧩 Props
63
+
64
+ ### `AnimatedHeaderFlatList<T>`
65
+
66
+ | Prop | Type | Required | Default | Description |
67
+ | --------------------------- | ---------------------- | -------- | ------- | ----------------------------------------------------- |
68
+ | `data` | `T[]` | ✅ | — | Data array for the FlatList |
69
+ | `renderItem` | `ListRenderItem<T>` | ✅ | — | Function to render each item |
70
+ | `keyExtractor` | `(item: T) => string` | ✅ | — | Extracts unique keys |
71
+ | `headerHeight` | `number` | ✅ | — | Height of the header in pixels |
72
+ | `renderHeader` | `React.ReactNode` | ✅ | — | Header component to render |
73
+ | `hideThreshold` | `number` | ❌ | `0.5` | Percentage (0–1) of header height before fully hidden |
74
+ | `headerContainerStyle` | `ViewStyle` | ❌ | — | Style override for the header container |
75
+ | `contentContainerStyle` | `StyleProp<ViewStyle>` | ❌ | — | Styles for FlatList content |
76
+ | `scrollEventThrottle` | `number` | ❌ | `16` | Scroll event frequency |
77
+ | `animationConfig.fadeOut` | `boolean` | ❌ | `true` | Fade header while scrolling |
78
+ | `animationConfig.translate` | `boolean` | ❌ | `true` | Translate header vertically |
79
+ | `animationConfig.scale` | `boolean` | ❌ | `false` | Scale header during scroll |
80
+ | `onScroll` | `(event) => void` | ❌ | — | Optional scroll listener |
81
+
82
+ > ✅ All standard `FlatList` props are supported.
83
+
84
+ ## 🤝 Contributing
85
+
86
+ Contributions are welcome!
87
+
88
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
89
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
90
+ - [Code of conduct](CODE_OF_CONDUCT.md)
91
+
92
+ ## 🎛 Animation Configuration Example
93
+
94
+ ```tsx
95
+ <AnimatedHeaderFlatList
96
+ headerHeight={300}
97
+ renderHeader={<Header />}
98
+ animationConfig={{
99
+ fadeOut: true,
100
+ translate: true,
101
+ scale: true,
102
+ }}
103
+ />
104
+ ```
105
+
106
+ ## ⚠️ Notes & Best Practices
107
+
108
+ - Requires **react-native-reanimated v2+**
109
+ - Header uses **absolute positioning**
110
+ - Avoid using margins for header layout
111
+ - Best performance with `scrollEventThrottle={16}`
112
+
113
+ ## 📄 License
114
+
115
+ MIT © Momen Mustafa
116
+
117
+ ---
118
+
119
+ Made with ❤️ using [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import { StyleSheet, View } from 'react-native';
5
+ import { FlatList } from 'react-native';
6
+ import Animated, { useSharedValue, useAnimatedStyle, interpolate, Extrapolation } from 'react-native-reanimated';
7
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
+ /**
9
+ * A FlatList component with an animated header that hides on scroll.
10
+ * @typeparam T - Type of items in the FlatList
11
+ */
12
+ export function AnimatedHeaderFlatList({
13
+ headerHeight,
14
+ renderHeader,
15
+ onScroll,
16
+ contentContainerStyle,
17
+ headerContainerStyle,
18
+ hideThreshold = 0.5,
19
+ scrollEventThrottle = 16,
20
+ animationConfig = {
21
+ fadeOut: true,
22
+ translate: true,
23
+ scale: false
24
+ },
25
+ ...props
26
+ }) {
27
+ const scrollY = useSharedValue(0);
28
+ const calculatedHideThreshold = headerHeight * hideThreshold;
29
+ const scrollHandler = event => {
30
+ const offsetY = event.nativeEvent.contentOffset.y;
31
+ scrollY.value = offsetY;
32
+ if (onScroll) {
33
+ onScroll(event);
34
+ }
35
+ };
36
+ const headerContainerStyles = useAnimatedStyle(() => {
37
+ const visibility = interpolate(scrollY.value, [0, calculatedHideThreshold], [1, 0], Extrapolation.CLAMP);
38
+ let animatedStyle = {};
39
+ if (animationConfig.fadeOut) {
40
+ animatedStyle = {
41
+ ...animatedStyle,
42
+ opacity: visibility
43
+ };
44
+ }
45
+ let transforms = [];
46
+ if (animationConfig.translate) {
47
+ transforms.push({
48
+ translateY: interpolate(visibility, [1, 0], [0, -20])
49
+ });
50
+ }
51
+ if (animationConfig.scale) {
52
+ transforms.push({
53
+ scale: interpolate(visibility, [0, 1], [0.8, 1])
54
+ });
55
+ }
56
+ if (transforms.length > 0) {
57
+ animatedStyle = {
58
+ ...animatedStyle,
59
+ transform: transforms
60
+ };
61
+ }
62
+ return animatedStyle;
63
+ });
64
+ return /*#__PURE__*/_jsxs(View, {
65
+ style: styles.listContainer,
66
+ children: [/*#__PURE__*/_jsx(Animated.View, {
67
+ style: [styles.headerViewContainer, {
68
+ height: headerHeight
69
+ }, headerContainerStyles, headerContainerStyle],
70
+ pointerEvents: "box-none",
71
+ children: renderHeader
72
+ }), /*#__PURE__*/_jsx(FlatList, {
73
+ ...props,
74
+ onScroll: scrollHandler,
75
+ scrollEventThrottle: scrollEventThrottle,
76
+ contentContainerStyle: [{
77
+ paddingTop: headerHeight
78
+ }, contentContainerStyle]
79
+ })]
80
+ });
81
+ }
82
+ const styles = StyleSheet.create({
83
+ listContainer: {
84
+ position: 'relative'
85
+ },
86
+ headerViewContainer: {
87
+ backgroundColor: 'transparent',
88
+ width: '100%',
89
+ flex: 1,
90
+ position: 'absolute',
91
+ top: 0,
92
+ left: 0
93
+ }
94
+ });
95
+ //# sourceMappingURL=AnimatedHeaderFlatList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","StyleSheet","View","FlatList","Animated","useSharedValue","useAnimatedStyle","interpolate","Extrapolation","jsx","_jsx","jsxs","_jsxs","AnimatedHeaderFlatList","headerHeight","renderHeader","onScroll","contentContainerStyle","headerContainerStyle","hideThreshold","scrollEventThrottle","animationConfig","fadeOut","translate","scale","props","scrollY","calculatedHideThreshold","scrollHandler","event","offsetY","nativeEvent","contentOffset","y","value","headerContainerStyles","visibility","CLAMP","animatedStyle","opacity","transforms","push","translateY","length","transform","style","styles","listContainer","children","headerViewContainer","height","pointerEvents","paddingTop","create","position","backgroundColor","width","flex","top","left"],"sourceRoot":"../../src","sources":["AnimatedHeaderFlatList.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { AnimatedHeaderFlatList } from "./AnimatedHeaderFlatList.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["AnimatedHeaderFlatList"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,sBAAsB,QAAQ,6BAA0B","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { type FlatListProps, type StyleProp, type ViewStyle } from 'react-native';
3
+ export type AnimatedHeaderFlatListProps<T> = FlatListProps<T> & {
4
+ /** Height of the header in pixels */
5
+ headerHeight: number;
6
+ /** Function that renders the header component */
7
+ renderHeader: React.ReactNode;
8
+ /** Additional styles for the header container */
9
+ headerContainerStyle?: ViewStyle;
10
+ /** Additional styles for the content container */
11
+ contentContainerStyle?: StyleProp<ViewStyle>;
12
+ /** Threshold for hiding the header (0 to 1) relative to headerHeight */
13
+ hideThreshold?: number;
14
+ scrollEventThrottle?: number;
15
+ /** Animation configuration */
16
+ animationConfig?: {
17
+ /** Whether to fade out the header */
18
+ fadeOut?: boolean;
19
+ /** Whether to translate the header */
20
+ translate?: boolean;
21
+ /** Whether to scale the header */
22
+ scale?: boolean;
23
+ };
24
+ };
25
+ /**
26
+ * A FlatList component with an animated header that hides on scroll.
27
+ * @typeparam T - Type of items in the FlatList
28
+ */
29
+ export declare function AnimatedHeaderFlatList<T>({ headerHeight, renderHeader, onScroll, contentContainerStyle, headerContainerStyle, hideThreshold, scrollEventThrottle, animationConfig, ...props }: AnimatedHeaderFlatListProps<T>): import("react/jsx-runtime").JSX.Element;
30
+ //# sourceMappingURL=AnimatedHeaderFlatList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnimatedHeaderFlatList.d.ts","sourceRoot":"","sources":["../../../src/AnimatedHeaderFlatList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAGL,KAAK,aAAa,EAGlB,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAStB,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IAC9D,qCAAqC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,SAAS,CAAC;IACjC,kDAAkD;IAClD,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7C,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,8BAA8B;IAC9B,eAAe,CAAC,EAAE;QAChB,qCAAqC;QACrC,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,sCAAsC;QACtC,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,kCAAkC;QAClC,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;CACH,CAAC;AAEF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,EACxC,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,qBAAqB,EACrB,oBAAoB,EACpB,aAAmB,EACnB,mBAAwB,EACxB,eAIC,EACD,GAAG,KAAK,EACT,EAAE,2BAA2B,CAAC,CAAC,CAAC,2CAwEhC"}
@@ -0,0 +1,3 @@
1
+ export { AnimatedHeaderFlatList } from './AnimatedHeaderFlatList';
2
+ export type { AnimatedHeaderFlatListProps } from './AnimatedHeaderFlatList';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,164 @@
1
+ {
2
+ "name": "react-native-flatlist-collapsible-header",
3
+ "version": "1.0.1",
4
+ "description": "A React Native FlatList with collapsible header that smoothly disappears on scroll",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
34
+ "scripts": {
35
+ "example": "yarn workspace react-native-flatlist-collapsible-header-example",
36
+ "clean": "del-cli lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
+ "test": "jest",
41
+ "release": "release-it --only-version",
42
+ "build": "tsc"
43
+ },
44
+ "keywords": [
45
+ "react-native",
46
+ "ios",
47
+ "android"
48
+ ],
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/MomenMustafa45/react-native-flatlist-collapisable-header.git"
52
+ },
53
+ "author": "MomenMustafa45 <momenkara2@gmail.com> (https://github.com/MomenMustafa45)",
54
+ "license": "MIT",
55
+ "bugs": {
56
+ "url": "https://github.com/MomenMustafa45/react-native-flatlist-collapisable-header/issues"
57
+ },
58
+ "homepage": "https://github.com/MomenMustafa45/react-native-flatlist-collapisable-header#readme",
59
+ "publishConfig": {
60
+ "registry": "https://registry.npmjs.org/"
61
+ },
62
+ "devDependencies": {
63
+ "@commitlint/config-conventional": "^19.8.1",
64
+ "@eslint/compat": "^1.3.2",
65
+ "@eslint/eslintrc": "^3.3.1",
66
+ "@eslint/js": "^9.35.0",
67
+ "@react-native/babel-preset": "0.83.0",
68
+ "@react-native/eslint-config": "0.83.0",
69
+ "@release-it/conventional-changelog": "^10.0.1",
70
+ "@types/jest": "^29.5.14",
71
+ "@types/react": "^19.1.12",
72
+ "commitlint": "^19.8.1",
73
+ "del-cli": "^6.0.0",
74
+ "eslint": "^9.35.0",
75
+ "eslint-config-prettier": "^10.1.8",
76
+ "eslint-plugin-prettier": "^5.5.4",
77
+ "jest": "^29.7.0",
78
+ "lefthook": "^2.0.3",
79
+ "prettier": "^2.8.8",
80
+ "react": "19.1.0",
81
+ "react-native": "0.81.5",
82
+ "react-native-builder-bob": "^0.40.17",
83
+ "react-native-reanimated": "^4.2.1",
84
+ "react-native-worklets": "0.5.1",
85
+ "release-it": "^19.0.4",
86
+ "typescript": "^5.9.3"
87
+ },
88
+ "peerDependencies": {
89
+ "react": "*",
90
+ "react-native": "*",
91
+ "react-native-reanimated": "^4.1.1"
92
+ },
93
+ "workspaces": [
94
+ "example"
95
+ ],
96
+ "packageManager": "yarn@4.11.0",
97
+ "react-native-builder-bob": {
98
+ "source": "src",
99
+ "output": "lib",
100
+ "targets": [
101
+ [
102
+ "module",
103
+ {
104
+ "esm": true
105
+ }
106
+ ],
107
+ [
108
+ "typescript",
109
+ {
110
+ "project": "tsconfig.build.json"
111
+ }
112
+ ]
113
+ ]
114
+ },
115
+ "prettier": {
116
+ "quoteProps": "consistent",
117
+ "singleQuote": true,
118
+ "tabWidth": 2,
119
+ "trailingComma": "es5",
120
+ "useTabs": false
121
+ },
122
+ "jest": {
123
+ "preset": "react-native",
124
+ "modulePathIgnorePatterns": [
125
+ "<rootDir>/example/node_modules",
126
+ "<rootDir>/lib/"
127
+ ]
128
+ },
129
+ "commitlint": {
130
+ "extends": [
131
+ "@commitlint/config-conventional"
132
+ ]
133
+ },
134
+ "release-it": {
135
+ "git": {
136
+ "commitMessage": "chore: release ${version}",
137
+ "tagName": "v${version}"
138
+ },
139
+ "npm": {
140
+ "publish": true
141
+ },
142
+ "github": {
143
+ "release": true
144
+ },
145
+ "plugins": {
146
+ "@release-it/conventional-changelog": {
147
+ "preset": {
148
+ "name": "angular"
149
+ }
150
+ }
151
+ }
152
+ },
153
+ "create-react-native-library": {
154
+ "type": "library",
155
+ "languages": "js",
156
+ "tools": [
157
+ "eslint",
158
+ "jest",
159
+ "lefthook",
160
+ "release-it"
161
+ ],
162
+ "version": "0.57.0"
163
+ }
164
+ }
@@ -0,0 +1,144 @@
1
+ import React from 'react';
2
+ import {
3
+ StyleSheet,
4
+ View,
5
+ type FlatListProps,
6
+ type NativeScrollEvent,
7
+ type NativeSyntheticEvent,
8
+ type StyleProp,
9
+ type ViewStyle,
10
+ } from 'react-native';
11
+ import { FlatList } from 'react-native';
12
+ import Animated, {
13
+ useSharedValue,
14
+ useAnimatedStyle,
15
+ interpolate,
16
+ Extrapolation,
17
+ } from 'react-native-reanimated';
18
+
19
+ export type AnimatedHeaderFlatListProps<T> = FlatListProps<T> & {
20
+ /** Height of the header in pixels */
21
+ headerHeight: number;
22
+ /** Function that renders the header component */
23
+ renderHeader: React.ReactNode;
24
+ /** Additional styles for the header container */
25
+ headerContainerStyle?: ViewStyle;
26
+ /** Additional styles for the content container */
27
+ contentContainerStyle?: StyleProp<ViewStyle>;
28
+ /** Threshold for hiding the header (0 to 1) relative to headerHeight */
29
+ hideThreshold?: number;
30
+ scrollEventThrottle?: number;
31
+ /** Animation configuration */
32
+ animationConfig?: {
33
+ /** Whether to fade out the header */
34
+ fadeOut?: boolean;
35
+ /** Whether to translate the header */
36
+ translate?: boolean;
37
+ /** Whether to scale the header */
38
+ scale?: boolean;
39
+ };
40
+ };
41
+
42
+ /**
43
+ * A FlatList component with an animated header that hides on scroll.
44
+ * @typeparam T - Type of items in the FlatList
45
+ */
46
+ export function AnimatedHeaderFlatList<T>({
47
+ headerHeight,
48
+ renderHeader,
49
+ onScroll,
50
+ contentContainerStyle,
51
+ headerContainerStyle,
52
+ hideThreshold = 0.5,
53
+ scrollEventThrottle = 16,
54
+ animationConfig = {
55
+ fadeOut: true,
56
+ translate: true,
57
+ scale: false,
58
+ },
59
+ ...props
60
+ }: AnimatedHeaderFlatListProps<T>) {
61
+ const scrollY = useSharedValue(0);
62
+
63
+ const calculatedHideThreshold = headerHeight * hideThreshold;
64
+
65
+ const scrollHandler = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
66
+ const offsetY = event.nativeEvent.contentOffset.y;
67
+ scrollY.value = offsetY;
68
+
69
+ if (onScroll) {
70
+ onScroll(event);
71
+ }
72
+ };
73
+
74
+ const headerContainerStyles = useAnimatedStyle(() => {
75
+ const visibility = interpolate(
76
+ scrollY.value,
77
+ [0, calculatedHideThreshold],
78
+ [1, 0],
79
+ Extrapolation.CLAMP
80
+ );
81
+
82
+ let animatedStyle: ViewStyle = {};
83
+
84
+ if (animationConfig.fadeOut) {
85
+ animatedStyle = { ...animatedStyle, opacity: visibility };
86
+ }
87
+
88
+ let transforms: any[] = [];
89
+
90
+ if (animationConfig.translate) {
91
+ transforms.push({
92
+ translateY: interpolate(visibility, [1, 0], [0, -20]),
93
+ });
94
+ }
95
+
96
+ if (animationConfig.scale) {
97
+ transforms.push({ scale: interpolate(visibility, [0, 1], [0.8, 1]) });
98
+ }
99
+
100
+ if (transforms.length > 0) {
101
+ animatedStyle = { ...animatedStyle, transform: transforms };
102
+ }
103
+
104
+ return animatedStyle;
105
+ });
106
+
107
+ return (
108
+ <View style={styles.listContainer}>
109
+ <Animated.View
110
+ style={[
111
+ styles.headerViewContainer,
112
+ { height: headerHeight },
113
+ headerContainerStyles,
114
+ headerContainerStyle,
115
+ ]}
116
+ pointerEvents="box-none"
117
+ >
118
+ {renderHeader}
119
+ </Animated.View>
120
+
121
+ <FlatList<T>
122
+ {...props}
123
+ onScroll={scrollHandler}
124
+ scrollEventThrottle={scrollEventThrottle}
125
+ contentContainerStyle={[
126
+ { paddingTop: headerHeight },
127
+ contentContainerStyle,
128
+ ]}
129
+ />
130
+ </View>
131
+ );
132
+ }
133
+
134
+ const styles = StyleSheet.create({
135
+ listContainer: { position: 'relative' },
136
+ headerViewContainer: {
137
+ backgroundColor: 'transparent',
138
+ width: '100%',
139
+ flex: 1,
140
+ position: 'absolute',
141
+ top: 0,
142
+ left: 0,
143
+ },
144
+ });
package/src/index.tsx ADDED
@@ -0,0 +1,2 @@
1
+ export { AnimatedHeaderFlatList } from './AnimatedHeaderFlatList';
2
+ export type { AnimatedHeaderFlatListProps } from './AnimatedHeaderFlatList';