├── .nvmrc ├── .watchmanconfig ├── example ├── App.js ├── assets │ ├── cabin.jpg │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ └── adaptive-icon.png ├── tsconfig.json ├── src │ ├── utils │ │ ├── index.ts │ │ └── enums.ts │ ├── components │ │ ├── index.ts │ │ ├── HeaderComponent.tsx │ │ ├── RoundButton.tsx │ │ ├── Card.tsx │ │ ├── HeaderNavBar.tsx │ │ └── TopNavBar.tsx │ ├── types.d.ts │ ├── icons │ │ ├── ArrowLeft.tsx │ │ ├── ArrowRight.tsx │ │ └── Share.tsx │ ├── screens │ │ ├── OnlyForegroundExample.tsx │ │ ├── ImageForegroundExample.tsx │ │ ├── HeaderNavBarExample.tsx │ │ ├── SafeAreaViewExample.tsx │ │ ├── HeaderNavbarFlatListExample.tsx │ │ ├── RefreshControlExample.tsx │ │ └── ExamplesDirectory.tsx │ └── App.tsx ├── babel.config.js ├── app.json ├── README.md ├── webpack.config.js ├── package.json └── metro.config.js ├── src ├── index.tsx ├── constants.ts ├── components │ ├── index.ts │ ├── AnimatedNavbar.tsx │ ├── AnimatedFlatList.tsx │ ├── AnimatedScrollView.tsx │ └── AnimatedHeader.tsx ├── hooks │ ├── useAnimateNavbar.ts │ └── useAnimateScrollView.ts ├── types.d.ts └── __tests__ │ └── useAnimateNavbar.test.ts ├── .gitattributes ├── tsconfig.build.json ├── babel.config.js ├── preview-ios.gif ├── .yarnrc ├── .editorconfig ├── lefthook.yml ├── .github ├── dependabot.yml ├── workflows │ ├── pull-request.yml │ ├── codeql.yml │ └── ci.yml ├── actions │ └── setup │ │ └── action.yml └── PULL_REQUEST_TEMPLATE.md ├── tsconfig.json ├── scripts └── bootstrap.js ├── .gitignore ├── LICENSE ├── CHANGELOG.md ├── CONTRIBUTING.md ├── package.json ├── README.md └── CODE_OF_CONDUCT.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.18.1 2 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | export { default } from './src/App'; 2 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | export { AnimatedScrollView, AnimatedFlatList } from './components'; 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const IMG_HEADER_HEIGHT = 300; 2 | export const HEADER_HEIGHT = 90; 3 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "extends": "./tsconfig", 4 | "exclude": ["example"] 5 | } 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /preview-ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kanelloc/react-native-animated-header-scroll-view/HEAD/preview-ios.gif -------------------------------------------------------------------------------- /example/assets/cabin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kanelloc/react-native-animated-header-scroll-view/HEAD/example/assets/cabin.jpg -------------------------------------------------------------------------------- /example/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kanelloc/react-native-animated-header-scroll-view/HEAD/example/assets/icon.png -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /example/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kanelloc/react-native-animated-header-scroll-view/HEAD/example/assets/favicon.png -------------------------------------------------------------------------------- /example/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kanelloc/react-native-animated-header-scroll-view/HEAD/example/assets/splash.png -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { AnimatedScrollView } from './AnimatedScrollView'; 2 | export { AnimatedFlatList } from './AnimatedFlatList'; 3 | -------------------------------------------------------------------------------- /example/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kanelloc/react-native-animated-header-scroll-view/HEAD/example/assets/adaptive-icon.png -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig", 3 | "compilerOptions": { 4 | // Avoid expo-cli auto-generating a tsconfig 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { Platform } from 'react-native'; 2 | 3 | export const isIOS = Platform.OS === 'ios'; 4 | 5 | export const data = Array.from(Array(20).keys()); 6 | -------------------------------------------------------------------------------- /example/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { Card } from './Card'; 2 | export { TopNavBar } from './TopNavBar'; 3 | export { HeaderNavBar } from './HeaderNavBar'; 4 | export { RoundButton } from './RoundButton'; 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /example/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type RootStackParamList = { 2 | ExamplesDirectory: undefined; 3 | HeaderNavBarExample: undefined; 4 | SafeAreaViewExample: undefined; 5 | SimpleExample: undefined; 6 | ImageForegroundExample: undefined; 7 | OnlyForegroundExample: undefined; 8 | RefreshControlExample: undefined; 9 | HeaderNavbarFlatListExample: undefined; 10 | }; 11 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | commands: 4 | lint: 5 | files: git diff --name-only @{push} 6 | glob: "*.{js,ts,jsx,tsx}" 7 | run: npx eslint {files} 8 | types: 9 | files: git diff --name-only @{push} 10 | glob: "*.{js,ts, jsx, tsx}" 11 | run: npx tsc --noEmit 12 | commit-msg: 13 | parallel: true 14 | commands: 15 | commitlint: 16 | run: npx commitlint --edit 17 | -------------------------------------------------------------------------------- /example/src/utils/enums.ts: -------------------------------------------------------------------------------- 1 | export enum SCREENS { 2 | EXAMPLES_DIRECTORY = 'ExamplesDirectory', 3 | HEADER_NAVBAR_EXAMPLE = 'HeaderNavBarExample', 4 | SAFE_AREA_VIEW_EXAMPLE = 'SafeAreaViewExample', 5 | IMAGE_FOREGROUND_EXAMPLE = 'ImageForegroundExample', 6 | ONLY_FOREGROUND_EXAMPLE = 'OnlyForegroundExample', 7 | REFRESH_CONTROL_EXAMPLE = 'RefreshControlExample', 8 | HEADER_NAVBAR_FLATLIST_EXAMPLE = 'HeaderNavbarFlatListExample', 9 | } 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | target-branch: "main" 13 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = function (api) { 5 | api.cache(true); 6 | 7 | return { 8 | presets: ['babel-preset-expo'], 9 | plugins: [ 10 | [ 11 | 'module-resolver', 12 | { 13 | extensions: ['.tsx', '.ts', '.js', '.json'], 14 | alias: { 15 | // For development, we want to alias the library to the source 16 | [pak.name]: path.join(__dirname, '..', pak.source), 17 | }, 18 | }, 19 | ], 20 | ], 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /example/src/components/HeaderComponent.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from 'react-native'; 2 | import React from 'react'; 3 | 4 | export const HeaderComponent = () => ( 5 | 6 | 7 | Foreground component 8 | 9 | 10 | ); 11 | 12 | const styles = StyleSheet.create({ 13 | container: { 14 | flex: 1, 15 | backgroundColor: 'transparent', 16 | alignItems: 'center', 17 | justifyContent: 'center', 18 | }, 19 | titleContainer: { 20 | backgroundColor: 'white', 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /example/src/icons/ArrowLeft.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Svg, { Path } from 'react-native-svg'; 3 | import { StyleSheet } from 'react-native'; 4 | 5 | const ArrowLeft = (props: any) => ( 6 | 13 | 21 | 22 | ); 23 | 24 | export default ArrowLeft; 25 | -------------------------------------------------------------------------------- /example/src/components/RoundButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, TouchableOpacity } from 'react-native'; 3 | 4 | type Props = { 5 | icon: JSX.Element; 6 | onPress: () => void; 7 | }; 8 | export const RoundButton = ({ icon, onPress }: Props) => { 9 | return ( 10 | 11 | {icon} 12 | 13 | ); 14 | }; 15 | const styles = StyleSheet.create({ 16 | container: { 17 | backgroundColor: 'white', 18 | width: 30, 19 | height: 30, 20 | borderRadius: 15, 21 | alignItems: 'center', 22 | justifyContent: 'center', 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: PR check 2 | on: [pull_request] 3 | 4 | concurrency: 5 | group: ${{ github.workflow }}-${{ github.ref }} 6 | cancel-in-progress: true 7 | 8 | jobs: 9 | Validate: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | 15 | - name: Setup 16 | uses: ./.github/actions/setup 17 | 18 | - name: Lint files 19 | run: yarn lint 20 | 21 | - name: Typecheck files 22 | run: yarn typecheck 23 | 24 | - name: Run unit tests 25 | run: yarn test --maxWorkers=2 --coverage 26 | 27 | - name: Build package 28 | run: yarn prepack 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/hooks/useAnimateNavbar.ts: -------------------------------------------------------------------------------- 1 | import type { Animated } from 'react-native'; 2 | 3 | export const useAnimateNavbar = ( 4 | scroll: Animated.Value, 5 | imageHeight: number, 6 | headerHeight: number 7 | ) => { 8 | const HEADER_HEIGHT_DIFFERENCE = imageHeight - headerHeight; 9 | const headerOpacity = scroll.interpolate({ 10 | inputRange: [0, HEADER_HEIGHT_DIFFERENCE * 0.75, HEADER_HEIGHT_DIFFERENCE], 11 | outputRange: [0, 0, 1], 12 | extrapolate: 'clamp', 13 | }); 14 | const overflowHeaderOpacity = scroll.interpolate({ 15 | inputRange: [0, HEADER_HEIGHT_DIFFERENCE * 0.75, HEADER_HEIGHT_DIFFERENCE], 16 | outputRange: [1, 1, 0], 17 | extrapolate: 'clamp', 18 | }); 19 | 20 | return [headerOpacity, overflowHeaderOpacity]; 21 | }; 22 | -------------------------------------------------------------------------------- /example/src/icons/ArrowRight.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import type { SvgProps } from 'react-native-svg'; 4 | import Svg, { Path } from 'react-native-svg'; 5 | 6 | export const ArrowRight = ({ 7 | color = '#000000', 8 | style, 9 | ...props 10 | }: SvgProps) => ( 11 | 19 | 23 | 24 | ); 25 | -------------------------------------------------------------------------------- /example/src/screens/OnlyForegroundExample.tsx: -------------------------------------------------------------------------------- 1 | import { AnimatedScrollView } from '@kanelloc/react-native-animated-header-scroll-view'; 2 | import { Card, TopNavBar, HeaderNavBar } from '../components'; 3 | import * as React from 'react'; 4 | import { HeaderComponent } from '../components/HeaderComponent'; 5 | import { data, isIOS } from '../utils'; 6 | 7 | export const OnlyForegroundExample = () => { 8 | return ( 9 | } 12 | topBarHeight={isIOS ? 90 : 70} 13 | HeaderNavbarComponent={} 14 | TopNavBarComponent={} 15 | > 16 | {data.map((e) => { 17 | return ; 18 | })} 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /example/src/components/Card.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from 'react-native'; 2 | import * as React from 'react'; 3 | 4 | type Props = { 5 | item: any; 6 | }; 7 | export const Card = ({ item }: Props) => ( 8 | 9 | Value {item} 10 | 11 | ); 12 | 13 | const styles = StyleSheet.create({ 14 | card: { 15 | flexDirection: 'row', 16 | alignItems: 'center', 17 | shadowColor: '#402583', 18 | backgroundColor: '#ffffff', 19 | shadowOffset: { 20 | width: 0, 21 | height: 0, 22 | }, 23 | shadowOpacity: 0.1, 24 | shadowRadius: 6, 25 | elevation: 1, 26 | borderRadius: 10, 27 | marginHorizontal: 12, 28 | marginTop: 12, 29 | paddingHorizontal: 16, 30 | paddingVertical: 12, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup Node.js and install dependencies 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup Node.js 8 | uses: actions/setup-node@v3 9 | with: 10 | node-version-file: .nvmrc 11 | 12 | - name: Cache dependencies 13 | id: yarn-cache 14 | uses: actions/cache@v3 15 | with: 16 | path: | 17 | **/node_modules 18 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 19 | restore-keys: | 20 | ${{ runner.os }}-yarn- 21 | 22 | - name: Install dependencies 23 | if: steps.yarn-cache.outputs.cache-hit != 'true' 24 | run: | 25 | yarn install --cwd example --frozen-lockfile 26 | yarn install --frozen-lockfile 27 | shell: bash 28 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "example", 4 | "slug": "example", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "updates": { 15 | "fallbackToCacheTimeout": 0 16 | }, 17 | "assetBundlePatterns": [ 18 | "**/*" 19 | ], 20 | "ios": { 21 | "supportsTablet": true 22 | }, 23 | "android": { 24 | "adaptiveIcon": { 25 | "foregroundImage": "./assets/adaptive-icon.png", 26 | "backgroundColor": "#FFFFFF" 27 | } 28 | }, 29 | "web": { 30 | "favicon": "./assets/favicon.png" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /example/src/screens/ImageForegroundExample.tsx: -------------------------------------------------------------------------------- 1 | import { AnimatedScrollView } from '@kanelloc/react-native-animated-header-scroll-view'; 2 | import { Card, TopNavBar, HeaderNavBar } from '../components'; 3 | import * as React from 'react'; 4 | import { HeaderComponent } from '../components/HeaderComponent'; 5 | import { data, isIOS } from '../utils'; 6 | 7 | export const ImageForegroundExample = () => { 8 | return ( 9 | } 11 | topBarHeight={isIOS ? 90 : 70} 12 | HeaderNavbarComponent={} 13 | TopNavBarComponent={} 14 | headerImage={require('../../assets/cabin.jpg')} 15 | > 16 | {data.map((e) => { 17 | return ; 18 | })} 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ main, develop ] 6 | pull_request: 7 | branches: [ main, develop ] 8 | 9 | jobs: 10 | CodeQL-Build: 11 | name: Analyze 12 | runs-on: ubuntu-latest 13 | permissions: 14 | security-events: write 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | language: [ 'javascript' ] 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v3 24 | 25 | - name: Initialize CodeQL 26 | uses: github/codeql-action/init@v2 27 | with: 28 | languages: ${{ matrix.language }} 29 | 30 | - name: Perform CodeQL Analysis 31 | uses: github/codeql-action/analyze@v2 32 | with: 33 | category: "/language:${{matrix.language}}" 34 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | To get started with the project make sure that you already installed node v16. If you use nvm then you can 3 | run: 4 | ```sh 5 | nvm use 6 | ``` 7 | 8 | Run `yarn` in the root directory to install the required dependencies for each package: 9 | 10 | ```sh 11 | yarn 12 | ``` 13 | 14 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. 15 | 16 | To start the packager: 17 | 18 | ```sh 19 | yarn example start 20 | ``` 21 | 22 | To run the example app on Android: 23 | 24 | ```sh 25 | yarn example android 26 | ``` 27 | 28 | To run the example app on iOS: 29 | 30 | ```sh 31 | yarn example ios 32 | ``` 33 | 34 | To run the example app on Web: 35 | 36 | ```sh 37 | yarn example web 38 | ``` 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@kanelloc/react-native-animated-header-scroll-view": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "importsNotUsedAsValues": "error", 11 | "forceConsistentCasingInFileNames": true, 12 | "jsx": "react", 13 | "lib": ["esnext"], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": true, 18 | "noImplicitUseStrict": false, 19 | "noStrictGenericChecks": false, 20 | "noUncheckedIndexedAccess": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "resolveJsonModule": true, 24 | "skipLibCheck": true, 25 | "strict": true, 26 | "target": "esnext" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const path = require('path'); 3 | const child_process = require('child_process'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const args = process.argv.slice(2); 7 | const options = { 8 | cwd: process.cwd(), 9 | env: process.env, 10 | stdio: 'inherit', 11 | encoding: 'utf-8', 12 | }; 13 | 14 | if (os.type() === 'Windows_NT') { 15 | options.shell = true; 16 | } 17 | 18 | let result; 19 | 20 | if (process.cwd() !== root || args.length) { 21 | // We're not in the root of the project, or additional arguments were passed 22 | // In this case, forward the command to `yarn` 23 | result = child_process.spawnSync('yarn', args, options); 24 | } else { 25 | // If `yarn` is run without arguments, perform bootstrap 26 | result = child_process.spawnSync('yarn', ['bootstrap'], options); 27 | } 28 | 29 | process.exitCode = result.status; 30 | -------------------------------------------------------------------------------- /example/src/screens/HeaderNavBarExample.tsx: -------------------------------------------------------------------------------- 1 | import { Card, HeaderNavBar, TopNavBar } from '../components'; 2 | import * as React from 'react'; 3 | import { useRef } from 'react'; 4 | import { AnimatedScrollView } from '@kanelloc/react-native-animated-header-scroll-view'; 5 | import { data, isIOS } from '../utils'; 6 | import type { ScrollView } from 'react-native'; 7 | 8 | export const HeaderNavBarExample = () => { 9 | const scrollRef = useRef(null); 10 | 11 | return ( 12 | } 17 | TopNavBarComponent={} 18 | headerImage={require('../../assets/cabin.jpg')} 19 | > 20 | {data.map((e) => { 21 | return ; 22 | })} 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /example/src/icons/Share.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import type { SvgProps } from 'react-native-svg'; 3 | import Svg, { Path } from 'react-native-svg'; 4 | 5 | export const Share = (props: SvgProps) => ( 6 | 7 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const createExpoWebpackConfigAsync = require('@expo/webpack-config'); 3 | const { resolver } = require('./metro.config'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const node_modules = path.join(__dirname, 'node_modules'); 7 | 8 | module.exports = async function (env, argv) { 9 | const config = await createExpoWebpackConfigAsync(env, argv); 10 | 11 | config.module.rules.push({ 12 | test: /\.(js|jsx|ts|tsx)$/, 13 | include: path.resolve(root, 'src'), 14 | use: 'babel-loader', 15 | }); 16 | 17 | // We need to make sure that only one version is loaded for peerDependencies 18 | // So we alias them to the versions in example's node_modules 19 | Object.assign(config.resolve.alias, { 20 | ...resolver.extraNodeModules, 21 | 'react-native-web': path.join(node_modules, 'react-native-web'), 22 | }); 23 | 24 | return config; 25 | }; 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .classpath 35 | .cxx 36 | .gradle 37 | .idea 38 | .project 39 | .settings 40 | local.properties 41 | android.iml 42 | 43 | # Cocoapods 44 | # 45 | example/ios/Pods 46 | 47 | # Ruby 48 | example/vendor/ 49 | 50 | # node.js 51 | # 52 | node_modules/ 53 | npm-debug.log 54 | yarn-debug.log 55 | yarn-error.log 56 | 57 | # BUCK 58 | buck-out/ 59 | \.buckd/ 60 | android/app/libs 61 | android/keystores/debug.keystore 62 | 63 | # Expo 64 | .expo/ 65 | 66 | # Turborepo 67 | .turbo/ 68 | 69 | # generated by bob 70 | lib/ 71 | -------------------------------------------------------------------------------- /example/src/screens/SafeAreaViewExample.tsx: -------------------------------------------------------------------------------- 1 | import { Card, TopNavBar, HeaderNavBar } from '../components'; 2 | import * as React from 'react'; 3 | import { SafeAreaView } from 'react-native-safe-area-context'; 4 | import { StyleSheet } from 'react-native'; 5 | import { AnimatedScrollView } from '@kanelloc/react-native-animated-header-scroll-view'; 6 | import { data } from '../utils'; 7 | 8 | export const SafeAreaViewExample = () => { 9 | return ( 10 | 11 | } 13 | TopNavBarComponent={} 14 | headerImage={require('../../assets/cabin.jpg')} 15 | > 16 | {data.map((e) => { 17 | return ; 18 | })} 19 | 20 | 21 | ); 22 | }; 23 | 24 | const styles = StyleSheet.create({ 25 | container: { 26 | flex: 1, 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "node_modules/expo/AppEntry.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web" 10 | }, 11 | "dependencies": { 12 | "@react-native-community/hooks": "^3.0.0", 13 | "@react-navigation/native": "^6.1.1", 14 | "@react-navigation/native-stack": "^6.9.7", 15 | "expo": "~47.0.9", 16 | "expo-status-bar": "~1.4.2", 17 | "react": "18.1.0", 18 | "react-dom": "18.1.0", 19 | "react-native": "0.70.5", 20 | "react-native-safe-area-context": "4.4.1", 21 | "react-native-screens": "~3.18.0", 22 | "react-native-svg": "13.4.0", 23 | "react-native-web": "~0.18.9" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.12.9", 27 | "@expo/webpack-config": "^0.17.2", 28 | "babel-loader": "^8.1.0", 29 | "babel-plugin-module-resolver": "^4.1.0" 30 | }, 31 | "private": true 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Babis Kanellopoulos 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. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please include a summary of the change and which issue is fixed 4 | 5 | 6 | 7 | ## Type of change 8 | 9 | 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | 16 | ## Checklist: 17 | 18 | - [ ] My code follows the style guidelines of this project 19 | - [ ] I have performed a self-review of my own code 20 | - [ ] I have commented my code, particularly in hard-to-understand areas 21 | - [ ] I have made corresponding changes to the documentation 22 | - [ ] My changes generate no new warnings 23 | - [ ] I have added tests that prove my fix is effective or that my feature works 24 | - [ ] New and existing unit tests pass locally with my changes 25 | - [ ] Any dependent changes have been merged and published in downstream modules 26 | - [ ] I have checked my code and corrected any misspellings 27 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const escape = require('escape-string-regexp'); 3 | const { getDefaultConfig } = require('@expo/metro-config'); 4 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 5 | const pak = require('../package.json'); 6 | 7 | const root = path.resolve(__dirname, '..'); 8 | 9 | const modules = Object.keys({ 10 | ...pak.peerDependencies, 11 | }); 12 | 13 | const defaultConfig = getDefaultConfig(__dirname); 14 | 15 | module.exports = { 16 | ...defaultConfig, 17 | 18 | projectRoot: __dirname, 19 | watchFolders: [root], 20 | 21 | // We need to make sure that only one version is loaded for peerDependencies 22 | // So we block them at the root, and alias them to the versions in example's node_modules 23 | resolver: { 24 | ...defaultConfig.resolver, 25 | 26 | blacklistRE: exclusionList( 27 | modules.map( 28 | (m) => 29 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 30 | ) 31 | ), 32 | 33 | extraNodeModules: modules.reduce((acc, name) => { 34 | acc[name] = path.join(__dirname, 'node_modules', name); 35 | return acc; 36 | }, {}), 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/hooks/useAnimateScrollView.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | import { Animated } from 'react-native'; 3 | 4 | export const useAnimateScrollView = ( 5 | imageHeight: number, 6 | disableScale?: boolean 7 | ) => { 8 | const scroll = useRef(new Animated.Value(0)).current; 9 | 10 | const scale = scroll.interpolate({ 11 | inputRange: [-imageHeight, 0, imageHeight], 12 | outputRange: [2.5, 1, 0.85], 13 | extrapolate: 'clamp', 14 | }); 15 | 16 | const translateYDown = scroll.interpolate({ 17 | inputRange: [-imageHeight, 0, imageHeight], 18 | outputRange: [-imageHeight * 0.6, 0, imageHeight * 0.5], 19 | extrapolate: 'clamp', 20 | }); 21 | 22 | const translateYUp = scroll.interpolate({ 23 | inputRange: [-imageHeight, 0, imageHeight], 24 | outputRange: [imageHeight * 0.3, 0, 0], 25 | extrapolate: 'clamp', 26 | }); 27 | 28 | const onScroll = Animated.event( 29 | [{ nativeEvent: { contentOffset: { y: scroll } } }], 30 | { useNativeDriver: true } 31 | ); 32 | 33 | return [ 34 | scroll, 35 | onScroll, 36 | disableScale ? 1 : scale, 37 | disableScale ? 0 : translateYDown, 38 | disableScale ? 0 : translateYUp, 39 | ] as const; 40 | }; 41 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | build_and_deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: git config 16 | if: ${{ (startsWith(github.event.head_commit.message, 'feat')) || (startsWith(github.event.head_commit.message, 'fix')) }} 17 | run: | 18 | git config user.name "kanelloc" 19 | git config user.email "kanelloc@users.noreply.github.com" 20 | 21 | - run: > 22 | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc 23 | 24 | - name: Setup 25 | uses: ./.github/actions/setup 26 | 27 | - name: Lint files 28 | run: yarn lint 29 | 30 | - name: Typecheck files 31 | run: yarn typecheck 32 | 33 | - name: Run unit tests 34 | run: yarn test --maxWorkers=2 --coverage 35 | 36 | - name: Build package 37 | run: yarn prepack 38 | 39 | - name: NPM release 40 | if: ${{ (startsWith(github.event.head_commit.message, 'feat')) || (startsWith(github.event.head_commit.message, 'fix')) }} 41 | run: yarn release-ci 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GIT_TOKEN }} 44 | 45 | -------------------------------------------------------------------------------- /example/src/components/HeaderNavBar.tsx: -------------------------------------------------------------------------------- 1 | import { RoundButton } from './RoundButton'; 2 | import { StatusBar, StyleSheet, View } from 'react-native'; 3 | import * as React from 'react'; 4 | import ArrowLeft from '../icons/ArrowLeft'; 5 | import { ArrowRight } from '../icons/ArrowRight'; 6 | import { Share } from '../icons/Share'; 7 | import { useNavigation } from '@react-navigation/native'; 8 | import { isIOS } from '../utils'; 9 | 10 | export const HeaderNavBar = () => { 11 | const nav = useNavigation(); 12 | return ( 13 | 14 | 19 | } onPress={nav.goBack} /> 20 | 21 | 22 | } onPress={() => null} /> 23 | 24 | } onPress={() => null} /> 25 | 26 | 27 | ); 28 | }; 29 | 30 | const styles = StyleSheet.create({ 31 | container: { 32 | width: '100%', 33 | paddingHorizontal: 8, 34 | paddingTop: 32, 35 | flexDirection: 'row', 36 | justifyContent: 'space-between', 37 | }, 38 | btnRightContainer: { 39 | flexDirection: 'row', 40 | alignItems: 'center', 41 | }, 42 | btnRight: { 43 | marginRight: 8, 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /example/src/components/TopNavBar.tsx: -------------------------------------------------------------------------------- 1 | import { RoundButton } from './RoundButton'; 2 | import { StyleSheet, Text, useWindowDimensions, View } from 'react-native'; 3 | import * as React from 'react'; 4 | import ArrowLeft from '../icons/ArrowLeft'; 5 | import { useNavigation } from '@react-navigation/native'; 6 | 7 | export const TopNavBar = () => { 8 | const { width } = useWindowDimensions(); 9 | const nav = useNavigation(); 10 | return ( 11 | 12 | 13 | } onPress={nav.goBack} /> 14 | 15 | 23 | Header 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | const styles = StyleSheet.create({ 31 | container: { 32 | width: '100%', 33 | height: '100%', 34 | alignItems: 'center', 35 | flexDirection: 'row', 36 | justifyContent: 'space-between', 37 | paddingTop: 32, 38 | borderBottomColor: '#a4a4a4', 39 | borderBottomWidth: StyleSheet.hairlineWidth, 40 | backgroundColor: 'tomato', 41 | }, 42 | leftContainer: { 43 | paddingLeft: 8, 44 | }, 45 | titleContainer: { 46 | alignItems: 'center', 47 | justifyContent: 'center', 48 | }, 49 | title: { 50 | fontWeight: 'bold', 51 | }, 52 | }); 53 | -------------------------------------------------------------------------------- /example/src/screens/HeaderNavbarFlatListExample.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useRef } from 'react'; 3 | import { FlatList, RefreshControl, StyleSheet, View } from 'react-native'; 4 | import { AnimatedFlatList } from '@kanelloc/react-native-animated-header-scroll-view'; 5 | import { data } from '../utils'; 6 | import { Card, HeaderNavBar, TopNavBar } from '../components'; 7 | import { useRefresh } from '@react-native-community/hooks'; 8 | 9 | const fetch = () => { 10 | return new Promise((resolve) => setTimeout(resolve, 5000)); 11 | }; 12 | export const HeaderNavbarFlatListExample = () => { 13 | const { isRefreshing, onRefresh } = useRefresh(fetch); 14 | const scrollRef = useRef(null); 15 | const renderItem = ({ item }: any) => { 16 | return ( 17 | 18 | 19 | 20 | ); 21 | }; 22 | 23 | return ( 24 | 25 | 35 | } 36 | headerImage={require('../../assets/cabin.jpg')} 37 | data={data} 38 | renderItem={renderItem} 39 | HeaderNavbarComponent={} 40 | TopNavBarComponent={} 41 | /> 42 | 43 | ); 44 | }; 45 | 46 | const styles = StyleSheet.create({ 47 | refresh: { 48 | zIndex: 10, 49 | }, 50 | }); 51 | -------------------------------------------------------------------------------- /example/src/screens/RefreshControlExample.tsx: -------------------------------------------------------------------------------- 1 | import { Card, TopNavBar, HeaderNavBar } from '../components'; 2 | import * as React from 'react'; 3 | import { AnimatedScrollView } from '@kanelloc/react-native-animated-header-scroll-view'; 4 | import { data, isIOS } from '../utils'; 5 | import { RefreshControl, StyleSheet } from 'react-native'; 6 | import { SafeAreaView } from 'react-native-safe-area-context'; 7 | import { useRefresh } from '@react-native-community/hooks'; 8 | 9 | const fetch = () => { 10 | return new Promise((resolve) => setTimeout(resolve, 5000)); 11 | }; 12 | 13 | export const RefreshControlExample = () => { 14 | const { isRefreshing, onRefresh } = useRefresh(fetch); 15 | 16 | return ( 17 | 18 | 27 | } 28 | headerMaxHeight={400} 29 | topBarHeight={isIOS ? 90 : 70} 30 | HeaderNavbarComponent={} 31 | TopNavBarComponent={} 32 | headerImage={require('../../assets/cabin.jpg')} 33 | > 34 | {data.map((e) => { 35 | return ; 36 | })} 37 | 38 | 39 | ); 40 | }; 41 | 42 | const styles = StyleSheet.create({ 43 | container: { 44 | flex: 1, 45 | }, 46 | refresh: { 47 | zIndex: 10, 48 | }, 49 | }); 50 | -------------------------------------------------------------------------------- /example/src/screens/ExamplesDirectory.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; 2 | import React from 'react'; 3 | import type { NativeStackScreenProps } from '@react-navigation/native-stack'; 4 | import type { RootStackParamList } from '../types'; 5 | import { SCREENS } from '../utils/enums'; 6 | 7 | type Props = NativeStackScreenProps; 8 | export const ExamplesDirectory = ({ navigation }: Props) => { 9 | const goTo = (screen: SCREENS) => { 10 | navigation.navigate(screen); 11 | }; 12 | 13 | const examples = [ 14 | { screen: SCREENS.HEADER_NAVBAR_EXAMPLE }, 15 | { screen: SCREENS.SAFE_AREA_VIEW_EXAMPLE }, 16 | { screen: SCREENS.IMAGE_FOREGROUND_EXAMPLE }, 17 | { screen: SCREENS.ONLY_FOREGROUND_EXAMPLE }, 18 | { screen: SCREENS.REFRESH_CONTROL_EXAMPLE }, 19 | { screen: SCREENS.HEADER_NAVBAR_FLATLIST_EXAMPLE }, 20 | ]; 21 | 22 | return ( 23 | 24 | {examples.map((e) => { 25 | return ( 26 | goTo(e.screen)} 29 | style={styles.button} 30 | > 31 | {e.screen} 32 | 33 | ); 34 | })} 35 | 36 | ); 37 | }; 38 | 39 | const styles = StyleSheet.create({ 40 | container: { 41 | flex: 1, 42 | alignItems: 'center', 43 | justifyContent: 'center', 44 | }, 45 | button: { 46 | marginTop: 8, 47 | padding: 16, 48 | borderWidth: 1, 49 | borderRadius: 24, 50 | width: 240, 51 | alignItems: 'center', 52 | justifyContent: 'center', 53 | }, 54 | }); 55 | -------------------------------------------------------------------------------- /src/components/AnimatedNavbar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, Animated } from 'react-native'; 3 | import { useAnimateNavbar } from '../hooks/useAnimateNavbar'; 4 | import type { AnimatedNavbarProps } from '../types'; 5 | 6 | const AnimatedNavbar = ({ 7 | scroll, 8 | imageHeight, 9 | OverflowHeaderComponent, 10 | TopNavbarComponent, 11 | headerHeight, 12 | headerElevation, 13 | }: AnimatedNavbarProps) => { 14 | const [headerOpacity, overflowHeaderOpacity] = useAnimateNavbar( 15 | scroll, 16 | imageHeight, 17 | headerHeight 18 | ); 19 | 20 | return ( 21 | <> 22 | 33 | {TopNavbarComponent} 34 | 35 | 46 | {OverflowHeaderComponent} 47 | 48 | 49 | ); 50 | }; 51 | 52 | const styles = StyleSheet.create({ 53 | container: { 54 | position: 'absolute', 55 | top: 0, 56 | width: '100%', 57 | backgroundColor: 'white', 58 | alignItems: 'center', 59 | justifyContent: 'center', 60 | }, 61 | overflowHeader: { 62 | backgroundColor: 'transparent', 63 | }, 64 | }); 65 | 66 | export default AnimatedNavbar; 67 | -------------------------------------------------------------------------------- /src/components/AnimatedFlatList.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react'; 2 | import { Animated, FlatList } from 'react-native'; 3 | import type { AnimatedFlatListViewProps } from '../types'; 4 | import AnimatedNavbar from './AnimatedNavbar'; 5 | import { HEADER_HEIGHT, IMG_HEADER_HEIGHT } from '../constants'; 6 | import { useAnimateScrollView } from '../hooks/useAnimateScrollView'; 7 | import { AnimatedHeader } from './AnimatedHeader'; 8 | 9 | export const AnimatedFlatList = forwardRef( 10 | ( 11 | { 12 | headerMaxHeight, 13 | topBarHeight, 14 | topBarElevation, 15 | disableScale, 16 | TopNavBarComponent, 17 | HeaderNavbarComponent, 18 | headerImage, 19 | imageStyle, 20 | HeaderComponent, 21 | ...props 22 | }: AnimatedFlatListViewProps, 23 | ref 24 | ) => { 25 | const imageHeight = headerMaxHeight || IMG_HEADER_HEIGHT; 26 | const headerNavHeight = topBarHeight || HEADER_HEIGHT; 27 | const headerElevation = topBarElevation || 0; 28 | const [scroll, onScroll, scale, translateYDown, translateYUp] = 29 | useAnimateScrollView(imageHeight, disableScale); 30 | 31 | return ( 32 | <> 33 | 47 | } 48 | /> 49 | 57 | 58 | ); 59 | } 60 | ); 61 | -------------------------------------------------------------------------------- /src/components/AnimatedScrollView.tsx: -------------------------------------------------------------------------------- 1 | import { Animated, ScrollView } from 'react-native'; 2 | import React, { forwardRef } from 'react'; 3 | import { HEADER_HEIGHT, IMG_HEADER_HEIGHT } from '../constants'; 4 | import AnimatedNavbar from './AnimatedNavbar'; 5 | import type { AnimatedScrollViewProps } from '../types'; 6 | import { useAnimateScrollView } from '../hooks/useAnimateScrollView'; 7 | import { AnimatedHeader } from './AnimatedHeader'; 8 | 9 | export const AnimatedScrollView = forwardRef< 10 | ScrollView, 11 | AnimatedScrollViewProps 12 | >( 13 | ( 14 | { 15 | TopNavBarComponent, 16 | HeaderNavbarComponent, 17 | HeaderComponent, 18 | headerMaxHeight, 19 | topBarHeight, 20 | topBarElevation, 21 | headerImage, 22 | disableScale, 23 | children, 24 | imageStyle, 25 | ...props 26 | }: AnimatedScrollViewProps, 27 | ref 28 | ) => { 29 | const imageHeight = headerMaxHeight || IMG_HEADER_HEIGHT; 30 | const headerNavHeight = topBarHeight || HEADER_HEIGHT; 31 | const headerElevation = topBarElevation || 0; 32 | const [scroll, onScroll, scale, translateYDown, translateYUp] = 33 | useAnimateScrollView(imageHeight, disableScale); 34 | 35 | return ( 36 | <> 37 | 43 | 52 | {children} 53 | 54 | 62 | 63 | ); 64 | } 65 | ); 66 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { NavigationContainer } from '@react-navigation/native'; 3 | import { createNativeStackNavigator } from '@react-navigation/native-stack'; 4 | import { HeaderNavBarExample } from './screens/HeaderNavBarExample'; 5 | import { ExamplesDirectory } from './screens/ExamplesDirectory'; 6 | import { SafeAreaViewExample } from './screens/SafeAreaViewExample'; 7 | import { SafeAreaProvider } from 'react-native-safe-area-context'; 8 | import type { RootStackParamList } from './types'; 9 | import { ImageForegroundExample } from './screens/ImageForegroundExample'; 10 | import { OnlyForegroundExample } from './screens/OnlyForegroundExample'; 11 | import { RefreshControlExample } from './screens/RefreshControlExample'; 12 | import { HeaderNavbarFlatListExample } from './screens/HeaderNavbarFlatListExample'; 13 | 14 | const App = () => { 15 | const Stack = createNativeStackNavigator(); 16 | 17 | return ( 18 | 19 | 20 | 21 | 25 | 29 | 33 | 37 | 41 | 45 | 49 | 50 | 51 | 52 | ); 53 | }; 54 | 55 | export default App; 56 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | FlatListProps, 3 | ImageStyle, 4 | ScrollViewProps, 5 | StyleProp, 6 | } from 'react-native'; 7 | import { Animated, ImageSourcePropType } from 'react-native'; 8 | 9 | type AnimatedViewProps = { 10 | /** 11 | * Rendered on top of the screen as a navbar when scrolling to the top 12 | */ 13 | TopNavBarComponent: JSX.Element; 14 | 15 | /** 16 | * A component to use on top of header image. It can also be used 17 | * without header image to render a custom component as header. 18 | */ 19 | HeaderComponent?: JSX.Element; 20 | 21 | /** 22 | * Rendered on top of the header. Transitions to TopNavbarComponent as you scroll 23 | */ 24 | HeaderNavbarComponent?: JSX.Element; 25 | 26 | /** 27 | * Height of the header (headerImage or HeaderComponent). Default value is 300 28 | */ 29 | headerMaxHeight?: number; 30 | 31 | /** 32 | * Height of the top navbar. Default value is 90 33 | */ 34 | topBarHeight?: number; 35 | 36 | /** 37 | * [ANDROID ONLY] Elevation of the top navbar. Default value is 0 38 | */ 39 | topBarElevation?: number; 40 | 41 | /** 42 | * @see https://reactnative.dev/docs/image#source 43 | */ 44 | headerImage?: ImageSourcePropType; 45 | 46 | /** 47 | * Disables header scaling when scrolling 48 | */ 49 | disableScale?: boolean; 50 | 51 | /** 52 | * Image styles 53 | */ 54 | imageStyle?: StyleProp; 55 | }; 56 | 57 | export type AnimatedScrollViewProps = AnimatedViewProps & ScrollViewProps; 58 | 59 | export type AnimatedFlatListViewProps = AnimatedViewProps & FlatListProps; 60 | 61 | export type AnimatedNavbarProps = { 62 | scroll: Animated.Value; 63 | OverflowHeaderComponent?: JSX.Element; 64 | TopNavbarComponent?: JSX.Element; 65 | imageHeight: number; 66 | headerHeight: number; 67 | headerElevation: number; 68 | }; 69 | 70 | export type AnimatedHeaderProps = { 71 | imageHeight: number; 72 | translateYUp: Animated.AnimatedInterpolation | 0; 73 | translateYDown: Animated.AnimatedInterpolation | 0; 74 | scale: Animated.AnimatedInterpolation | 1; 75 | imageStyle?: StyleProp; 76 | HeaderComponent?: JSX.Element; 77 | headerImage?: ImageSourcePropType; 78 | }; 79 | -------------------------------------------------------------------------------- /src/components/AnimatedHeader.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Animated, 3 | ImageBackground, 4 | StyleSheet, 5 | useWindowDimensions, 6 | View, 7 | } from 'react-native'; 8 | import React from 'react'; 9 | import type { AnimatedHeaderProps } from '../types'; 10 | 11 | export const AnimatedHeader = ({ 12 | HeaderComponent, 13 | headerImage, 14 | imageHeight, 15 | translateYUp, 16 | translateYDown, 17 | scale, 18 | imageStyle, 19 | }: AnimatedHeaderProps) => { 20 | const { width } = useWindowDimensions(); 21 | const AnimatedImageBackground = 22 | Animated.createAnimatedComponent(ImageBackground); 23 | return ( 24 | 33 | {HeaderComponent ? ( 34 | <> 35 | {headerImage ? ( 36 | 50 | {HeaderComponent} 51 | 52 | ) : ( 53 | 65 | {HeaderComponent} 66 | 67 | )} 68 | 69 | ) : ( 70 | 84 | )} 85 | 86 | ); 87 | }; 88 | 89 | const styles = StyleSheet.create({ 90 | imgContainer: { 91 | alignItems: 'center', 92 | overflow: 'hidden', 93 | }, 94 | }); 95 | -------------------------------------------------------------------------------- /src/__tests__/useAnimateNavbar.test.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks'; 2 | import { useAnimateNavbar } from '../hooks/useAnimateNavbar'; 3 | import { HEADER_HEIGHT, IMG_HEADER_HEIGHT } from '../constants'; 4 | import { Animated } from 'react-native'; 5 | import { useRef } from 'react'; 6 | 7 | describe('useAnimatedNavbar basic tests', () => { 8 | it('should return starting points', () => { 9 | const { result } = renderHook(() => { 10 | const first = (IMG_HEADER_HEIGHT - HEADER_HEIGHT) * 0.75; // 157,5 11 | const scroll = useRef(new Animated.Value(first)).current; 12 | return useAnimateNavbar(scroll, IMG_HEADER_HEIGHT, HEADER_HEIGHT); 13 | }); 14 | 15 | const [headerOpacity, overflowHeaderOpacity] = result.current; 16 | 17 | expect(JSON.stringify(headerOpacity)).toBe('0'); 18 | expect(JSON.stringify(overflowHeaderOpacity)).toBe('1'); 19 | }); 20 | 21 | it('should return ending points', () => { 22 | const { result } = renderHook(() => { 23 | const first = IMG_HEADER_HEIGHT - HEADER_HEIGHT; // 210 24 | const scroll = useRef(new Animated.Value(first)).current; 25 | return useAnimateNavbar(scroll, IMG_HEADER_HEIGHT, HEADER_HEIGHT); 26 | }); 27 | 28 | const [headerOpacity, overflowHeaderOpacity] = result.current; 29 | 30 | expect(JSON.stringify(headerOpacity)).toBe('1'); 31 | expect(JSON.stringify(overflowHeaderOpacity)).toBe('0'); 32 | }); 33 | 34 | it('should return 50% of interpolation', () => { 35 | const { result } = renderHook(() => { 36 | const start = (IMG_HEADER_HEIGHT - HEADER_HEIGHT) * 0.75; // 157,5 37 | const end = IMG_HEADER_HEIGHT - HEADER_HEIGHT; // 210 38 | const diff = (end - start) / 2; 39 | 40 | const scroll = useRef(new Animated.Value(end - diff)).current; 41 | return useAnimateNavbar(scroll, IMG_HEADER_HEIGHT, HEADER_HEIGHT); 42 | }); 43 | 44 | const [headerOpacity, overflowHeaderOpacity] = result.current; 45 | 46 | expect(JSON.stringify(headerOpacity)).toBe('0.5'); 47 | expect(JSON.stringify(overflowHeaderOpacity)).toBe('0.5'); 48 | }); 49 | 50 | it('should return 80% of interpolation', () => { 51 | const { result } = renderHook(() => { 52 | const start = (IMG_HEADER_HEIGHT - HEADER_HEIGHT) * 0.75; // 157,5 53 | const end = IMG_HEADER_HEIGHT - HEADER_HEIGHT; // 210 54 | const diff = (end - start) * 0.8; 55 | 56 | const scroll = useRef(new Animated.Value(end - diff)).current; 57 | return useAnimateNavbar(scroll, IMG_HEADER_HEIGHT, HEADER_HEIGHT); 58 | }); 59 | 60 | const [headerOpacity, overflowHeaderOpacity] = result.current; 61 | 62 | expect(JSON.stringify(headerOpacity)).toBe('0.2'); 63 | expect(JSON.stringify(overflowHeaderOpacity)).toBe('0.8'); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | # [1.0.0](https://github.com/kanelloc/react-native-animated-header-scroll-view/compare/v0.12.0...v1.0.0) (2023-09-02) 4 | 5 | 6 | * feat!: make navbar opts more robust (#62) ([cbe08ec](https://github.com/kanelloc/react-native-animated-header-scroll-view/commit/cbe08ec924ece38c4f830081b11f09d12fac3d6d)), closes [#62](https://github.com/kanelloc/react-native-animated-header-scroll-view/issues/62) 7 | 8 | 9 | ### BREAKING CHANGES 10 | 11 | * Default paddingTop, borderBottomWidth & borderBottomColor have moved from the wrapper header component to the component provided from the user/dev 12 | 13 | # [0.12.0](https://github.com/kanelloc/react-native-animated-header-scroll-view/compare/v0.11.1...v0.12.0) (2023-03-04) 14 | 15 | 16 | ### Features 17 | 18 | * Enable ref forwarding ([#47](https://github.com/kanelloc/react-native-animated-header-scroll-view/issues/47)) ([581c87f](https://github.com/kanelloc/react-native-animated-header-scroll-view/commit/581c87f62711ffbc90e177d8c4404eedbeb26fd7)) 19 | 20 | ## [0.11.1](https://github.com/kanelloc/react-native-animated-header-scroll-view/compare/v0.11.0...v0.11.1) (2023-02-05) 21 | 22 | 23 | ### Bug Fixes 24 | 25 | * bypass translations when disableScale is enabled ([#35](https://github.com/kanelloc/react-native-animated-header-scroll-view/issues/35)) ([53066d6](https://github.com/kanelloc/react-native-animated-header-scroll-view/commit/53066d6a8961c6c8b99eca95f3015bc00e671626)) 26 | 27 | # [0.11.0](https://github.com/kanelloc/react-native-animated-header-scroll-view/compare/v0.10.4...v0.11.0) (2023-02-05) 28 | 29 | 30 | ### Features 31 | 32 | * New AnimatedFlatList component ([#34](https://github.com/kanelloc/react-native-animated-header-scroll-view/issues/34)) ([92a02bd](https://github.com/kanelloc/react-native-animated-header-scroll-view/commit/92a02bd045e50221e1095d55d358a307e1905d26)) 33 | 34 | ### Chore 35 | * Bump deps ([#31](https://github.com/kanelloc/react-native-animated-header-scroll-view/pull/31)) 36 | * Trigger release only for feats and fixes ([#32](https://github.com/kanelloc/react-native-animated-header-scroll-view/pull/32)) 37 | 38 | ### Docs 39 | * Update contribution readme ([#33](https://github.com/kanelloc/react-native-animated-header-scroll-view/pull/33)) 40 | 41 | # [0.10.0](https://github.com/kanelloc/react-native-animated-header-scroll-view/compare/v0.9.4...v0.10.0) (2023-02-02) 42 | 43 | 44 | ### Features 45 | 46 | * add image style prop ([#25](https://github.com/kanelloc/react-native-animated-header-scroll-view/issues/25)) ([01682b7](https://github.com/kanelloc/react-native-animated-header-scroll-view/commit/01682b7eca81e350c2a7cd5e586e9a44b06e87b6)) 47 | 48 | # [0.9.3](https://github.com/kanelloc/react-native-animated-header-scroll-view/compare/v0.9.2...v0.9.3) (2023-01-29) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * override status bar ([#19](https://github.com/kanelloc/react-native-animated-header-scroll-view/issues/19)) ([3570cab](https://github.com/kanelloc/react-native-animated-header-scroll-view/commit/3570cab1ccf3330ed30a8d326fa003f25f33b146)) 54 | 55 | # [0.9.0](https://github.com/kanelloc/react-native-animated-header-scroll-view/compare/v0.8.7...v0.9.0) (2023-01-07) 56 | 57 | 58 | ### Features 59 | 60 | * add new refresh control example ([#6](https://github.com/kanelloc/react-native-animated-header-scroll-view/issues/6)) ([b700e0c](https://github.com/kanelloc/react-native-animated-header-scroll-view/commit/b700e0c068fb18ab128a93dc0387287e3d88e17f)) -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are always welcome, no matter how large or small! 4 | 5 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md). 6 | 7 | ## Development workflow 8 | Project includes a `.nvmrc` file. If you use nvm just run 9 | ```shell 10 | nvm use 11 | ``` 12 | 13 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 14 | 15 | ```sh 16 | yarn 17 | ``` 18 | 19 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. 20 | 21 | While developing, you can run the [example app](/example/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. 22 | 23 | To start the packager: 24 | 25 | ```sh 26 | yarn example start 27 | ``` 28 | 29 | To run the example app on Android: 30 | 31 | ```sh 32 | yarn example android 33 | ``` 34 | 35 | To run the example app on iOS: 36 | 37 | ```sh 38 | yarn example ios 39 | ``` 40 | 41 | To run the example app on Web: 42 | 43 | ```sh 44 | yarn example web 45 | ``` 46 | 47 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 48 | 49 | ```sh 50 | yarn typecheck 51 | yarn lint 52 | ``` 53 | 54 | To fix formatting errors, run the following: 55 | 56 | ```sh 57 | yarn lint --fix 58 | ``` 59 | 60 | Remember to add tests for your change if possible. Run the unit tests by: 61 | 62 | ```sh 63 | yarn test 64 | ``` 65 | 66 | 67 | ### Commit message convention 68 | 69 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 70 | 71 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 72 | - `feat`: new features, e.g. add new method to the module. 73 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 74 | - `docs`: changes into documentation, e.g. add usage example for the module.. 75 | - `test`: adding or updating tests, e.g. add integration tests using detox. 76 | - `chore`: tooling changes, e.g. change CI config. 77 | 78 | Our pre-commit hooks verify that your commit message matches this format when committing. 79 | 80 | ### Linting and tests 81 | 82 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 83 | 84 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 85 | 86 | Our pre-commit hooks verify that the linter and tests pass when committing. 87 | 88 | ### Scripts 89 | 90 | The `package.json` file contains various scripts for common tasks: 91 | 92 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 93 | - `yarn typecheck`: type-check files with TypeScript. 94 | - `yarn lint`: lint files with ESLint. 95 | - `yarn test`: run unit tests with Jest. 96 | - `yarn example start`: start the Metro server for the example app. 97 | - `yarn example android`: run the example app on Android. 98 | - `yarn example ios`: run the example app on iOS. 99 | 100 | ### Sending a pull request 101 | 102 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 103 | 104 | When you're sending a pull request: 105 | 106 | - Prefer small pull requests focused on one change. 107 | - Verify that linters and tests are passing. 108 | - Review the documentation to make sure it looks good. 109 | - Follow the pull request template when opening a pull request. 110 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 111 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kanelloc/react-native-animated-header-scroll-view", 3 | "version": "1.0.0", 4 | "description": "React native animated header scroll view component", 5 | "main": "lib/commonjs/index", 6 | "module": "lib/module/index", 7 | "types": "lib/typescript/index.d.ts", 8 | "react-native": "src/index", 9 | "source": "src/index", 10 | "files": [ 11 | "src", 12 | "lib", 13 | "android", 14 | "ios", 15 | "cpp", 16 | "*.podspec", 17 | "!lib/typescript/example", 18 | "!ios/build", 19 | "!android/build", 20 | "!android/gradle", 21 | "!android/gradlew", 22 | "!android/gradlew.bat", 23 | "!android/local.properties", 24 | "!**/__tests__", 25 | "!**/__fixtures__", 26 | "!**/__mocks__", 27 | "!**/.*" 28 | ], 29 | "scripts": { 30 | "test": "jest", 31 | "typecheck": "tsc --noEmit", 32 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 33 | "prepack": "bob build", 34 | "release": "release-it", 35 | "release-ci": "release-it --ci", 36 | "example": "yarn --cwd example", 37 | "bootstrap": "yarn example && yarn install" 38 | }, 39 | "keywords": [ 40 | "react-native", 41 | "ios", 42 | "android" 43 | ], 44 | "repository": "https://github.com/kanelloc/react-native-animated-header-scroll-view", 45 | "author": "Babis Kanellopoulos (https://github.com/kanelloc)", 46 | "license": "MIT", 47 | "bugs": { 48 | "url": "https://github.com/kanelloc/react-native-animated-header-scroll-view/issues" 49 | }, 50 | "homepage": "https://github.com/kanelloc/react-native-animated-header-scroll-view#readme", 51 | "publishConfig": { 52 | "access": "public", 53 | "registry": "https://registry.npmjs.org/" 54 | }, 55 | "devDependencies": { 56 | "@commitlint/config-conventional": "^17.0.2", 57 | "@evilmartians/lefthook": "^1.2.2", 58 | "@react-native-community/eslint-config": "^3.0.2", 59 | "@release-it/conventional-changelog": "^5.0.0", 60 | "@testing-library/react-hooks": "^8.0.1", 61 | "@types/jest": "^29.4.0", 62 | "@types/react": "~17.0.21", 63 | "@types/react-native": "0.70.0", 64 | "commitlint": "^17.0.2", 65 | "del-cli": "^5.0.0", 66 | "eslint": "^8.4.1", 67 | "eslint-config-prettier": "^9.0.0", 68 | "eslint-plugin-prettier": "^5.0.0", 69 | "eslint-plugin-react-native": "^4.0.0", 70 | "jest": "^29.4.1", 71 | "pod-install": "^0.1.0", 72 | "prettier": "^3.0.3", 73 | "react": "18.1.0", 74 | "react-native": "0.70.5", 75 | "react-native-builder-bob": "^0.20.0", 76 | "react-test-renderer": "^18.2.0", 77 | "release-it": "^15.0.0", 78 | "typescript": "^4.5.2" 79 | }, 80 | "resolutions": { 81 | "@types/react": "17.0.21" 82 | }, 83 | "peerDependencies": { 84 | "react": "*", 85 | "react-native": "*" 86 | }, 87 | "engines": { 88 | "node": ">= 16.0.0" 89 | }, 90 | "jest": { 91 | "preset": "react-native", 92 | "modulePathIgnorePatterns": [ 93 | "/example/node_modules", 94 | "/lib/" 95 | ] 96 | }, 97 | "commitlint": { 98 | "extends": [ 99 | "@commitlint/config-conventional" 100 | ] 101 | }, 102 | "release-it": { 103 | "git": { 104 | "commitMessage": "chore: release ${version}", 105 | "tagName": "v${version}" 106 | }, 107 | "npm": { 108 | "publish": true 109 | }, 110 | "github": { 111 | "release": true 112 | }, 113 | "plugins": { 114 | "@release-it/conventional-changelog": { 115 | "infile": "CHANGELOG.md", 116 | "header": "# Changelog", 117 | "preset": { 118 | "name": "angular", 119 | "types": [ 120 | { 121 | "type": "feat", 122 | "section": "Features" 123 | }, 124 | { 125 | "type": "fix", 126 | "section": "Bug Fixes" 127 | } 128 | ] 129 | } 130 | } 131 | } 132 | }, 133 | "eslintConfig": { 134 | "root": true, 135 | "extends": [ 136 | "@react-native-community", 137 | "prettier" 138 | ], 139 | "plugins": [ 140 | "react-native" 141 | ], 142 | "rules": { 143 | "react-native/no-unused-styles": 2, 144 | "react-native/no-inline-styles": 2, 145 | "prettier/prettier": [ 146 | "error", 147 | { 148 | "quoteProps": "consistent", 149 | "singleQuote": true, 150 | "tabWidth": 2, 151 | "trailingComma": "es5", 152 | "useTabs": false 153 | } 154 | ] 155 | } 156 | }, 157 | "eslintIgnore": [ 158 | "node_modules/", 159 | "lib/" 160 | ], 161 | "prettier": { 162 | "quoteProps": "consistent", 163 | "singleQuote": true, 164 | "tabWidth": 2, 165 | "trailingComma": "es5", 166 | "useTabs": false 167 | }, 168 | "react-native-builder-bob": { 169 | "source": "src", 170 | "output": "lib", 171 | "targets": [ 172 | "commonjs", 173 | "module", 174 | [ 175 | "typescript", 176 | { 177 | "project": "tsconfig.build.json" 178 | } 179 | ] 180 | ] 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Animated Header ScrollView 2 | 3 | [![NPM version][npm-image]][npm-url] [![npm][npm-downloads]][npm-url] [![npm][license-url]][npm-url] [![npm][types-url]][npm-url] [![runs with expo][expo-image]][expo-url] 4 | 5 | Performant animated scroll view components that: 6 | * 🔥Support `FlatList` and `ScrollView` scrolling interactions. 7 | * 🔥Animate an image or a custom component into a navbar header 8 | * 🔥Support bounce animation on scroll down 9 | * 🔥Support both iOS and Android devices 10 | 11 | ![React Native Animated Header ScrollView](./preview-ios.gif) 12 | 13 | 14 | # Installation 15 | ```sh 16 | $ npm install @kanelloc/react-native-animated-header-scroll-view 17 | ``` 18 | 19 | # Usage 20 | ```typescript 21 | import { Card, TopNavBar, HeaderNavBar } from '../components'; 22 | import { AnimatedScrollView } from '@kanelloc/react-native-animated-header-scroll-view'; 23 | import * as React from 'react'; 24 | 25 | export const App = () => { 26 | const data = Array.from(Array(20).keys()); 27 | return ( 28 | } 30 | TopNavBarComponent={} 31 | headerImage={require('../../assets/cabin.jpg')} 32 | > 33 | {data.map((e) => { 34 | return ; 35 | })} 36 | 37 | ); 38 | }; 39 | ``` 40 | 41 | ```typescript 42 | import { Card, TopNavBar, HeaderNavBar } from '../components'; 43 | import { AnimatedScrollView } from '@kanelloc/react-native-animated-header-scroll-view'; 44 | import * as React from 'react'; 45 | 46 | export const App = () => { 47 | const data = Array.from(Array(20).keys()); 48 | const renderItem = ({ item }: any) => { 49 | return ( 50 | 51 | 52 | 53 | ); 54 | }; 55 | 56 | return ( 57 | } 62 | TopNavBarComponent={} 63 | /> 64 | ); 65 | }; 66 | ``` 67 | 68 | You can find a set of detailed examples [here](https://github.com/kanelloc/react-native-animated-header-scroll-view/tree/main/example) 69 | 70 | Also a running snack [here](https://snack.expo.dev/ukGomwbdE) 71 | 72 | # Props 73 | 74 | | Prop name | Description | Type | Required | 75 | |-------------------------|-----------------------------------------------------------------------------------------------------------------------------|-----------------------|----------| 76 | | `TopNavBarComponent` | Rendered on top of the screen as a navbar when scrolling to the top | JSX.Element | true | 77 | | `HeaderComponent` | A component to use on top of header image. It can also be used without header image to render a custom component as header. | JSX.Element | false | 78 | | `HeaderNavbarComponent` | Rendered on top of the header. Transitions to TopNavbarComponent as you scroll | JSX.Element | false | 79 | | `headerMaxHeight` | Height of the header (headerImage or HeaderComponent). Default value is 300 | number | false | 80 | | `topBarHeight` | Height of the top navbar. Default value is 90 | number | false | 81 | | `topBarElevation` | [ANDROID ONLY] Elevation of the top navbar. Default value is 0 | number | false | 82 | | `headerImage` | Image header source | ImageSourcePropType | false | 83 | | `disableScale` | Disables header scaling when scrolling. Default value is false | boolean | false | 84 | | `imageStyle` | Image styles | StyleProp | false | 85 | 86 | 87 | 88 | # Contributing 89 | 90 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. 91 | 92 | # License 93 | 94 | MIT 95 | 96 | [npm-url]: https://www.npmjs.com/package/@kanelloc/react-native-animated-header-scroll-view 97 | [npm-image]: https://img.shields.io/npm/v/@kanelloc/react-native-animated-header-scroll-view?style=flat-square 98 | [license-url]: https://img.shields.io/npm/l/@kanelloc/react-native-animated-header-scroll-view?style=flat-square 99 | [types-url]: https://img.shields.io/badge/types-included-blue?style=flat-square 100 | [expo-image]: https://img.shields.io/badge/Runs%20with%20Expo-4630EB.svg?style=flat-square&logo=EXPO&labelColor=f3f3f3&logoColor=000 101 | [expo-url]: https://expo.io 102 | [npm-downloads]: https://img.shields.io/npm/dm/@kanelloc/react-native-animated-header-scroll-view?style=flat-square 103 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | --------------------------------------------------------------------------------