├── .buckconfig ├── .eslintrc.js ├── .flowconfig ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .prettierrc.js ├── .watchmanconfig ├── Example ├── .gitignore ├── __tests__ │ └── App-test.js ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── tabbartest │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── tabbartest │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── node_modules_reactnavigation_stack_src_views_assets_backicon.png │ │ │ ├── drawable-mdpi │ │ │ ├── node_modules_reactnavigation_stack_src_views_assets_backicon.png │ │ │ ├── node_modules_reactnavigation_stack_src_views_assets_backiconmask.png │ │ │ ├── src_resource_header_img.png │ │ │ ├── src_resource_img1.jpeg │ │ │ ├── src_resource_img10.jpeg │ │ │ ├── src_resource_img11.jpeg │ │ │ ├── src_resource_img12.jpeg │ │ │ ├── src_resource_img13.jpeg │ │ │ ├── src_resource_img14.jpeg │ │ │ ├── src_resource_img15.jpeg │ │ │ ├── src_resource_img2.jpeg │ │ │ ├── src_resource_img3.jpeg │ │ │ ├── src_resource_img4.jpeg │ │ │ ├── src_resource_img5.jpeg │ │ │ ├── src_resource_img6.jpeg │ │ │ ├── src_resource_img7.jpeg │ │ │ ├── src_resource_img8.jpeg │ │ │ ├── src_resource_img9.jpg │ │ │ ├── src_resource_tab1.png │ │ │ ├── src_resource_tab2.png │ │ │ └── src_resource_tab3.png │ │ │ ├── drawable-xhdpi │ │ │ └── node_modules_reactnavigation_stack_src_views_assets_backicon.png │ │ │ ├── drawable-xxhdpi │ │ │ └── node_modules_reactnavigation_stack_src_views_assets_backicon.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── node_modules_reactnavigation_stack_src_views_assets_backicon.png │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── raw │ │ │ └── app.json │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── assetsTransformer.js ├── babel.config.js ├── index.js ├── ios │ ├── Podfile │ ├── TabBarTest-tvOS │ │ └── Info.plist │ ├── TabBarTest-tvOSTests │ │ └── Info.plist │ ├── TabBarTest.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── TabBarTest-tvOS.xcscheme │ │ │ └── TabBarTest.xcscheme │ ├── TabBarTest.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── TabBarTest │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── main.m │ └── TabBarTestTests │ │ ├── Info.plist │ │ └── TabBarTestTests.m ├── jest.config.js ├── metro.config.js ├── package-lock.json ├── package.json ├── react-native-head-tab-view │ ├── GestureContainer.tsx │ ├── HeaderContext.tsx │ ├── README.md │ ├── RefreshControlContainer.tsx │ ├── createCollapsibleScrollView.tsx │ ├── hook.tsx │ ├── index.d.ts │ ├── index.js │ ├── package.json │ ├── types.tsx │ └── utils.tsx ├── react-native-scrollable-tab-view-collapsible-header │ ├── CollapsibleHeaderTabView.tsx │ ├── README.md │ ├── createHeaderTabsComponent.tsx │ ├── index.d.ts │ ├── index.js │ └── package.json ├── react-native-tab-view-collapsible-header │ ├── CollapsibleHeaderTabView.tsx │ ├── README.md │ ├── createHeaderTabsComponent.tsx │ ├── index.d.ts │ ├── index.js │ └── package.json ├── src │ ├── Example.tsx │ ├── ExampleBasic.tsx │ ├── ExampleCarouselHeader.tsx │ ├── ExampleCustomTabbar.tsx │ ├── ExampleHeaderAnimated.tsx │ ├── ExampleWithPullRefresh.tsx │ ├── ExampleWithTabViewPullRefresh.tsx │ ├── MainScreen.tsx │ ├── component │ │ ├── CustomRefreshControl.tsx │ │ ├── FlatListPage.tsx │ │ ├── ScrollViewPage.tsx │ │ ├── SectionListPage.tsx │ │ ├── TabViewBase.tsx │ │ ├── TestScrollView.tsx │ │ └── index.ts │ ├── config │ │ ├── NavigationService.js │ │ ├── getScreenOptions.js │ │ └── staticData.ts │ ├── hook.tsx │ ├── resource │ │ ├── header_icon.png │ │ ├── header_img.png │ │ ├── img1.jpeg │ │ ├── img10.jpeg │ │ ├── img11.jpeg │ │ ├── img12.jpeg │ │ ├── img13.jpeg │ │ ├── img14.jpeg │ │ ├── img15.jpeg │ │ ├── img2.jpeg │ │ ├── img3.jpeg │ │ ├── img4.jpeg │ │ ├── img5.jpeg │ │ ├── img6.jpeg │ │ ├── img7.jpeg │ │ ├── img8.jpeg │ │ ├── img9.jpg │ │ ├── refresh.png │ │ ├── tab1.png │ │ ├── tab2.png │ │ └── tab3.png │ ├── styles │ │ └── index.ts │ ├── types.ts │ └── wdyr.ts └── tsconfig.json ├── README.md └── demoGIF └── demo_ios.gif /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/Libraries/react-native/react-native-interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native$' -> '/node_modules/react-native/Libraries/react-native/react-native-implementation' 40 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 41 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 42 | 43 | suppress_type=$FlowIssue 44 | suppress_type=$FlowFixMe 45 | suppress_type=$FlowFixMeProps 46 | suppress_type=$FlowFixMeState 47 | 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 50 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 51 | 52 | [lints] 53 | sketchy-null-number=warn 54 | sketchy-null-mixed=warn 55 | sketchy-number=warn 56 | untyped-type-import=warn 57 | nonstrict-import=warn 58 | deprecated-type=warn 59 | unsafe-getters-setters=warn 60 | inexact-spread=warn 61 | unnecessary-invariant=warn 62 | signature-verification-failure=warn 63 | deprecated-utility=error 64 | 65 | [strict] 66 | deprecated-type 67 | nonstrict-import 68 | sketchy-null 69 | unclear-type 70 | unsafe-getters-setters 71 | untyped-import 72 | untyped-type-import 73 | 74 | [version] 75 | ^0.105.0 76 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | 12 | ### Expected behavior 13 | 14 | ## Package versions 15 | 18 | 19 | - React: 20 | - React Native: 21 | - React-Native-Gesture-Handler 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | Example/ios/Podfile.lock 61 | Example/yarn.lock 62 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /Example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /Example/__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /Example/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.tabbartest", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.tabbartest", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /Example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation. If none specified and 19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is 20 | * // default. Can be overridden with ENTRY_FILE environment variable. 21 | * entryFile: "index.android.js", 22 | * 23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format 24 | * bundleCommand: "ram-bundle", 25 | * 26 | * // whether to bundle JS and assets in debug mode 27 | * bundleInDebug: false, 28 | * 29 | * // whether to bundle JS and assets in release mode 30 | * bundleInRelease: true, 31 | * 32 | * // whether to bundle JS and assets in another build variant (if configured). 33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 34 | * // The configuration property can be in the following formats 35 | * // 'bundleIn${productFlavor}${buildType}' 36 | * // 'bundleIn${buildType}' 37 | * // bundleInFreeDebug: true, 38 | * // bundleInPaidRelease: true, 39 | * // bundleInBeta: true, 40 | * 41 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 42 | * // for example: to disable dev mode in the staging build type (if configured) 43 | * devDisabledInStaging: true, 44 | * // The configuration property can be in the following formats 45 | * // 'devDisabledIn${productFlavor}${buildType}' 46 | * // 'devDisabledIn${buildType}' 47 | * 48 | * // the root of your project, i.e. where "package.json" lives 49 | * root: "../../", 50 | * 51 | * // where to put the JS bundle asset in debug mode 52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 53 | * 54 | * // where to put the JS bundle asset in release mode 55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 56 | * 57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 58 | * // require('./image.png')), in debug mode 59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 60 | * 61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 62 | * // require('./image.png')), in release mode 63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 64 | * 65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 69 | * // for example, you might want to remove it from here. 70 | * inputExcludes: ["android/**", "ios/**"], 71 | * 72 | * // override which node gets called and with what additional arguments 73 | * nodeExecutableAndArgs: ["node"], 74 | * 75 | * // supply additional arguments to the packager 76 | * extraPackagerArgs: [] 77 | * ] 78 | */ 79 | 80 | project.ext.react = [ 81 | enableHermes: true, // clean and rebuild if changing 82 | ] 83 | 84 | apply from: "../../node_modules/react-native/react.gradle" 85 | 86 | /** 87 | * Set this to true to create two separate APKs instead of one: 88 | * - An APK that only works on ARM devices 89 | * - An APK that only works on x86 devices 90 | * The advantage is the size of the APK is reduced by about 4MB. 91 | * Upload all the APKs to the Play Store and people will download 92 | * the correct one based on the CPU architecture of their device. 93 | */ 94 | def enableSeparateBuildPerCPUArchitecture = false 95 | 96 | /** 97 | * Run Proguard to shrink the Java bytecode in release builds. 98 | */ 99 | def enableProguardInReleaseBuilds = false 100 | 101 | /** 102 | * The preferred build flavor of JavaScriptCore. 103 | * 104 | * For example, to use the international variant, you can use: 105 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 106 | * 107 | * The international variant includes ICU i18n library and necessary data 108 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 109 | * give correct results when using with locales other than en-US. Note that 110 | * this variant is about 6MiB larger per architecture than default. 111 | */ 112 | def jscFlavor = 'org.webkit:android-jsc:+' 113 | 114 | /** 115 | * Whether to enable the Hermes VM. 116 | * 117 | * This should be set on project.ext.react and mirrored here. If it is not set 118 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 119 | * and the benefits of using Hermes will therefore be sharply reduced. 120 | */ 121 | def enableHermes = project.ext.react.get("enableHermes", false); 122 | 123 | android { 124 | compileSdkVersion rootProject.ext.compileSdkVersion 125 | 126 | compileOptions { 127 | sourceCompatibility JavaVersion.VERSION_1_8 128 | targetCompatibility JavaVersion.VERSION_1_8 129 | } 130 | 131 | defaultConfig { 132 | applicationId "com.tabbartest" 133 | minSdkVersion rootProject.ext.minSdkVersion 134 | targetSdkVersion rootProject.ext.targetSdkVersion 135 | versionCode 1 136 | versionName "1.0" 137 | } 138 | splits { 139 | abi { 140 | reset() 141 | enable enableSeparateBuildPerCPUArchitecture 142 | universalApk false // If true, also generate a universal APK 143 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 144 | } 145 | } 146 | signingConfigs { 147 | debug { 148 | storeFile file('debug.keystore') 149 | storePassword 'android' 150 | keyAlias 'androiddebugkey' 151 | keyPassword 'android' 152 | } 153 | } 154 | buildTypes { 155 | debug { 156 | signingConfig signingConfigs.debug 157 | } 158 | release { 159 | // Caution! In production, you need to generate your own keystore file. 160 | // see https://reactnative.dev/docs/signed-apk-android. 161 | signingConfig signingConfigs.debug 162 | minifyEnabled enableProguardInReleaseBuilds 163 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 164 | } 165 | } 166 | 167 | // applicationVariants are e.g. debug, release 168 | applicationVariants.all { variant -> 169 | variant.outputs.each { output -> 170 | // For each separate APK per architecture, set a unique version code as described here: 171 | // https://developer.android.com/studio/build/configure-apk-splits.html 172 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 173 | def abi = output.getFilter(OutputFile.ABI) 174 | if (abi != null) { // null for the universal-debug, universal-release variants 175 | output.versionCodeOverride = 176 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 177 | } 178 | 179 | } 180 | } 181 | } 182 | 183 | dependencies { 184 | implementation fileTree(dir: "libs", include: ["*.jar"]) 185 | //noinspection GradleDynamicVersion 186 | implementation "com.facebook.react:react-native:+" // From node_modules 187 | 188 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" 189 | 190 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { 191 | exclude group:'com.facebook.fbjni' 192 | } 193 | 194 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 195 | exclude group:'com.facebook.flipper' 196 | exclude group:'com.squareup.okhttp3', module:'okhttp' 197 | } 198 | 199 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { 200 | exclude group:'com.facebook.flipper' 201 | } 202 | 203 | if (enableHermes) { 204 | def hermesPath = "../../node_modules/hermes-engine/android/"; 205 | debugImplementation files(hermesPath + "hermes-debug.aar") 206 | releaseImplementation files(hermesPath + "hermes-release.aar") 207 | } else { 208 | implementation jscFlavor 209 | } 210 | } 211 | 212 | // Run this once to be able to run the application with BUCK 213 | // puts all compile dependencies into folder libs for BUCK to use 214 | task copyDownloadableDepsToLibs(type: Copy) { 215 | from configurations.compile 216 | into 'libs' 217 | } 218 | 219 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 220 | -------------------------------------------------------------------------------- /Example/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /Example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/debug.keystore -------------------------------------------------------------------------------- /Example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /Example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/android/app/src/debug/java/com/tabbartest/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.tabbartest; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | 32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 33 | client.addPlugin(new ReactFlipperPlugin()); 34 | client.addPlugin(new DatabasesFlipperPlugin(context)); 35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 36 | client.addPlugin(CrashReporterPlugin.getInstance()); 37 | 38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 39 | NetworkingModule.setCustomClientBuilder( 40 | new NetworkingModule.CustomClientBuilder() { 41 | @Override 42 | public void apply(OkHttpClient.Builder builder) { 43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 44 | } 45 | }); 46 | client.addPlugin(networkFlipperPlugin); 47 | client.start(); 48 | 49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 50 | // Hence we run if after all native modules have been initialized 51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 52 | if (reactContext == null) { 53 | reactInstanceManager.addReactInstanceEventListener( 54 | new ReactInstanceManager.ReactInstanceEventListener() { 55 | @Override 56 | public void onReactContextInitialized(ReactContext reactContext) { 57 | reactInstanceManager.removeReactInstanceEventListener(this); 58 | reactContext.runOnNativeModulesQueueThread( 59 | new Runnable() { 60 | @Override 61 | public void run() { 62 | client.addPlugin(new FrescoFlipperPlugin()); 63 | } 64 | }); 65 | } 66 | }); 67 | } else { 68 | client.addPlugin(new FrescoFlipperPlugin()); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/android/app/src/main/java/com/tabbartest/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.tabbartest; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "TabBarTest"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Example/android/app/src/main/java/com/tabbartest/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.tabbartest; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.facebook.react.BuildConfig; 7 | import com.facebook.react.PackageList; 8 | import com.facebook.react.ReactApplication; 9 | import com.facebook.react.ReactInstanceManager; 10 | import com.facebook.react.ReactNativeHost; 11 | import com.facebook.react.ReactPackage; 12 | import com.facebook.react.bridge.JSIModulePackage; 13 | import com.facebook.soloader.SoLoader; 14 | import com.swmansion.reanimated.ReanimatedJSIModulePackage; 15 | 16 | import java.lang.reflect.InvocationTargetException; 17 | import java.util.List; 18 | 19 | public class MainApplication extends Application implements ReactApplication { 20 | 21 | private final ReactNativeHost mReactNativeHost = 22 | new ReactNativeHost(this) { 23 | @Override 24 | public boolean getUseDeveloperSupport() { 25 | return true; 26 | } 27 | 28 | @Override 29 | protected List getPackages() { 30 | @SuppressWarnings("UnnecessaryLocalVariable") 31 | List packages = new PackageList(this).getPackages(); 32 | // Packages that cannot be autolinked yet can be added manually here, for example: 33 | // packages.add(new MyReactNativePackage()); 34 | return packages; 35 | } 36 | 37 | @Override 38 | protected String getJSMainModuleName() { 39 | return "index"; 40 | } 41 | 42 | @Override 43 | protected JSIModulePackage getJSIModulePackage() { 44 | return new ReanimatedJSIModulePackage(); // <- add 45 | } 46 | }; 47 | 48 | @Override 49 | public ReactNativeHost getReactNativeHost() { 50 | return mReactNativeHost; 51 | } 52 | 53 | @Override 54 | public void onCreate() { 55 | super.onCreate(); 56 | SoLoader.init(this, /* native exopackage */ false); 57 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 58 | } 59 | 60 | /** 61 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 62 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 63 | * 64 | * @param context 65 | * @param reactInstanceManager 66 | */ 67 | private static void initializeFlipper( 68 | Context context, ReactInstanceManager reactInstanceManager) { 69 | if (BuildConfig.DEBUG) { 70 | try { 71 | /* 72 | We use reflection here to pick up the class that initializes Flipper, 73 | since Flipper library is not available in release mode 74 | */ 75 | Class aClass = Class.forName("com.tabbartest.ReactNativeFlipper"); 76 | aClass 77 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 78 | .invoke(null, context, reactInstanceManager); 79 | } catch (ClassNotFoundException e) { 80 | e.printStackTrace(); 81 | } catch (NoSuchMethodException e) { 82 | e.printStackTrace(); 83 | } catch (IllegalAccessException e) { 84 | e.printStackTrace(); 85 | } catch (InvocationTargetException e) { 86 | e.printStackTrace(); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_stack_src_views_assets_backiconmask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_stack_src_views_assets_backiconmask.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_header_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_header_img.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img1.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img10.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img11.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img12.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img13.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img13.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img14.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img14.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img15.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img15.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img2.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img3.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img4.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img5.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img6.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img7.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img8.jpeg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_img9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_img9.jpg -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_tab1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_tab1.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_tab2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_tab2.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-mdpi/src_resource_tab3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-mdpi/src_resource_tab3.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_stack_src_views_assets_backicon.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/raw/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TabBarTest", 3 | "displayName": "TabBarTest" 4 | } -------------------------------------------------------------------------------- /Example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TabBarTest 3 | 4 | -------------------------------------------------------------------------------- /Example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "29.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.5.3") 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | mavenLocal() 24 | maven { 25 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 26 | url("$rootDir/../node_modules/react-native/android") 27 | } 28 | maven { 29 | // Android JSC is installed from npm 30 | url("$rootDir/../node_modules/jsc-android/dist") 31 | } 32 | 33 | google() 34 | jcenter() 35 | maven { url 'https://www.jitpack.io' } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.54.0 29 | -------------------------------------------------------------------------------- /Example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /Example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /Example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /Example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'TabBarTest' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /Example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TabBarTest", 3 | "displayName": "TabBarTest" 4 | } -------------------------------------------------------------------------------- /Example/assetsTransformer.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | process(src, filename, config, options) { 5 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; 6 | }, 7 | }; -------------------------------------------------------------------------------- /Example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | plugins: ['react-native-reanimated/plugin'], 4 | }; 5 | -------------------------------------------------------------------------------- /Example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | // import './src/wdyr'; 5 | import {AppRegistry} from 'react-native'; 6 | import Example from './src/Example'; 7 | import {name as appName} from './app.json'; 8 | import 'react-native-gesture-handler'; 9 | 10 | AppRegistry.registerComponent(appName, () => Example); 11 | -------------------------------------------------------------------------------- /Example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'TabBarTest' do 7 | config = use_native_modules! 8 | pod 'OpenSSL-Universal', '~>1.0.2.20' 9 | 10 | use_react_native!(:path => config["reactNativePath"]) 11 | 12 | target 'TabBarTestTests' do 13 | inherit! :complete 14 | # Pods for testing 15 | end 16 | 17 | # Enables Flipper. 18 | # 19 | # Note that if you have use_frameworks! enabled, Flipper will not work and 20 | # you should disable these next few lines. 21 | use_flipper! 22 | post_install do |installer| 23 | flipper_post_install(installer) 24 | end 25 | end 26 | 27 | target 'TabBarTest-tvOS' do 28 | # Pods for TabBarTest-tvOS 29 | 30 | target 'TabBarTest-tvOSTests' do 31 | inherit! :search_paths 32 | # Pods for testing 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest.xcodeproj/xcshareddata/xcschemes/TabBarTest-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest.xcodeproj/xcshareddata/xcschemes/TabBarTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #ifdef FB_SONARKIT_ENABLED 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | static void InitializeFlipper(UIApplication *application) { 16 | FlipperClient *client = [FlipperClient sharedClient]; 17 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 18 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 19 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 20 | [client addPlugin:[FlipperKitReactPlugin new]]; 21 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 22 | [client start]; 23 | } 24 | #endif 25 | 26 | @implementation AppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 29 | { 30 | #ifdef FB_SONARKIT_ENABLED 31 | InitializeFlipper(application); 32 | #endif 33 | 34 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 35 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 36 | moduleName:@"TabBarTest" 37 | initialProperties:nil]; 38 | 39 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 40 | 41 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 42 | UIViewController *rootViewController = [UIViewController new]; 43 | rootViewController.view = rootView; 44 | self.window.rootViewController = rootViewController; 45 | [self.window makeKeyAndVisible]; 46 | return YES; 47 | } 48 | 49 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 50 | { 51 | #if DEBUG 52 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 53 | #else 54 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 55 | #endif 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | TabBarTest 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UIViewControllerBasedStatusBarAppearance 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/ios/TabBarTest/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Example/ios/TabBarTestTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/ios/TabBarTestTests/TabBarTestTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface TabBarTestTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation TabBarTestTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Example/jest.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | preset: 'react-native', 4 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 5 | setupFiles: ["./node_modules/react-native-gesture-handler/jestSetup.js"], 6 | moduleNameMapper: { 7 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/assetsTransformer.js", 8 | "\\.(css|less)$": "/assetsTransformer.js" 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /Example/metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /Example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TabBarTest", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@react-native-community/viewpager": { 8 | "version": "3.3.0", 9 | "resolved": "https://registry.npmjs.org/@react-native-community/viewpager/-/viewpager-3.3.0.tgz", 10 | "integrity": "sha512-tyzh79l4t/hxiyS9QD3LRmWMs8KVkZzjrkQ8U8+8To1wmvVCBtp8BenvNsDLTBO7CpO/YmiThpmIdEZMr1WuVw==" 11 | }, 12 | "react-native-head-tab-view": { 13 | "version": "1.0.1", 14 | "resolved": "https://registry.npmjs.org/react-native-head-tab-view/-/react-native-head-tab-view-1.0.1.tgz", 15 | "integrity": "sha512-VgQA4MeQJVX0h4BwIHJh2zKTeJAebe2n4AxDbg9z1t6f20i/K9Xr9ARPi9UrO0yDZSj9RidgcB2wrviXaIwc/g==", 16 | "requires": { 17 | "@react-native-community/viewpager": "^3.3.0" 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TabBarTest", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "eslint ." 11 | }, 12 | "dependencies": { 13 | "@react-native-community/masked-view": "^0.1.10", 14 | "@react-native-community/viewpager": "^3.3.0", 15 | "@react-navigation/native": "^5.4.3", 16 | "@react-navigation/stack": "^5.4.0", 17 | "@types/react-native-scrollable-tab-view": "^0.10.1", 18 | "@welldone-software/why-did-you-render": "^6.0.5", 19 | "add": "^2.0.6", 20 | "react": "16.13.1", 21 | "react-native": "0.63.4", 22 | "react-native-gesture-handler": "^1.6.1", 23 | "react-native-head-tab-view": "file:./react-native-head-tab-view", 24 | "react-native-reanimated": "^2.0.0", 25 | "react-native-safe-area-context": "^2.0.2", 26 | "react-native-screens": "^2.8.0", 27 | "react-native-scrollable-tab-view": "^1.0.0", 28 | "react-native-snap-carousel": "^4.0.0-beta.6", 29 | "react-native-tab-view": "^2.15.2", 30 | "yarn": "^1.22.10" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "^7.12.17", 34 | "@babel/runtime": "^7.12.18", 35 | "@react-native-community/eslint-config": "^2.0.0", 36 | "@testing-library/jest-native": "^3.4.3", 37 | "@testing-library/react-native": "^7.1.0", 38 | "@types/jest": "^25.1.1", 39 | "@types/react": "^16.9.19", 40 | "@types/react-native": "^0.61.7", 41 | "@types/react-test-renderer": "^16.9.2", 42 | "babel-jest": "^26.6.3", 43 | "eslint": "^7.20.0", 44 | "jest": "^26.6.3", 45 | "jetifier": "^1.6.4", 46 | "metro-react-native-babel-preset": "^0.65.1", 47 | "react-test-renderer": "16.13.1", 48 | "typescript": "^3.7.5" 49 | }, 50 | "jest": { 51 | "preset": "react-native" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/react-native-head-tab-view/HeaderContext.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { IHeaderContext } from './types' 4 | export const HeaderContext = React.createContext(undefined); 5 | -------------------------------------------------------------------------------- /Example/react-native-head-tab-view/RefreshControlContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | TextInput, 4 | ActivityIndicator, 5 | StyleSheet 6 | } from 'react-native'; 7 | import Animated, { 8 | useAnimatedProps, 9 | createAnimatedPropAdapter, 10 | useSharedValue, 11 | useDerivedValue, 12 | useAnimatedStyle, 13 | interpolate, 14 | useAnimatedReaction, 15 | } from 'react-native-reanimated' 16 | import { RefreshControlProps, RefreshType } from './types' 17 | import { useRefreshDerivedValue } from './hook' 18 | interface RefreshControlContainerProps { 19 | top: number 20 | refreshHeight: number 21 | overflowPull: number 22 | opacityValue: Animated.SharedValue 23 | refreshValue: Animated.SharedValue 24 | isRefreshing: Animated.SharedValue 25 | isRefreshingWithAnimation: Animated.SharedValue 26 | pullExtendedCoefficient: number 27 | renderContent?: (refreshProps: RefreshControlProps) => React.ReactElement 28 | } 29 | 30 | const RefreshControlContainer: React.FC = ({ 31 | top, 32 | refreshHeight, 33 | overflowPull, 34 | opacityValue, 35 | refreshValue, 36 | isRefreshing, 37 | isRefreshingWithAnimation, 38 | pullExtendedCoefficient, 39 | renderContent 40 | }) => { 41 | const refreshType: { value: RefreshType } = useSharedValue('pullToRefresh') 42 | const progress = useDerivedValue(() => { 43 | if (isRefreshingWithAnimation.value) return 1 44 | return Math.min(refreshValue.value / refreshHeight, 1) 45 | }) 46 | 47 | const tranYValue = useRefreshDerivedValue({ 48 | animatedValue: refreshValue, 49 | refreshHeight, 50 | overflowPull, 51 | pullExtendedCoefficient 52 | }) 53 | 54 | useAnimatedReaction(() => { 55 | return { 56 | prs: progress.value, 57 | isR: isRefreshing.value, 58 | isRA: isRefreshingWithAnimation.value 59 | } 60 | }, ({ prs, isR, isRA }) => { 61 | if (isR !== isRA) { 62 | refreshType.value = isR ? 'prepare' : 'finish' 63 | return 64 | } 65 | if (isR) { 66 | refreshType.value = 'refreshing' 67 | } else { 68 | refreshType.value = prs < 1 ? 'pullToRefresh' : 'enough' 69 | } 70 | }, [refreshHeight]) 71 | 72 | const animatedStyle = useAnimatedStyle(() => { 73 | return { 74 | opacity: opacityValue.value, 75 | transform: [ 76 | { 77 | translateY: tranYValue.value, 78 | }, 79 | ] 80 | }; 81 | }); 82 | 83 | const _renderContent = () => { 84 | const _props = makeChildProps() 85 | if (renderContent) { 86 | return React.cloneElement(renderContent(_props), makeChildProps()) 87 | } 88 | return 89 | } 90 | 91 | const makeChildProps = () => { 92 | return { 93 | refreshValue, 94 | refreshType, 95 | progress 96 | } 97 | } 98 | 99 | return 102 | {_renderContent()} 103 | 104 | } 105 | 106 | export default RefreshControlContainer 107 | 108 | const AnimatedText = Animated.createAnimatedComponent(TextInput); 109 | 110 | 111 | 112 | const TextInputAdapter = createAnimatedPropAdapter( 113 | (props) => { 114 | 'worklet'; 115 | const keys = Object.keys(props); 116 | if (keys.includes('value')) { 117 | props.text = props.value; 118 | delete props.value; 119 | } 120 | }, 121 | ['text'] 122 | ); 123 | 124 | const RefreshControlNormal: React.FC = ({ refreshValue, refreshType, progress }: RefreshControlProps) => { 125 | 126 | const textInputProps = useAnimatedProps( 127 | () => { 128 | return { 129 | value: Math.round(progress.value * 100) + '%', 130 | }; 131 | }, 132 | null, 133 | [TextInputAdapter] 134 | ); 135 | 136 | return ( 137 | 138 | 139 | 145 | 146 | ) 147 | } 148 | 149 | const styles = StyleSheet.create({ 150 | container: { 151 | position: 'absolute', 152 | left: 0, 153 | right: 0, 154 | width: '100%', 155 | backgroundColor: 'transparent' 156 | }, 157 | baseControl: { 158 | flex: 1, 159 | justifyContent: 'center', 160 | alignItems: 'center', 161 | paddingTop: 10, 162 | backgroundColor: '#fff' 163 | }, 164 | textStyle: { 165 | color: '#26323F', 166 | marginTop: 5, 167 | fontSize: 17, 168 | width:100, 169 | textAlign:'center' 170 | } 171 | }) -------------------------------------------------------------------------------- /Example/react-native-head-tab-view/hook.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext, useEffect, useCallback, useRef, useMemo } from 'react'; 2 | import { StyleProp, ViewStyle, StyleSheet } from 'react-native' 3 | import { HeaderContext } from './HeaderContext' 4 | import { ForwardRefType, updateSceneInfoType } from './types' 5 | import { mScrollTo } from './utils' 6 | import Animated, { 7 | useAnimatedRef, 8 | useSharedValue, 9 | runOnUI, 10 | withTiming, 11 | useDerivedValue, 12 | interpolate, 13 | useAnimatedReaction, 14 | runOnJS 15 | } from 'react-native-reanimated' 16 | 17 | export const useSceneContext = () => { 18 | const z = useContext(HeaderContext) 19 | if (!z) throw new Error('HeaderContext not found') 20 | return z; 21 | } 22 | 23 | export function useSharedScrollableRef( 24 | forwardRef: ForwardRefType 25 | ) { 26 | const ref = useAnimatedRef() 27 | 28 | useEffect(() => { 29 | if (!forwardRef) { 30 | return 31 | } 32 | if (typeof forwardRef === 'function') { 33 | forwardRef(ref.current) 34 | } else { 35 | forwardRef.current = ref.current 36 | } 37 | }) 38 | 39 | return ref 40 | } 41 | 42 | export const useSyncInitialPosition = (_scrollView: any) => { 43 | const needTryScroll = useRef(true) 44 | const opacityValue = useSharedValue(0) 45 | const { shareAnimatedValue, headerHeight, frozeTop } = useSceneContext() 46 | 47 | const syncInitialPosition = useCallback((position: number) => { 48 | if (!needTryScroll.current) return; 49 | needTryScroll.current = false; 50 | const calcH = headerHeight - frozeTop 51 | let scrollValue = Math.min(position, calcH) 52 | 53 | runOnUI(mScrollTo)(_scrollView, 0, scrollValue, false) 54 | requestAnimationFrame(() => { 55 | opacityValue.value = withTiming(1) 56 | }) 57 | }, [shareAnimatedValue, headerHeight]) 58 | 59 | return { 60 | opacityValue, 61 | syncInitialPosition 62 | } 63 | } 64 | 65 | export const useSceneInfo = (curIndexValue: Animated.SharedValue) => { 66 | //Are all the fields on the scene ready 67 | const sceneIsReady = useSharedValue<{ [index: number]: boolean }>({}) 68 | const [sceneScrollEnabledValue, setSceneScrollEnabledValue] = useState<{ [index: number]: Animated.SharedValue }>({}) 69 | const [childScrollYTrans, setChildScrollYTrans] = useState<{ [index: number]: Animated.SharedValue }>({}) 70 | const [childScrollRef, setChildScrollRef] = useState<{ [index: number]: any }>({}) 71 | const [sceneRefreshTrans, setSceneRefreshTrans] = useState<{ [index: number]: Animated.SharedValue }>({}) 72 | const [sceneIsRefreshing, setSceneIsRefreshing] = useState<{ [index: number]: Animated.SharedValue }>({}) 73 | const [sceneIsRefreshingWithAnimation, setSceneIsRefreshingWithAnimation] = useState<{ [index: number]: Animated.SharedValue }>({}) 74 | const [sceneIsDragging, setSceneIsDragging] = useState<{ [index: number]: Animated.SharedValue }>({}) 75 | const [sceneIsLosingMomentum, setSceneIsScrolling] = useState<{ [index: number]: Animated.SharedValue }>({}) 76 | const [sceneCanPullRefresh, setSceneCanPullRefresh] = useState<{ [index: number]: boolean }>({}) 77 | const [sceneRefreshCallBack, setSceneRefreshCallBack] = useState<{ [index: number]: (isToRefresh: boolean) => void }>({}) 78 | 79 | const updateSceneInfo = useCallback(({ 80 | index, 81 | scrollRef, 82 | scrollY, 83 | canPullRefresh, 84 | isRefreshing, 85 | isRefreshingWithAnimation, 86 | refreshTrans, 87 | isDragging, 88 | scrollEnabledValue, 89 | isLosingMomentum, 90 | onRefreshStatusCallback 91 | }: updateSceneInfoType) => { 92 | 93 | if (scrollRef && childScrollRef[index] !== scrollRef) { 94 | setChildScrollRef(preChildRef => { 95 | return { ...preChildRef, [index]: scrollRef } 96 | }) 97 | } 98 | if (isLosingMomentum && sceneIsLosingMomentum[index] !== isLosingMomentum) { 99 | setSceneIsScrolling(_p => { 100 | return { ..._p, [index]: isLosingMomentum } 101 | }) 102 | } 103 | if (isRefreshing !== sceneIsRefreshing[index]) { 104 | setSceneIsRefreshing(_p => { 105 | return { ..._p, [index]: isRefreshing } 106 | }) 107 | } 108 | if (isRefreshingWithAnimation !== sceneIsRefreshingWithAnimation[index]) { 109 | setSceneIsRefreshingWithAnimation(_p => { 110 | return { ..._p, [index]: isRefreshingWithAnimation } 111 | }) 112 | } 113 | if (isDragging !== sceneIsDragging[index]) { 114 | setSceneIsDragging(_p => { 115 | return { ..._p, [index]: isDragging } 116 | }) 117 | } 118 | if (refreshTrans !== sceneRefreshTrans[index]) { 119 | setSceneRefreshTrans(_p => { 120 | return { ..._p, [index]: refreshTrans } 121 | }) 122 | } 123 | 124 | if (canPullRefresh !== sceneCanPullRefresh[index]) { 125 | setSceneCanPullRefresh(_p => { 126 | return { ..._p, [index]: canPullRefresh } 127 | }) 128 | } 129 | if (scrollY && childScrollYTrans[index] !== scrollY) { 130 | setChildScrollYTrans(_p => { 131 | return { ..._p, [index]: scrollY } 132 | }) 133 | } 134 | if (scrollEnabledValue && sceneScrollEnabledValue[index] !== scrollEnabledValue) { 135 | setSceneScrollEnabledValue(_p => { 136 | return { ..._p, [index]: scrollEnabledValue } 137 | }) 138 | } 139 | if (onRefreshStatusCallback && sceneRefreshCallBack[index] !== onRefreshStatusCallback) { 140 | setSceneRefreshCallBack(_p => { 141 | return { ..._p, [index]: onRefreshStatusCallback } 142 | }) 143 | } 144 | }, []) 145 | 146 | const aArray = [childScrollRef, sceneRefreshTrans, childScrollYTrans, sceneIsRefreshing, sceneIsDragging, sceneCanPullRefresh, sceneRefreshCallBack, sceneScrollEnabledValue, sceneIsRefreshingWithAnimation, sceneIsLosingMomentum] 147 | const updateIsReady = useCallback(() => { 148 | const mIndex = curIndexValue.value 149 | if (sceneIsReady.value[mIndex]) return 150 | 151 | const isReady = aArray.every(item => item.hasOwnProperty(mIndex)) 152 | if (isReady) { 153 | sceneIsReady.value = { 154 | ...sceneIsReady.value, 155 | [mIndex]: isReady 156 | } 157 | } 158 | }, [curIndexValue, sceneIsReady, ...aArray]) 159 | 160 | //We should call function updateIsReady when the elements in the aArray change 161 | useEffect(() => { 162 | updateIsReady() 163 | }, [updateIsReady, ...aArray]) 164 | 165 | /** 166 | * If all of the elements in the Aarray have changed, the tabIndex is switched. 167 | * At this point the above useEffect will not be called again, 168 | * and we will have to call the updateisReady function again. 169 | */ 170 | useAnimatedReaction(() => { 171 | return curIndexValue.value 172 | }, () => { 173 | runOnJS(updateIsReady)() 174 | }, [updateIsReady]) 175 | 176 | return { 177 | childScrollRef, 178 | sceneRefreshTrans, 179 | childScrollYTrans, 180 | sceneIsRefreshing, 181 | sceneIsDragging, 182 | sceneCanPullRefresh, 183 | sceneRefreshCallBack, 184 | sceneScrollEnabledValue, 185 | sceneIsRefreshingWithAnimation, 186 | sceneIsLosingMomentum, 187 | sceneIsReady, 188 | updateSceneInfo 189 | } 190 | } 191 | 192 | export const useRefreshDerivedValue = ({ 193 | refreshHeight, 194 | overflowPull, 195 | animatedValue, 196 | pullExtendedCoefficient 197 | }: { 198 | refreshHeight: number 199 | overflowPull: number 200 | animatedValue: Animated.SharedValue 201 | pullExtendedCoefficient: number 202 | }) => { 203 | return useDerivedValue(() => { 204 | return interpolate( 205 | animatedValue.value, 206 | [0, refreshHeight + overflowPull, refreshHeight + overflowPull + 1], 207 | [0, refreshHeight + overflowPull, refreshHeight + overflowPull + pullExtendedCoefficient], 208 | ) 209 | }) 210 | } 211 | 212 | export const useVerifyProps = ( 213 | { 214 | scrollEventThrottle, 215 | directionalLockEnabled, 216 | contentContainerStyle, 217 | scrollIndicatorInsets 218 | }: { 219 | scrollEventThrottle?: number, 220 | directionalLockEnabled?: boolean, 221 | contentContainerStyle?: StyleProp, 222 | scrollIndicatorInsets?: { top?: number, left?: number, bottom?: number, right?: number } 223 | } 224 | 225 | ) => { 226 | let _scrollIndicatorInsets 227 | let _contentContainerStyle 228 | 229 | if (scrollEventThrottle !== undefined) { 230 | console.warn("Please do not assign scrollEventThrottle") 231 | } 232 | if (directionalLockEnabled !== undefined) { 233 | console.warn("Please do not assign directionalLockEnabled") 234 | } 235 | if (scrollIndicatorInsets && scrollIndicatorInsets.top !== undefined) { 236 | console.warn("Please do not assign scrollIndicatorInsets.top") 237 | const { top, ...restS } = scrollIndicatorInsets 238 | _scrollIndicatorInsets = restS 239 | } 240 | if (contentContainerStyle) { 241 | const tempStyle = StyleSheet.flatten(contentContainerStyle) 242 | if (tempStyle.paddingTop !== undefined || tempStyle.minHeight !== undefined) { 243 | console.warn("Please do not assign paddingTop and minHeight to contentContainerStyle") 244 | const { paddingTop, minHeight, ...restC } = tempStyle 245 | _contentContainerStyle = restC 246 | } 247 | 248 | } 249 | 250 | return { scrollIndicatorInsets: _scrollIndicatorInsets, contentContainerStyle: _contentContainerStyle } 251 | 252 | } -------------------------------------------------------------------------------- /Example/react-native-head-tab-view/index.d.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import * as React from 'react'; 4 | import { ScrollView, FlatList, SectionList, ScrollViewProps, FlatListProps, SectionListProps } from 'react-native'; 5 | import { default as Reanimated2 } from 'react-native-reanimated' 6 | export interface CollapsibleHeaderProps { 7 | /** 8 | * render the collapsible header 9 | */ 10 | renderScrollHeader: () => React.ComponentType | React.ReactElement | null; 11 | /** 12 | * The height of collapsible header 13 | */ 14 | headerHeight?: number; 15 | /** 16 | * The height of the Tabbar. 17 | * If this parameter is set, the initial rendering performance will be improved 18 | */ 19 | tabbarHeight?: number 20 | /** 21 | * The height at which the top area of the Tabview is frozen 22 | * 23 | */ 24 | frozeTop?: number; 25 | /** 26 | * Sets the upward offset distance of the TabView and TabBar 27 | */ 28 | overflowHeight?: number; 29 | /** 30 | * Gets the Animated.Value for the current active scene 31 | * You can use it for animation 32 | */ 33 | makeScrollTrans?: (scrollValue: Reanimated2.SharedValue) => void; 34 | /** 35 | * Whether to allow the scene to slide vertically 36 | */ 37 | scrollEnabled?: boolean; 38 | /** 39 | * Whether the TabView is refreshing 40 | */ 41 | isRefreshing?: boolean; 42 | /** 43 | * If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. 44 | * Make sure to also set the isRefreshing prop correctly. 45 | */ 46 | onStartRefresh?: () => void; 47 | /** 48 | * A custom RefreshControl 49 | */ 50 | renderRefreshControl?: (refreshProps: RefreshControlProps) => React.ReactElement; 51 | /** 52 | * If this height is reached, a refresh event will be triggered (onStartRefresh) 53 | * defaults to 80 54 | */ 55 | refreshHeight?: number; 56 | /** 57 | * It's the distance beyond the refreshHeight, the distance to continue the displacement, when the pull is long enough, 58 | * it defaults to 50. 59 | */ 60 | overflowPull?: number; 61 | /** 62 | * When the maximum drop-down distance(refreshHeight+overflowPull) is reached, 63 | * the refreshControl moves the distance for each pixel the finger moves 64 | * The recommended number is between 0 and 1. 65 | */ 66 | pullExtendedCoefficient?: number; 67 | /** 68 | * When it stops sliding, it automatically switches to the folded and expanded states. 69 | * it defaults to false. 70 | */ 71 | enableSnap?: boolean; 72 | /** 73 | * The amount of time between the onScroll function being called. 74 | * (When the interval is longer than scrollingCheckDuration, I think scrolling has stopped) 75 | * it defaults to 50ms. 76 | */ 77 | scrollingCheckDuration?: number 78 | } 79 | 80 | 81 | export interface CommonSceneProps { 82 | index: number; 83 | } 84 | export interface NormalSceneBaseProps extends CommonSceneProps { 85 | /** 86 | * Whether the scene is refreshing 87 | */ 88 | isRefreshing?: boolean; 89 | /** 90 | * If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. 91 | * Make sure to also set the isRefreshing prop correctly. 92 | */ 93 | onStartRefresh?: () => void; 94 | /** 95 | * A custom RefreshControl for scene 96 | */ 97 | renderRefreshControl?: (refreshProps: RefreshControlProps) => React.ReactElement; 98 | /** 99 | * You can provide a LoadingView 100 | * when the scene is transparent until the height of the onContentSizeRange callback is less than minHeight. 101 | */ 102 | renderLoadingView?: (headerHeight: number) => React.ReactElement; 103 | } 104 | 105 | export interface SceneConfig { 106 | 107 | } 108 | 109 | export type RefreshType = 'pullToRefresh' | 'enough' | 'prepare' | 'refreshing' | 'finish' 110 | 111 | export interface RefreshControlProps { 112 | refreshValue: Reanimated2.SharedValue 113 | refreshType: Reanimated2.SharedValue 114 | progress: Reanimated2.SharedValue 115 | } 116 | 117 | type HScrollViewType = React.ForwardRefExoticComponent> & React.RefAttributes & NormalSceneBaseProps> 118 | type HFlatListType = React.ForwardRefExoticComponent> & React.RefAttributes> & NormalSceneBaseProps> 119 | type HSectionListType = React.ForwardRefExoticComponent> & React.RefAttributes> & NormalSceneBaseProps> 120 | 121 | export const HScrollView: HScrollViewType 122 | export const HFlatList: HFlatListType 123 | export const HSectionList: HSectionListType 124 | 125 | 126 | 127 | export interface IGestureContainerProps extends CollapsibleHeaderProps { 128 | initialPage: number 129 | renderTabView: any 130 | } 131 | 132 | export type GestureContainerRef = { 133 | setCurrentIndex: (index: number) => void; 134 | } | undefined 135 | 136 | export const GestureContainer: React.ForwardRefExoticComponent & React.RefAttributes> 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /Example/react-native-head-tab-view/index.js: -------------------------------------------------------------------------------- 1 | import { ScrollView, FlatList, SectionList } from 'react-native' 2 | import createCollapsibleScrollView from './createCollapsibleScrollView' 3 | export { HeaderContext } from './HeaderContext' 4 | export { default as GestureContainer } from './GestureContainer' 5 | 6 | const HScrollView = createCollapsibleScrollView(ScrollView) 7 | const HFlatList = createCollapsibleScrollView(FlatList) 8 | const HSectionList = createCollapsibleScrollView(SectionList) 9 | export { HScrollView, HFlatList, HSectionList } -------------------------------------------------------------------------------- /Example/react-native-head-tab-view/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-head-tab-view", 3 | "version": "4.0.0-rc.13", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "yu zou ", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/zyslife/react-native-head-tab-view.git" 12 | }, 13 | "homepage": "https://github.com/zyslife/react-native-head-tab-view#readme", 14 | "license": "ISC", 15 | "keywords": [ 16 | "react-native-component", 17 | "react-component", 18 | "react-native", 19 | "ios", 20 | "android", 21 | "tab", 22 | "swipe", 23 | "scrollable", 24 | "tabview", 25 | "head", 26 | "stick", 27 | "collapsible-header" 28 | ], 29 | "peerDependencies": { 30 | "react-native-gesture-handler": "^1.6.1", 31 | "react-native-reanimated": "^2.0.1" 32 | }, 33 | "description": "Tab view component for React Native" 34 | } 35 | -------------------------------------------------------------------------------- /Example/react-native-head-tab-view/types.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentClass } from 'react'; 2 | import { FlatListProps, SectionListProps, ScrollViewProps } from 'react-native'; 3 | import { default as Reanimated2 } from 'react-native-reanimated' 4 | export interface CollapsibleHeaderProps { 5 | headerHeight?: number 6 | renderScrollHeader: () => React.ComponentType | React.ReactElement | null; 7 | tabbarHeight?: number 8 | frozeTop?: number; 9 | overflowHeight?: number; 10 | makeScrollTrans?: (scrollValue: Reanimated2.SharedValue) => void; 11 | headerRespond?: boolean; 12 | scrollEnabled?: boolean; 13 | isRefreshing?: boolean; 14 | onStartRefresh?: () => void; 15 | renderRefreshControl?: (refreshProps: RefreshControlProps) => React.ReactElement; 16 | refreshHeight?: number; 17 | overflowPull?: number; 18 | pullExtendedCoefficient?: number; 19 | enableSnap?: boolean 20 | scrollingCheckDuration?: number 21 | } 22 | 23 | export interface CommonSceneProps { 24 | index: number; 25 | } 26 | export interface NormalSceneBaseProps extends CommonSceneProps { 27 | isRefreshing?: boolean; 28 | onStartRefresh?: () => void; 29 | renderRefreshControl?: (refreshProps: RefreshControlProps) => React.ReactElement; 30 | renderLoadingView?: (headerHeight: number) => React.ReactElement; 31 | } 32 | 33 | export interface IGestureContainerProps extends CollapsibleHeaderProps { 34 | initialPage: number 35 | renderTabView: any 36 | forwardedRef: any 37 | } 38 | 39 | 40 | export interface RefreshControlProps { 41 | refreshValue: Reanimated2.SharedValue 42 | refreshType: Reanimated2.SharedValue 43 | progress: Reanimated2.SharedValue 44 | } 45 | 46 | export interface NormalSceneProps extends ScrollViewProps, NormalSceneBaseProps { 47 | ContainerView: any 48 | forwardedRef: any 49 | } 50 | 51 | export interface HPageViewProps { 52 | forwardedRef: React.LegacyRef; 53 | } 54 | 55 | export type RefreshType = 'pullToRefresh' | 'enough' | 'prepare' | 'refreshing' | 'finish' 56 | 57 | export interface IHeaderContext { 58 | tabsIsWorking: Reanimated2.SharedValue 59 | isTouchTabs: Reanimated2.SharedValue 60 | isSlidingHeader: Reanimated2.SharedValue 61 | shareAnimatedValue: Reanimated2.SharedValue 62 | frozeTop: number 63 | tabbarHeight: number 64 | headerHeight: number 65 | refreshHeight: number 66 | overflowPull: number 67 | pullExtendedCoefficient: number 68 | headerTrans: Reanimated2.SharedValue, 69 | expectHeight: number; 70 | tabsRefreshEnabled: boolean 71 | refHasChanged: (ref: React.RefObject) => void; 72 | curIndexValue: Reanimated2.SharedValue 73 | updateSceneInfo: (e: updateSceneInfoType) => void 74 | enableSnap: boolean 75 | scrollingCheckDuration: number 76 | } 77 | 78 | export type updateSceneInfoType = { 79 | scrollRef: any 80 | index: number 81 | refreshTrans: Reanimated2.SharedValue 82 | isRefreshing: Reanimated2.SharedValue 83 | isRefreshingWithAnimation: Reanimated2.SharedValue 84 | isDragging: Reanimated2.SharedValue 85 | scrollEnabledValue: Reanimated2.SharedValue 86 | canPullRefresh: boolean 87 | scrollY: Reanimated2.SharedValue 88 | isLosingMomentum: Reanimated2.SharedValue 89 | onRefreshStatusCallback: (isToRefresh: boolean) => void 90 | } 91 | 92 | export type ScrollableView = ComponentClass | FlatListProps | ScrollViewProps> 93 | 94 | 95 | export enum Direction { 96 | top, 97 | horizontal, 98 | bottom, 99 | } 100 | 101 | export type ForwardRefType = ((instance: T | null) => void) | React.MutableRefObject | null 102 | 103 | export type GesturePanContext = { 104 | starty: number 105 | basyY: number 106 | } -------------------------------------------------------------------------------- /Example/react-native-head-tab-view/utils.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | PanGestureHandlerGestureEvent, 4 | } from 'react-native-gesture-handler'; 5 | import Animated, { 6 | withDecay, 7 | withTiming, 8 | runOnJS, 9 | scrollTo 10 | } from 'react-native-reanimated' 11 | import { GesturePanContext } from './types' 12 | 13 | export function mScrollTo( 14 | ref: any, 15 | x: number, 16 | y: number, 17 | animated: boolean, 18 | ) { 19 | 'worklet'; 20 | if (!ref) return; 21 | scrollTo(ref, x, y, animated) 22 | } 23 | 24 | export const toRunSlide = ({ 25 | transValue, 26 | translationY, 27 | isActive, 28 | getStartY, 29 | ctx 30 | }: { 31 | translationY: number 32 | transValue: Animated.SharedValue 33 | isActive: Animated.SharedValue 34 | getStartY: () => number 35 | ctx: { starty: number } 36 | }) => { 37 | 'worklet' 38 | 39 | 40 | if (isActive.value === false) { 41 | const starty = getStartY() 42 | ctx.starty = starty 43 | isActive.value = true 44 | } 45 | transValue.value = Math.max(-translationY + ctx.starty, 0) 46 | } 47 | 48 | export const toEndSlide = ({ 49 | transValue, 50 | velocityY, 51 | isActive, 52 | ctx 53 | }: { 54 | transValue: Animated.SharedValue 55 | velocityY: number 56 | isActive: Animated.SharedValue 57 | ctx: { starty: number } 58 | }) => { 59 | 'worklet' 60 | ctx.starty = 0 61 | transValue.value = withDecay({ velocity: velocityY, deceleration: 0.997, clamp: [0, Number.MAX_VALUE] }, (finished) => { 62 | isActive.value = false 63 | }) 64 | } 65 | 66 | export const onActiveRefreshImpl = ({ 67 | isRefreshing, 68 | isRefreshingWithAnimation, 69 | transRefreshing, 70 | refreshHeight, 71 | shareAnimatedValue, 72 | isDragging, 73 | onReadyToActive, 74 | }: { 75 | isRefreshingWithAnimation: Animated.SharedValue 76 | isRefreshing: Animated.SharedValue 77 | isDragging: Animated.SharedValue 78 | transRefreshing: Animated.SharedValue 79 | shareAnimatedValue: Animated.SharedValue 80 | refreshHeight: number 81 | onReadyToActive: (isPulling: boolean) => number 82 | }) => { 83 | 'worklet' 84 | return (event: PanGestureHandlerGestureEvent['nativeEvent'], ctx: GesturePanContext) => { 85 | 'worklet'; 86 | 87 | if (isRefreshing.value !== isRefreshingWithAnimation.value) return 88 | if (isRefreshing.value) { 89 | const getStartY = () => { 90 | return onReadyToActive(false) 91 | } 92 | 93 | toRunSlide({ transValue: transRefreshing, translationY: event.translationY, isActive: isDragging, ctx, getStartY }) 94 | } else { 95 | if (shareAnimatedValue.value !== 0 || event.translationY <= 0) return 96 | if (isDragging.value === false) { 97 | ctx.basyY = onReadyToActive(true) 98 | isDragging.value = true 99 | return 100 | } 101 | 102 | transRefreshing.value = refreshHeight - (event.translationY - ctx.basyY) 103 | } 104 | } 105 | } 106 | 107 | export const onEndRefreshImpl = ({ 108 | isRefreshing, 109 | isRefreshingWithAnimation, 110 | transRefreshing, 111 | onReadyRefresh, 112 | onEndRefresh, 113 | isDragging 114 | }: { 115 | isRefreshing: Animated.SharedValue 116 | isRefreshingWithAnimation: Animated.SharedValue, 117 | isDragging: Animated.SharedValue 118 | transRefreshing: Animated.SharedValue 119 | onReadyRefresh: () => void 120 | onEndRefresh: () => void 121 | }) => { 122 | 'worklet' 123 | return (event: PanGestureHandlerGestureEvent['nativeEvent'], ctx: GesturePanContext) => { 124 | 'worklet'; 125 | 126 | if (isDragging.value === false) return 127 | isDragging.value = false 128 | if (isRefreshing.value !== isRefreshingWithAnimation.value) return 129 | if (isRefreshing.value) { 130 | toEndSlide({ 131 | transValue: transRefreshing, 132 | velocityY: -event.velocityY, 133 | isActive: isDragging, 134 | ctx 135 | }) 136 | 137 | } else { 138 | if (transRefreshing.value < 0) { 139 | onReadyRefresh() 140 | } else { 141 | onEndRefresh() 142 | } 143 | } 144 | } 145 | 146 | } 147 | 148 | export const animateToRefresh = ({ 149 | transRefreshing, 150 | isRefreshing, 151 | isRefreshingWithAnimation, 152 | isToRefresh, 153 | destPoi, 154 | onStartRefresh, 155 | }: { 156 | transRefreshing: Animated.SharedValue, 157 | isRefreshing: Animated.SharedValue, 158 | isRefreshingWithAnimation: Animated.SharedValue, 159 | isToRefresh: boolean, 160 | destPoi: number, 161 | onStartRefresh?: () => void 162 | }) => { 163 | 'worklet' 164 | if (isToRefresh === true && isRefreshing.value === true) return 165 | if (isToRefresh === false && isRefreshing.value === false && transRefreshing.value === destPoi) return 166 | isRefreshing.value = isToRefresh 167 | if (isToRefresh && onStartRefresh) { 168 | runOnJS(onStartRefresh)() 169 | } 170 | 171 | if (transRefreshing.value === destPoi) { 172 | isRefreshingWithAnimation.value = isToRefresh 173 | return 174 | } 175 | transRefreshing.value = withTiming(destPoi, undefined, (finished) => { 176 | isRefreshingWithAnimation.value = isToRefresh 177 | }) 178 | } 179 | 180 | //Once the slide is complete, scroll to the folded or unfolded state 181 | export const snapAfterGlideOver = ({ 182 | sceneRef, 183 | shareAnimatedValue, 184 | headerHeight, 185 | frozeTop 186 | }: { 187 | sceneRef: any, 188 | shareAnimatedValue: Animated.SharedValue, 189 | headerHeight: number, 190 | frozeTop: number 191 | }) => { 192 | 'worklet' 193 | const calcH = headerHeight - frozeTop 194 | if (shareAnimatedValue.value > calcH) return 195 | const poi = shareAnimatedValue.value < calcH * 0.5 ? 0 : calcH 196 | if (shareAnimatedValue.value === poi) return; 197 | mScrollTo(sceneRef, 0, poi, true) 198 | } -------------------------------------------------------------------------------- /Example/react-native-scrollable-tab-view-collapsible-header/CollapsibleHeaderTabView.tsx: -------------------------------------------------------------------------------- 1 | import createHeaderTabsComponent from './createHeaderTabsComponent' 2 | import ScrollableTabView from 'react-native-scrollable-tab-view' 3 | export default createHeaderTabsComponent(ScrollableTabView) -------------------------------------------------------------------------------- /Example/react-native-scrollable-tab-view-collapsible-header/README.md: -------------------------------------------------------------------------------- 1 | # react-native-scrollable-tab-view-collapsible-header 2 | 3 | Extend [react-native-scrollable-tab-view](https://github.com/ptomasroos/react-native-scrollable-tab-view) to have shared collapsible headers 4 | 5 | Please check the [base library](https://github.com/zyslife/react-native-head-tab-view) before using this library. 6 | 7 | 8 | ## Demo 9 | 10 | 11 | ![demo_ios.gif](https://github.com/zyslife/react-native-head-tab-view/blob/master/demoGIF/demo_ios.gif) 12 | 13 | ## Example 14 | 15 | ```js 16 | import * as React from 'react'; 17 | import { View, StyleSheet, Dimensions } from 'react-native'; 18 | import { SceneMap } from 'react-native-tab-view'; 19 | import { HScrollView } from 'react-native-head-tab-view' 20 | import { CollapsibleHeaderTabView } from 'react-native-tab-view-collapsible-header' 21 | 22 | const FirstRoute = () => ( 23 | 24 | 25 | 26 | ); 27 | 28 | const SecondRoute = () => ( 29 | 30 | 31 | 32 | ); 33 | 34 | const initialLayout = { width: Dimensions.get('window').width }; 35 | 36 | export default function TabViewExample() { 37 | const [index, setIndex] = React.useState(0); 38 | const [routes] = React.useState([ 39 | { key: 'first', title: 'First' }, 40 | { key: 'second', title: 'Second' }, 41 | ]); 42 | 43 | const renderScene = SceneMap({ 44 | first: FirstRoute, 45 | second: SecondRoute, 46 | }); 47 | 48 | return ( 49 | } 51 | navigationState={{ index, routes }} 52 | renderScene={renderScene} 53 | onIndexChange={setIndex} 54 | initialLayout={initialLayout} 55 | /> 56 | ); 57 | } 58 | 59 | const styles = StyleSheet.create({ 60 | scene: { 61 | flex: 1, 62 | }, 63 | }); 64 | ``` 65 | 66 | More examples:[Example](https://github.com/zyslife/react-native-head-tab-view/blob/master/Example/src) 67 | 68 | 69 | ## Installation 70 | 71 | - The first step is to add the base library and its dependencies 72 | ```sh 73 | 74 | yarn add react-native-head-tab-view react-native-gesture-handler 75 | ``` 76 | - Then add this library 77 | ```sh 78 | yarn add react-native-scrollable-tab-view-collapsible-header 79 | ``` 80 | 81 | 82 | ## Version 83 | 84 | | react-native-head-tab-view | react-native-scrollable-tab-view | react-native-tab-view-collapsible-header | 85 | | :--------------: | :--------------------: | :--------------------: | 86 | | v1 ~ v2 | - | - | 87 | | v3 | v0 | v0 | 88 | | v4-rc.1 | v1 | v1 | 89 | | v4-rc.2 | v2 | v2 | 90 | 91 | 92 | 93 | --- 94 | ## Documentation 95 | 96 |

97 | CollapsibleHeaderTabView 98 | 99 | 100 | - If your tabs component is react-native-scrollable-tab-view 101 | ```js 102 | import { CollapsibleHeaderTabView } from 'react-native-scrollable-tab-view-collapsible-header' 103 | ``` 104 | 105 | - If your tabs component is react-native-tab-view 106 | ```js 107 | import { CollapsibleHeaderTabView } from 'react-native-tab-view-collapsible-header' 108 | ``` 109 | 110 | `CollapsibleHeaderTabView` extends the props for the tabs component by adding the **CollapsibleHeaderProps** 111 | 112 | #### CollapsibleHeaderProps 113 | 114 | ##### `renderScrollHeader` _(React.ComponentType | React.ReactElement | null)_ (require) 115 | 116 | *render the collapsible header* 117 | 118 | ```js 119 | renderScrollHeader={()=>} 120 | ``` 121 | 122 | 123 | ##### `headerHeight` (optional) 124 | 125 | The height of collapsible header. 126 | 127 | 128 | ##### `tabbarHeight` (optional) 129 | 130 | The height of collapsible tabbar 131 | 132 | ##### `frozeTop` 133 | 134 | The height at which the top area of the Tabview is frozen 135 | 136 | 137 | ##### `overflowHeight` 138 | 139 | Sets the upward offset distance of the TabView and TabBar 140 | 141 | ##### `makeScrollTrans` _(scrollValue: Animated.ShareValue) => void_ 142 | Gets the animation value of the shared collapsible header. 143 | ```js 144 | { 146 | this.setState({ scrollValue }) 147 | }} 148 | /> 149 | ``` 150 | 151 | ##### `onStartRefresh` _(() => void)_ 152 | If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. 153 | Make sure to also set the isRefreshing prop correctly. 154 | 155 | ##### `isRefreshing` _(boolean)_ 156 | Whether the TabView is refreshing 157 | 158 | ##### `renderRefreshControl` _(() => React.ReactElement)_ 159 | A custom RefreshControl 160 | 161 | ##### `refreshHeight` _(number)_ 162 | If this height is reached, a refresh event will be triggered (onStartRefresh) 163 | it defaults to 80 164 | 165 | ##### `scrollEnabled` _(boolean)_ 166 | Whether to allow the scene to slide vertically 167 | 168 | --- 169 | 170 | 171 |
172 | 173 | -------------------------------------------------------------------------------- /Example/react-native-scrollable-tab-view-collapsible-header/createHeaderTabsComponent.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React, { useRef } from 'react'; 3 | import { 4 | StyleSheet, 5 | } from 'react-native'; 6 | import { GestureContainer, CollapsibleHeaderProps, GestureContainerRef } from 'react-native-head-tab-view' 7 | import ScrollableTabView, { DefaultTabBar, ScrollableTabViewProperties } from 'react-native-scrollable-tab-view' 8 | 9 | type ZTabViewProps = Omit & CollapsibleHeaderProps 10 | type ForwardTabViewProps = ZTabViewProps & { forwardedRef: React.Ref, Component: typeof ScrollableTabView } 11 | 12 | export default function createHeaderTabsComponent(Component: typeof ScrollableTabView, config?: {}): React.ForwardRefExoticComponent & React.RefAttributes> { 13 | 14 | return React.forwardRef((props: ZTabViewProps, ref) => { 15 | return 16 | }); 17 | 18 | } 19 | 20 | const CollapsibleHeaderTabView: React.FC = (props: ForwardTabViewProps) => { 21 | const mRef = useRef() 22 | const _onChangeTab = (e: any) => { 23 | props.onChangeTab && props.onChangeTab(e) 24 | mRef.current && mRef.current.setCurrentIndex(e.i) 25 | } 26 | 27 | const _renderTabBar = (mProps: any) => { 28 | if (props.renderTabBar) return props.renderTabBar(mProps) 29 | return 30 | } 31 | 32 | const renderTabView = (mProps: { 33 | renderTabBarContainer: any, 34 | }) => { 35 | const { Component } = props 36 | return { 41 | const newProps = { ...tabbarProps } 42 | delete tabbarProps.scrollValue 43 | return mProps.renderTabBarContainer(_renderTabBar(newProps)) 44 | }} 45 | onChangeTab={_onChangeTab} 46 | /> 47 | } 48 | return 53 | } 54 | 55 | 56 | const styles = StyleSheet.create({ 57 | tabbarStyle: { 58 | backgroundColor: '#fff' 59 | } 60 | }) -------------------------------------------------------------------------------- /Example/react-native-scrollable-tab-view-collapsible-header/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { CollapsibleHeaderProps } from 'react-native-head-tab-view' 3 | import { ScrollableTabViewProperties } from 'react-native-scrollable-tab-view' 4 | 5 | export type ZTabViewProps = Omit & CollapsibleHeaderProps 6 | 7 | export class CollapsibleHeaderTabView extends React.Component{ } -------------------------------------------------------------------------------- /Example/react-native-scrollable-tab-view-collapsible-header/index.js: -------------------------------------------------------------------------------- 1 | export { default as createHeaderTabsComponent } from './createHeaderTabsComponent' 2 | export { default as CollapsibleHeaderTabView } from './CollapsibleHeaderTabView' -------------------------------------------------------------------------------- /Example/react-native-scrollable-tab-view-collapsible-header/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-scrollable-tab-view-collapsible-header", 3 | "version": "2.0.1", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "yu zou ", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/zyslife/react-native-scrollable-tab-view-collapsible-header.git" 12 | }, 13 | "homepage": "https://github.com/zyslife/react-native-scrollable-tab-view-collapsible-header#readme", 14 | "license": "ISC", 15 | "keywords": [ 16 | "react-native-component", 17 | "react-component", 18 | "react-native", 19 | "ios", 20 | "android", 21 | "tab", 22 | "swipe", 23 | "scrollable", 24 | "tabview", 25 | "head", 26 | "collapsible-header" 27 | ], 28 | "peerDependencies": { 29 | "react-native-head-tab-view": "^4.0.0" 30 | }, 31 | "description": "Extend react-native-scrollable-tab-view to have shared collapsible headers" 32 | } 33 | -------------------------------------------------------------------------------- /Example/react-native-tab-view-collapsible-header/CollapsibleHeaderTabView.tsx: -------------------------------------------------------------------------------- 1 | import createHeaderTabsComponent from './createHeaderTabsComponent' 2 | import { TabView } from 'react-native-tab-view' 3 | export default createHeaderTabsComponent(TabView) -------------------------------------------------------------------------------- /Example/react-native-tab-view-collapsible-header/README.md: -------------------------------------------------------------------------------- 1 | # react-native-tab-view-collapsible-header 2 | 3 | Extend [react-native-tab-view](https://github.com/satya164/react-native-tab-view) to have shared collapsible headers 4 | 5 | Please check the [base library](https://github.com/zyslife/react-native-head-tab-view) before using this library. 6 | 7 | 8 | ## Demo 9 | 10 | 11 | ![demo_ios.gif](https://github.com/zyslife/react-native-head-tab-view/blob/master/demoGIF/demo_ios.gif) 12 | 13 | ## Example 14 | 15 | ```js 16 | import * as React from 'react'; 17 | import { View, StyleSheet, Dimensions, ScrollView } from 'react-native'; 18 | import { SceneMap } from 'react-native-tab-view'; 19 | import { HPageViewHoc } from 'react-native-head-tab-view' 20 | import { CollapsibleHeaderTabView } from 'react-native-tab-view-collapsible-header' 21 | const HScrollView = HPageViewHoc(ScrollView) 22 | 23 | const FirstRoute = () => ( 24 | 25 | 26 | 27 | ); 28 | 29 | const SecondRoute = () => ( 30 | 31 | 32 | 33 | ); 34 | 35 | const initialLayout = { width: Dimensions.get('window').width }; 36 | 37 | export default function TabViewExample() { 38 | const [index, setIndex] = React.useState(0); 39 | const [routes] = React.useState([ 40 | { key: 'first', title: 'First' }, 41 | { key: 'second', title: 'Second' }, 42 | ]); 43 | 44 | const renderScene = SceneMap({ 45 | first: FirstRoute, 46 | second: SecondRoute, 47 | }); 48 | 49 | return ( 50 | 200} 52 | renderScrollHeader={() => } 53 | navigationState={{ index, routes }} 54 | renderScene={renderScene} 55 | onIndexChange={setIndex} 56 | initialLayout={initialLayout} 57 | /> 58 | ); 59 | } 60 | 61 | const styles = StyleSheet.create({ 62 | scene: { 63 | flex: 1, 64 | }, 65 | }); 66 | ``` 67 | 68 | More examples:[Example](https://github.com/zyslife/react-native-head-tab-view/blob/master/Example/src) 69 | 70 | 71 | ## Installation 72 | 73 | - The first step is to add the base library and its dependencies 74 | ```sh 75 | 76 | yarn add react-native-head-tab-view react-native-gesture-handler 77 | ``` 78 | - Then add this library 79 | ```sh 80 | yarn add react-native-tab-view-collapsible-header 81 | ``` 82 | 83 | 84 | ## Version 85 | 86 | | react-native-head-tab-view | react-native-scrollable-tab-view | react-native-tab-view-collapsible-header | 87 | | :--------------: | :--------------------: | :--------------------: | 88 | | v1 ~ v2 | - | - | 89 | | v3 | v0 | v0 | 90 | | v4-rc.1 | v1 | v1 | 91 | | v4-rc.2 | v2 | v2 | 92 | 93 | 94 | --- 95 | ## Documentation 96 | 97 |
98 | CollapsibleHeaderTabView 99 | 100 | 101 | - If your tabs component is react-native-scrollable-tab-view 102 | ```js 103 | import { CollapsibleHeaderTabView } from 'react-native-scrollable-tab-view-collapsible-header' 104 | ``` 105 | 106 | - If your tabs component is react-native-tab-view 107 | ```js 108 | import { CollapsibleHeaderTabView } from 'react-native-tab-view-collapsible-header' 109 | ``` 110 | 111 | `CollapsibleHeaderTabView` extends the props for the tabs component by adding the **CollapsibleHeaderProps** 112 | 113 | #### CollapsibleHeaderProps 114 | 115 | ##### `renderScrollHeader` _(React.ComponentType | React.ReactElement | null)_ (require) 116 | 117 | *render the collapsible header* 118 | 119 | ```js 120 | renderScrollHeader={()=>} 121 | ``` 122 | 123 | 124 | ##### `headerHeight` (optional) 125 | 126 | The height of collapsible header. 127 | 128 | 129 | ##### `tabbarHeight` (optional) 130 | 131 | The height of collapsible tabbar 132 | 133 | ##### `frozeTop` 134 | 135 | The height at which the top area of the Tabview is frozen 136 | 137 | 138 | ##### `overflowHeight` 139 | 140 | Sets the upward offset distance of the TabView and TabBar 141 | 142 | ##### `makeScrollTrans` _(scrollValue: Animated.ShareValue) => void_ 143 | Gets the animation value of the shared collapsible header. 144 | ```js 145 | { 147 | this.setState({ scrollValue }) 148 | }} 149 | /> 150 | ``` 151 | 152 | ##### `onStartRefresh` _(() => void)_ 153 | If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. 154 | Make sure to also set the isRefreshing prop correctly. 155 | 156 | ##### `isRefreshing` _(boolean)_ 157 | Whether the TabView is refreshing 158 | 159 | ##### `renderRefreshControl` _(() => React.ReactElement)_ 160 | A custom RefreshControl 161 | 162 | ##### `refreshHeight` _(number)_ 163 | If this height is reached, a refresh event will be triggered (onStartRefresh) 164 | it defaults to 80 165 | 166 | ##### `scrollEnabled` _(boolean)_ 167 | Whether to allow the scene to slide vertically 168 | 169 | --- 170 | 171 | 172 |
173 | 174 | -------------------------------------------------------------------------------- /Example/react-native-tab-view-collapsible-header/createHeaderTabsComponent.tsx: -------------------------------------------------------------------------------- 1 | import { TabView, TabViewProps, Route, TabBar } from 'react-native-tab-view'; 2 | import React, { useEffect, useRef } from 'react'; 3 | import { GestureContainer, CollapsibleHeaderProps, GestureContainerRef } from 'react-native-head-tab-view' 4 | 5 | type ZTabViewProps = Partial> & 6 | Pick, 'onIndexChange' | 'navigationState' | 'renderScene'> & CollapsibleHeaderProps 7 | type ForwardTabViewProps = ZTabViewProps & { forwardedRef: React.Ref>, Component: typeof TabView } 8 | 9 | export default function createHeaderTabsComponent(Component: typeof TabView, config?: {}): React.ForwardRefExoticComponent> & React.RefAttributes>> { 10 | 11 | return React.forwardRef((props: ZTabViewProps, ref) => { 12 | return 13 | }); 14 | } 15 | 16 | function CollapsibleHeaderTabView(props: ForwardTabViewProps): any { 17 | const mRef = useRef() 18 | const initialPageRef = useRef(props.navigationState.index) 19 | 20 | useEffect(() => { 21 | mRef.current && mRef.current.setCurrentIndex(props.navigationState.index) 22 | }, [props.navigationState.index]) 23 | 24 | const _renderTabBar = (tabbarProps: any) => { 25 | if (props.renderTabBar) { 26 | return props.renderTabBar(tabbarProps) 27 | } 28 | return 29 | } 30 | 31 | const renderTabView = (e: { renderTabBarContainer: any }) => { 32 | const { Component } = props 33 | return e.renderTabBarContainer(_renderTabBar(tabbarProps))} /> 37 | } 38 | 39 | return 44 | } 45 | -------------------------------------------------------------------------------- /Example/react-native-tab-view-collapsible-header/index.d.ts: -------------------------------------------------------------------------------- 1 | import { TabViewProps, Route } from 'react-native-tab-view'; 2 | import React from 'react'; 3 | 4 | import { CollapsibleHeaderProps } from 'react-native-head-tab-view' 5 | 6 | export type ZTabViewProps = Partial> & 7 | Pick, 'onIndexChange' | 'navigationState' | 'renderScene'> & CollapsibleHeaderProps 8 | 9 | export class CollapsibleHeaderTabView extends React.Component>{ } -------------------------------------------------------------------------------- /Example/react-native-tab-view-collapsible-header/index.js: -------------------------------------------------------------------------------- 1 | export { default as createHeaderTabsComponent } from './createHeaderTabsComponent' 2 | export { default as CollapsibleHeaderTabView } from './CollapsibleHeaderTabView' -------------------------------------------------------------------------------- /Example/react-native-tab-view-collapsible-header/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-tab-view-collapsible-header", 3 | "version": "2.0.1", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "yu zou ", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/zyslife/react-native-tab-view-collapsible-header.git" 12 | }, 13 | "homepage": "https://github.com/zyslife/react-native-tab-view-collapsible-header#readme", 14 | "license": "ISC", 15 | "keywords": [ 16 | "react-native-component", 17 | "react-component", 18 | "react-native", 19 | "ios", 20 | "android", 21 | "tab", 22 | "swipe", 23 | "scrollable", 24 | "tabview", 25 | "head", 26 | "collapsible-header" 27 | ], 28 | "peerDependencies": { 29 | "react-native-head-tab-view": "^4.0.0" 30 | }, 31 | "description": "Extend react-native-tab-view to have shared collapsible headers" 32 | } 33 | -------------------------------------------------------------------------------- /Example/src/Example.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { NavigationContainer } from '@react-navigation/native'; 4 | import { createStackNavigator } from '@react-navigation/stack'; 5 | import getScreenOptions from './config/getScreenOptions'; 6 | import { navigationRef } from './config/NavigationService'; 7 | import MainScreen from './MainScreen' 8 | import ExampleBasic from './ExampleBasic' 9 | import ExampleCustomTabbar from './ExampleCustomTabbar' 10 | import ExampleWithPullRefresh from './ExampleWithPullRefresh' 11 | import ExampleHeaderAnimated from './ExampleHeaderAnimated' 12 | import ExampleCarouselHeader from './ExampleCarouselHeader' 13 | import ExampleWithTabViewPullRefresh from './ExampleWithTabViewPullRefresh' 14 | 15 | const Stack = createStackNavigator(); 16 | 17 | export default class Example extends React.PureComponent { 18 | 19 | render() { 20 | return ( 21 | 23 | 27 | 32 | 37 | 42 | 47 | 52 | 56 | 60 | 61 | 62 | ) 63 | } 64 | } -------------------------------------------------------------------------------- /Example/src/ExampleBasic.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | View, 5 | } from 'react-native'; 6 | import { ScrollableTabViewContainer, TabViewContainer } from './component/TabViewBase' 7 | import { TabViewType } from './types' 8 | import { styles } from './styles' 9 | import { useHomeConfig } from './hook' 10 | 11 | const ExampleBasic: React.FC = (props) => { 12 | const { tabviewType, enableSnap } = useHomeConfig(props) 13 | return ( 14 | 15 | { 16 | tabviewType === TabViewType.default 17 | ? 18 | 19 | : 20 | } 21 | 22 | ) 23 | } 24 | export default ExampleBasic 25 | 26 | -------------------------------------------------------------------------------- /Example/src/ExampleCarouselHeader.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React, { useState } from 'react'; 3 | import { 4 | View, 5 | Dimensions, 6 | TouchableOpacity, 7 | Image, 8 | Text, 9 | Alert 10 | } from 'react-native'; 11 | import { ScrollableTabViewContainer, TabViewContainer } from './component/TabViewBase' 12 | import Carousel from 'react-native-snap-carousel'; 13 | import staticData from './config/staticData' 14 | import { styles } from './styles' 15 | import { TabViewType } from './types' 16 | import { useHomeConfig } from './hook' 17 | 18 | const TIMECOUNT = 1000 19 | const SUB_TITLE = "It's a little long here." 20 | const SUB_TITLE2 = "It's SUB_TITLE2" 21 | const G_WIN_WIDTH = Dimensions.get('window').width; 22 | const G_WIN_HEIGHT = Dimensions.get('window').height; 23 | 24 | const HEAD_HEIGHT = G_WIN_HEIGHT * 0.6 25 | 26 | const ExampleCarouselHeader: React.FC = (props) => { 27 | const [isRefreshing, setIsRefreshing] = useState(false) 28 | const [title, setTitle] = useState('Hello World!') 29 | const [refreshCount, setRefreshCount] = useState(0) 30 | const { tabviewType, enableSnap } = useHomeConfig(props) 31 | 32 | const _renderCarouselItem = ({ item, index }: any) => { 33 | 34 | return ( 35 | 36 | 37 | { Alert.alert(`Click on the item with position ${index}`) }}> 38 | 39 | 40 | 41 | ) 42 | } 43 | 44 | const _renderScrollHeader = () => { 45 | return ( 46 | 47 | 56 | 57 | 58 | {`${title}`} 59 | {refreshCount > 0 ? {`+${refreshCount}`} : null} 60 | 61 | {SUB_TITLE} 62 | {SUB_TITLE2} 63 | 64 | 65 | ) 66 | } 67 | 68 | const onStartRefresh = () => { 69 | setIsRefreshing(true) 70 | setTimeout(() => { 71 | setIsRefreshing(false) 72 | setRefreshCount(preCount => preCount + 1) 73 | }, TIMECOUNT); 74 | } 75 | 76 | const Props = { 77 | renderScrollHeader: _renderScrollHeader, 78 | tabsRefreshEnabled: true, 79 | onStartRefresh: onStartRefresh, 80 | isRefreshing: isRefreshing, 81 | enableSnap 82 | } 83 | return ( 84 | 85 | { 86 | tabviewType === TabViewType.default ? 87 | : 89 | 92 | } 93 | 94 | ) 95 | } 96 | export default ExampleCarouselHeader 97 | 98 | -------------------------------------------------------------------------------- /Example/src/ExampleCustomTabbar.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | View 5 | } from 'react-native'; 6 | import { TabBar } from 'react-native-tab-view' 7 | import { DefaultTabBar } from 'react-native-scrollable-tab-view' 8 | import { ScrollableTabViewContainer, TabViewContainer } from './component/TabViewBase' 9 | import { styles } from './styles' 10 | import { TabViewType } from './types' 11 | import { useHomeConfig } from './hook' 12 | 13 | const ExampleCustomTabbar: React.FC = (props) => { 14 | const { tabviewType, enableSnap } = useHomeConfig(props) 15 | 16 | const _renderScrollableTabBar = (tabbarProps: any) => { 17 | return 21 | } 22 | 23 | const _renderTabBar = (props: any) => { 24 | return 25 | } 26 | 27 | const Props = { 28 | overflowHeight: 20, 29 | tabbarHeight: 60, 30 | enableSnap 31 | } 32 | 33 | return ( 34 | 35 | { 36 | tabviewType === TabViewType.default ? 37 | : 40 | 44 | } 45 | 46 | ) 47 | } 48 | export default ExampleCustomTabbar 49 | -------------------------------------------------------------------------------- /Example/src/ExampleHeaderAnimated.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React, { useState, useMemo } from 'react'; 3 | import { 4 | View, 5 | Dimensions, 6 | Text 7 | } from 'react-native'; 8 | import { RefreshControlProps } from 'react-native-head-tab-view' 9 | import Animated, { useSharedValue, useAnimatedStyle, useDerivedValue, interpolate, Extrapolate } from 'react-native-reanimated' 10 | import { styles } from './styles' 11 | import { CustomRefreshControl } from './component' 12 | import { ScrollableTabViewContainer, TabViewContainer } from './component/TabViewBase' 13 | import { useHomeConfig } from './hook' 14 | 15 | import staticData from './config/staticData' 16 | import { TabViewType } from './types' 17 | 18 | const G_WIN_WIDTH = Dimensions.get('window').width 19 | const G_WIN_HEIGHT = Dimensions.get('window').height 20 | const HEAD_HEIGHT = G_WIN_HEIGHT * 0.5 21 | 22 | const IMG_WH = 100 23 | const MARGIN_H = 15 24 | const MARGIN_V = 20 25 | const FROZE_TOP = IMG_WH 26 | const LINE_HEIGHT = 20 27 | const LINE_COUNT = 3 28 | const moveDistance = HEAD_HEIGHT - FROZE_TOP 29 | const title_h = LINE_HEIGHT 30 | const detail_h = LINE_HEIGHT * LINE_COUNT 31 | const marginTop = (HEAD_HEIGHT - IMG_WH - title_h - MARGIN_V * 2 - detail_h) * 0.5 32 | 33 | const TIMECOUNT = 2000 34 | 35 | 36 | const ExampleHeaderAnimated: React.FC = (props) => { 37 | const { tabviewType, enableSnap } = useHomeConfig(props) 38 | const [scrollTrans, setScrollTrans] = useState(useSharedValue(0)) 39 | const [isRefreshing, setIsRefreshing] = useState(false) 40 | const [headerImage, setHeaderImage] = useState(staticData.DetailImg) 41 | const [detail, setDetail] = useState("It's hard to stay mad when there's so much beauty in the world.") 42 | 43 | const transXValue = useDerivedValue(() => { 44 | const left = (G_WIN_WIDTH - IMG_WH) / 2 45 | return interpolate(scrollTrans.value, 46 | [0, moveDistance], 47 | [0, -left], 48 | Extrapolate.CLAMP) 49 | }) 50 | const transYValue = useDerivedValue(() => { 51 | const moveDistance = HEAD_HEIGHT - FROZE_TOP 52 | const Img_one_move = marginTop + title_h + detail_h + MARGIN_V * 2 53 | return interpolate(scrollTrans.value, 54 | [0, moveDistance], 55 | [0, Img_one_move], 56 | Extrapolate.CLAMP) 57 | }) 58 | const scaleValue = useDerivedValue(() => { 59 | const moveDistance = HEAD_HEIGHT - FROZE_TOP 60 | return interpolate(scrollTrans.value, 61 | [0, moveDistance], 62 | [1, 0.7], 63 | Extrapolate.CLAMP) 64 | }) 65 | 66 | const headerTransStyle = useAnimatedStyle(() => { 67 | return { 68 | transform: [ 69 | { 70 | translateX: transXValue.value 71 | }, 72 | { 73 | translateY: transYValue.value 74 | }, 75 | { 76 | scale: scaleValue.value 77 | } 78 | ] 79 | } 80 | }) 81 | 82 | const titleOpacity = useDerivedValue(() => { 83 | return interpolate(scrollTrans.value, 84 | [0, 10, 20], 85 | [1, 0.8, 0], 86 | Extrapolate.CLAMP) 87 | }) 88 | const titleStyle = useAnimatedStyle(() => { 89 | return { opacity: titleOpacity.value } 90 | }) 91 | 92 | const detailTransX = useDerivedValue(() => { 93 | return interpolate(scrollTrans.value, 94 | [0, moveDistance], 95 | [0, IMG_WH - (MARGIN_H + IMG_WH) * 0.5], 96 | Extrapolate.CLAMP) 97 | }) 98 | const detailTransY = useDerivedValue(() => { 99 | return interpolate(scrollTrans.value, 100 | [0, moveDistance], 101 | [0, marginTop - (IMG_WH - detail_h) * 0.5], 102 | Extrapolate.CLAMP) 103 | }) 104 | const detailStyle = useAnimatedStyle(() => { 105 | return { 106 | transform: [ 107 | { 108 | translateX: detailTransX.value 109 | }, 110 | { 111 | translateY: detailTransY.value 112 | } 113 | ] 114 | } 115 | }) 116 | 117 | const renderScrollHeader = () => { 118 | return 119 | 120 | 121 | Good luck! 122 | 123 | 124 | 125 | {detail} 126 | 127 | 128 | 129 | 130 | } 131 | 132 | const makeScrollTrans = (scrollTrans: Animated.SharedValue) => { 133 | setScrollTrans(scrollTrans) 134 | } 135 | 136 | const onStartRefresh = () => { 137 | setIsRefreshing(true) 138 | setTimeout(() => { 139 | setDetail('Nobody gets to live life backwards. Look ahead, that’s where your future lies.') 140 | setHeaderImage(staticData.HeaderImg) 141 | setIsRefreshing(false) 142 | }, TIMECOUNT); 143 | } 144 | 145 | const renderRefreshControl = (refreshProps: RefreshControlProps) => { 146 | return 147 | } 148 | 149 | const Props = { 150 | renderScrollHeader, 151 | makeScrollTrans, 152 | frozeTop: FROZE_TOP, 153 | onStartRefresh: onStartRefresh, 154 | renderRefreshControl, 155 | isRefreshing, 156 | enableSnap 157 | } 158 | return ( 159 | 160 | { 161 | tabviewType === TabViewType.default ? 162 | : 164 | 167 | } 168 | 169 | ) 170 | } 171 | 172 | export default ExampleHeaderAnimated 173 | 174 | 175 | -------------------------------------------------------------------------------- /Example/src/ExampleWithPullRefresh.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | View, 5 | } from 'react-native'; 6 | import { ScrollableTabViewContainer, TabViewContainer } from './component/TabViewBase' 7 | import { TabViewType } from './types' 8 | import { useHomeConfig } from './hook' 9 | import { styles } from './styles' 10 | 11 | const ExampleWithPullRefresh: React.FC = (props) => { 12 | const { tabviewType, enableSnap } = useHomeConfig(props) 13 | return ( 14 | 15 | { 16 | tabviewType === TabViewType.default ? 17 | : 19 | 21 | } 22 | 23 | ) 24 | } 25 | export default ExampleWithPullRefresh 26 | -------------------------------------------------------------------------------- /Example/src/ExampleWithTabViewPullRefresh.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | View 5 | } from 'react-native'; 6 | import { ScrollableTabViewContainer, TabViewContainer } from './component/TabViewBase' 7 | import { TabViewType } from './types' 8 | import { styles } from './styles' 9 | import { useHomeConfig } from './hook' 10 | 11 | const ExampleWithTabViewPullRefresh: React.FC = (props) => { 12 | const { tabviewType, enableSnap } = useHomeConfig(props) 13 | return ( 14 | 15 | { 16 | tabviewType === TabViewType.default ? 17 | : 19 | 21 | } 22 | 23 | ) 24 | } 25 | export default ExampleWithTabViewPullRefresh 26 | -------------------------------------------------------------------------------- /Example/src/MainScreen.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | Text, 5 | TouchableOpacity, 6 | View, 7 | ScrollView, 8 | StyleSheet, 9 | FlatList 10 | } from 'react-native'; 11 | import { SafeAreaView } from 'react-native-safe-area-context'; 12 | interface Props { 13 | navigation: any; 14 | } 15 | import { TabViewType, EnableSnapType } from './types' 16 | import staticData from './config/staticData' 17 | interface State { 18 | configIndexs: number[] 19 | } 20 | export default class MainScreen extends React.PureComponent { 21 | constructor(props: Props) { 22 | super(props); 23 | this.state = { 24 | configIndexs: [0, 0] 25 | } 26 | } 27 | 28 | _renderItem = ({ item }: { item: { title: string, description: string, page: string } }) => { 29 | 30 | return { 31 | const { configIndexs } = this.state 32 | this.props.navigation.navigate(item.page, { configIndexs }) 33 | }}> 34 | {item.title} 35 | {item.description} 36 | 37 | } 38 | 39 | _keyExtractor = (item: any, index: number) => index + '' 40 | 41 | render() { 42 | 43 | return ( 44 | 46 | {staticData.homeConfig.map((item, index) => { 47 | this.setState((preState) => { 48 | return { 49 | configIndexs: preState.configIndexs.map((el, ii) => { 50 | if (ii === index) { 51 | return mI 52 | } 53 | return el 54 | }) 55 | } 56 | }) 57 | }} />)} 58 | 63 | 64 | ) 65 | } 66 | } 67 | 68 | interface SelectViewProps { 69 | title: string 70 | data: { type: TabViewType | EnableSnapType, title: string }[] 71 | onPress: (index: number) => void 72 | index: number 73 | } 74 | class SelectView extends React.PureComponent { 75 | 76 | render() { 77 | const { title, data, index } = this.props; 78 | 79 | return ( 80 | 81 | {title} 82 | { 83 | data.map((item, mIndex) => { 84 | const isSelect = mIndex === index 85 | 86 | return 87 | { 88 | this.props.onPress && this.props.onPress(mIndex) 89 | }}> 90 | 91 | {item.title} 92 | 93 | {/* {detail ? {detail} : null} */} 94 | 95 | }) 96 | } 97 | 98 | ) 99 | } 100 | } 101 | 102 | const styles = StyleSheet.create({ 103 | selectContainer: { 104 | backgroundColor: '#fff', 105 | paddingHorizontal: 15, 106 | paddingVertical: 10 107 | }, 108 | scrollContainer: { 109 | // height: 40, 110 | backgroundColor: '#fff', 111 | }, 112 | section: { 113 | color: '#333', 114 | fontWeight: 'bold', 115 | fontSize: 15 116 | }, 117 | itemStyle: { 118 | paddingHorizontal: 15, 119 | paddingVertical: 15 120 | }, 121 | titleStyle: { 122 | fontSize: 15, 123 | color: '#333', 124 | fontWeight: 'bold' 125 | }, 126 | detail: { 127 | fontSize: 13, 128 | color: '#888', 129 | marginTop: 5 130 | }, 131 | description: { 132 | fontSize: 11, 133 | color: '#888', 134 | marginTop: 5 135 | }, 136 | selectBtn: { 137 | flexDirection: 'row', 138 | alignItems: 'center', 139 | marginTop: 8, 140 | marginRight: 15 141 | } 142 | }) -------------------------------------------------------------------------------- /Example/src/component/CustomRefreshControl.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect } from 'react'; 2 | import { StyleSheet } from 'react-native' 3 | import Animated, { cancelAnimation, useSharedValue, useAnimatedStyle, useAnimatedReaction, useDerivedValue, withRepeat, withTiming, Easing } from 'react-native-reanimated' 4 | import { RefreshControlProps } from 'react-native-head-tab-view' 5 | import staticData from '../config/staticData' 6 | const config = { 7 | duration: 1000, 8 | easing: Easing.linear, 9 | }; 10 | 11 | const CustomRefreshControl: React.FC = ({ 12 | refreshValue, 13 | refreshType, 14 | progress 15 | }) => { 16 | const rotateValue = useSharedValue('0deg') 17 | 18 | useAnimatedReaction(() => { 19 | return refreshType.value === 'refreshing' 20 | }, (isStart) => { 21 | if (!isStart) return 22 | cancelAnimation(rotateValue) 23 | rotateValue.value = '0deg' 24 | rotateValue.value = withRepeat(withTiming(`${360}deg`, config), -1, false) 25 | }) 26 | 27 | useAnimatedReaction(() => { 28 | return refreshType.value === 'finish' 29 | }, (isStart) => { 30 | if (!isStart) return 31 | cancelAnimation(rotateValue) 32 | 33 | }) 34 | 35 | useAnimatedReaction(() => { 36 | return refreshType.value === 'pullToRefresh' && progress 37 | }, (isStart) => { 38 | if (!isStart) return 39 | rotateValue.value = `${progress.value * 360}deg` 40 | }) 41 | 42 | const transformStyle = useAnimatedStyle(() => { 43 | return { 44 | transform: [ 45 | { rotate: rotateValue.value }, 46 | { scale: 0.3 + progress.value * 0.7 } 47 | ] 48 | } 49 | }) 50 | 51 | return ( 52 | 53 | 54 | 55 | 56 | ) 57 | } 58 | 59 | export default CustomRefreshControl 60 | 61 | const styles = StyleSheet.create({ 62 | circle: { 63 | width: 60, 64 | height: 60, 65 | }, 66 | text: { 67 | color: '#333', 68 | fontSize: 17, 69 | marginTop: 10 70 | } 71 | }) -------------------------------------------------------------------------------- /Example/src/component/FlatListPage.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | Image, 5 | StyleSheet, 6 | FlatList, 7 | View, 8 | Text, 9 | TouchableOpacity 10 | } from 'react-native'; 11 | import { HFlatList } from 'react-native-head-tab-view' 12 | import staticData from '../config/staticData' 13 | 14 | interface Props { 15 | index: number 16 | refreshEnabled?: boolean 17 | timecount?: number 18 | tabLabel?: string 19 | } 20 | 21 | const defaultProps = { 22 | refreshEnabled: false, 23 | timecount: 2000, 24 | } 25 | 26 | interface State { 27 | isRefreshing: boolean 28 | signOfRefresh?: boolean 29 | data: Array 30 | 31 | } 32 | interface FlatListItemInfo { 33 | image: any; 34 | height: number; 35 | text: string; 36 | directory: string; 37 | imgSize: number; 38 | } 39 | 40 | export default class FlatListPage extends React.PureComponent { 41 | static defaultProps = defaultProps 42 | private mTimer?: NodeJS.Timeout 43 | mFlatlist: any; 44 | 45 | constructor(props: any) { 46 | super(props); 47 | this.state = { 48 | isRefreshing: false, 49 | signOfRefresh: true, 50 | data: staticData.Page2Data 51 | } 52 | } 53 | 54 | componentWillUnmount() { 55 | if (this.mTimer) { 56 | clearInterval(this.mTimer) 57 | } 58 | } 59 | 60 | onStartRefresh = () => { 61 | this.setState({ isRefreshing: true }) 62 | 63 | this.mTimer = setTimeout(() => { 64 | this.setState({ isRefreshing: false }) 65 | }, this.props.timecount); 66 | } 67 | 68 | renderItem = (itemInfo: { item: FlatListItemInfo, index: number }) => { 69 | const { item, index } = itemInfo 70 | return ( 71 | 72 | {item.image ? : null} 73 | {`${item.text}${index}`} 74 | 75 | ) 76 | } 77 | 78 | renderFooterComponent = () => { 79 | return ( 80 | { 81 | if (this.mFlatlist) { 82 | this.mFlatlist.scrollToOffset({ offset: 0, animated: true }) 83 | } 84 | }}> 85 | scrollTo Top 86 | 87 | ) 88 | } 89 | keyExtractor = (item: any, index: number) => index.toString() 90 | 91 | _ref = (_ref: any) => this.mFlatlist = _ref 92 | render() { 93 | const props = this.props.refreshEnabled ? { 94 | isRefreshing: this.state.isRefreshing, 95 | onStartRefresh: this.onStartRefresh, 96 | } : {} 97 | const { data } = this.state; 98 | return ( 99 | 109 | ) 110 | } 111 | } 112 | 113 | 114 | const styles = StyleSheet.create({ 115 | container: { 116 | backgroundColor: '#fff' 117 | }, 118 | titleStyle: { 119 | justifyContent: 'center', 120 | alignItems: 'center', 121 | backgroundColor: '#FFF' 122 | }, 123 | sectionTitle: { 124 | color: '#4D4D4D', 125 | fontSize: 15, 126 | }, 127 | imageStyle: { 128 | width: '100%', 129 | height: 200 130 | }, 131 | flatItem: { 132 | paddingLeft: 15, 133 | borderBottomWidth: 1, 134 | borderBottomColor: '#EAEAEA', 135 | flexDirection: 'row', 136 | alignItems: 'center' 137 | }, 138 | 139 | }); -------------------------------------------------------------------------------- /Example/src/component/ScrollViewPage.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | Image, 5 | StyleSheet, 6 | ScrollView, 7 | View, 8 | Text, 9 | RefreshControl, 10 | TouchableOpacity 11 | } from 'react-native'; 12 | import { HScrollView } from 'react-native-head-tab-view' 13 | import staticData from '../config/staticData' 14 | 15 | interface Props { 16 | index: number 17 | refreshEnabled?: boolean 18 | timecount?: number 19 | tabLabel?: string 20 | onPressItem?: () => void 21 | } 22 | 23 | const defaultProps = { 24 | refreshEnabled: false, 25 | timecount: 2000, 26 | } 27 | 28 | interface State { 29 | isRefreshing: boolean 30 | data?: Array 31 | } 32 | 33 | export default class ScrollViewPage extends React.PureComponent { 34 | static defaultProps = defaultProps 35 | private mTimer?: NodeJS.Timeout 36 | 37 | constructor(props: any) { 38 | super(props) 39 | this.state = { 40 | isRefreshing: false, 41 | } 42 | } 43 | private onStartRefresh = () => { 44 | this.setState({ isRefreshing: true }) 45 | this.mTimer = setTimeout(() => { 46 | this.setState({ isRefreshing: false }) 47 | }, this.props.timecount); 48 | } 49 | 50 | 51 | 52 | componentWillUnmount() { 53 | if (this.mTimer) { 54 | clearInterval(this.mTimer) 55 | } 56 | } 57 | 58 | render() { 59 | const props = this.props.refreshEnabled ? { 60 | isRefreshing: this.state.isRefreshing, 61 | onStartRefresh: this.onStartRefresh, 62 | } : {} 63 | return ( 64 | 68 | { 69 | staticData.Page1Data.map((item, index) => { 70 | return ( 71 | 72 | 73 | {item.title} 74 | 75 | 76 | 77 | ) 78 | }) 79 | } 80 | 81 | ) 82 | } 83 | } 84 | 85 | 86 | const styles = StyleSheet.create({ 87 | titleStyle: { 88 | height: 40, 89 | width: '100%', 90 | justifyContent: 'center', 91 | alignItems: 'center', 92 | backgroundColor: '#fff' 93 | }, 94 | sectionTitle: { 95 | color: '#4D4D4D', 96 | fontSize: 15, 97 | }, 98 | imageStyle: { 99 | width: '100%', 100 | height: 200 101 | } 102 | 103 | }); -------------------------------------------------------------------------------- /Example/src/component/SectionListPage.tsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | Image, 5 | StyleSheet, 6 | SectionList, 7 | View, 8 | Text, 9 | } from 'react-native'; 10 | import { HSectionList } from 'react-native-head-tab-view' 11 | import staticData from '../config/staticData' 12 | 13 | interface Props { 14 | index: number 15 | refreshEnabled?: boolean 16 | timecount?: number 17 | tabLabel?: string 18 | } 19 | 20 | const defaultProps = { 21 | refreshEnabled: false, 22 | timecount: 2000, 23 | } 24 | 25 | interface State { 26 | isRefreshing: boolean 27 | data?: Array 28 | } 29 | 30 | export default class SectionListPage extends React.PureComponent { 31 | static defaultProps = defaultProps 32 | private mTimer?: NodeJS.Timeout 33 | 34 | constructor(props: any) { 35 | super(props) 36 | this.state = { 37 | isRefreshing: false 38 | } 39 | } 40 | 41 | componentWillUnmount() { 42 | if (this.mTimer) { 43 | clearInterval(this.mTimer) 44 | } 45 | } 46 | 47 | private renderItem = (itemInfo: { item: string }) => { 48 | const { item } = itemInfo; 49 | 50 | return ( 51 | 52 | {item} 53 | 54 | ) 55 | } 56 | 57 | private renderSectionHeader = (sectionInfo: { section: any }) => { 58 | const { section } = sectionInfo; 59 | const { title } = section; 60 | return ( 61 | 62 | {title} 63 | 64 | ) 65 | } 66 | 67 | private getItemLayout = (data: any, index: number) => { 68 | return { length: 50, offset: index * 50, index }; 69 | } 70 | 71 | private onStartRefresh = () => { 72 | this.setState({ isRefreshing: true }) 73 | this.mTimer = setTimeout(() => { 74 | this.setState({ isRefreshing: false }) 75 | }, this.props.timecount); 76 | } 77 | 78 | 79 | keyExtractor = (item: any, index: number) => index.toString() 80 | 81 | render() { 82 | const props = this.props.refreshEnabled ? { 83 | isRefreshing: this.state.isRefreshing, 84 | onStartRefresh: this.onStartRefresh, 85 | } : {} 86 | 87 | return ( 88 | 98 | ) 99 | } 100 | } 101 | 102 | 103 | const styles = StyleSheet.create({ 104 | titleStyle: { 105 | color: '#333', 106 | fontSize: 14 107 | }, 108 | sectionTitle: { 109 | color: '#4D4D4D', 110 | fontSize: 15, 111 | }, 112 | sectionItem: { 113 | height: 50, 114 | justifyContent: 'center', 115 | paddingLeft: 15, 116 | borderBottomWidth: 1, 117 | borderBottomColor: '#EAEAEA', 118 | }, 119 | 120 | }); -------------------------------------------------------------------------------- /Example/src/component/TabViewBase.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import { StyleSheet, Dimensions, ImageBackground } from 'react-native' 3 | import staticData from '../config/staticData' 4 | import { CollapsibleHeaderTabView as ZHeaderTabView, ZTabViewProps } from 'react-native-tab-view-collapsible-header' 5 | import { CollapsibleHeaderTabView, ZTabViewProps as TabViewProps } from 'react-native-scrollable-tab-view-collapsible-header' 6 | import { ScrollViewPage, FlatListPage, SectionListPage } from './index' 7 | import { styles } from '../styles' 8 | const G_WIN_WIDTH = Dimensions.get('window').width 9 | const TIMECOUNT = 3000 10 | const HEAD_HEIGHT = 180 11 | 12 | interface ScrollableTabViewContainerProps { 13 | renderScrollHeader?: () => React.ComponentType | React.ReactElement | null; 14 | sceneRefreshEnabled?: boolean 15 | tabsRefreshEnabled?: boolean 16 | } 17 | 18 | const ScrollableTabViewContainer: React.FC> = (props) => { 19 | const [isRefreshing, setIsRefreshing] = useState(false) 20 | const mTimer = useRef(null) 21 | const onStartRefresh = () => { 22 | 23 | setIsRefreshing(true) 24 | mTimer.current = setTimeout(() => { 25 | setIsRefreshing(false) 26 | }, TIMECOUNT); 27 | } 28 | 29 | const _renderScrollHeader = () => { 30 | return ( 31 | 32 | ) 33 | } 34 | 35 | useEffect(() => { 36 | return mTimer.current ? clearTimeout(mTimer.current) : () => { } 37 | }, []) 38 | 39 | return 45 | 46 | 47 | 48 | 49 | } 50 | 51 | 52 | const TabViewContainer: React.FC>> = (props) => { 53 | const [index, setIndex] = useState(0) 54 | const [isRefreshing, setIsRefreshing] = useState(false) 55 | const mTimer = useRef(null) 56 | const [routes, setRoutes] = useState([ 57 | { key: 'ScrollView', title: 'ScrollView' }, 58 | { key: 'FlatList', title: 'FlatList' }, 59 | { key: 'SectionList', title: 'SectionList' }, 60 | ]) 61 | 62 | const onStartRefresh = () => { 63 | setIsRefreshing(true) 64 | mTimer.current = setTimeout(() => { 65 | setIsRefreshing(false) 66 | }, TIMECOUNT); 67 | } 68 | 69 | useEffect(() => { 70 | return mTimer.current ? clearTimeout(mTimer.current) : () => { } 71 | }, []) 72 | 73 | 74 | const _renderScene = (e: any) => { 75 | const { route } = e 76 | 77 | if (route.key == 'ScrollView') { 78 | return 79 | } else if (route.key == 'FlatList') { 80 | return 81 | } else if (route.key == 'SectionList') { 82 | return 83 | } 84 | return null; 85 | } 86 | 87 | const _renderScrollHeader = () => { 88 | return ( 89 | 90 | ) 91 | } 92 | 93 | return 104 | 105 | } 106 | 107 | 108 | export { ScrollableTabViewContainer, TabViewContainer } 109 | -------------------------------------------------------------------------------- /Example/src/component/TestScrollView.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | View, 4 | } from 'react-native'; 5 | 6 | export default class TestScrollView extends React.PureComponent { 7 | private data = [0,1,2,3,4,5,6,7,8,9,10] 8 | render() { 9 | return ( 10 | 11 | {this.data.map((item, index) =>{ 12 | return ( 13 | 14 | ) 15 | })} 16 | 17 | ) 18 | } 19 | } -------------------------------------------------------------------------------- /Example/src/component/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ScrollViewPage } from './ScrollViewPage' 2 | export { default as FlatListPage } from './FlatListPage' 3 | export { default as SectionListPage } from './SectionListPage' 4 | export { default as CustomRefreshControl } from './CustomRefreshControl' -------------------------------------------------------------------------------- /Example/src/config/NavigationService.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const navigationRef = React.createRef(); 4 | 5 | const navigate = (name, params) => { 6 | navigationRef.current && navigationRef.current.navigate(name, params); 7 | }; 8 | 9 | const getNavigation = () => { 10 | return navigationRef.current && navigationRef.current; 11 | }; 12 | 13 | export default { 14 | navigate, 15 | getNavigation, 16 | }; 17 | -------------------------------------------------------------------------------- /Example/src/config/getScreenOptions.js: -------------------------------------------------------------------------------- 1 | import {TransitionPresets} from '@react-navigation/stack'; 2 | 3 | const getScreenOptions = () => { 4 | return { 5 | headerStyle: { 6 | backgroundColor: '#ffffff', 7 | }, 8 | headerTintColor: '#000000', 9 | headerTitleStyle: { 10 | fontWeight: 'bold', 11 | }, 12 | headerBackTitleVisible: false, 13 | cardStyle: { 14 | flex: 1, 15 | backgroundColor: '#f5f5f9', 16 | }, 17 | ...TransitionPresets.SlideFromRightIOS, 18 | }; 19 | }; 20 | 21 | export default getScreenOptions; 22 | -------------------------------------------------------------------------------- /Example/src/config/staticData.ts: -------------------------------------------------------------------------------- 1 | import { TabViewType, EnableSnapType } from '../types' 2 | 3 | export default { 4 | PageRouteData: [ 5 | { title: 'ExampleBasic.', description: 'A simple example', page: 'ExampleBasic' }, 6 | { title: 'ExampleCustomTabbar.', description: 'The example contains the usage of a custom tabbar', page: 'ExampleCustomTabbar' }, 7 | { title: 'ExampleWithPullRefresh.', description: 'Make each Tab page allow pull-down refreshes', page: 'ExampleWithPullRefresh' }, 8 | { title: 'ExampleWithTabViewPullRefresh.', description: 'Let the tabs component allow a drop-down refresh', page: 'ExampleWithTabViewPullRefresh' }, 9 | { title: 'ExampleHeaderAnimated.', description: 'Get scrollTrans using the makeScrollTrans function', page: 'ExampleHeaderAnimated' }, 10 | { title: 'ExampleCarouselHeader.', description: "The header contains the Carousel component", page: 'ExampleCarouselHeader' }, 11 | ], 12 | homeConfig: [ 13 | { 14 | sectionTitle: 'Select your tab view', data: [ 15 | { type: TabViewType.tabview, title: 'react-native-tab-view' }, 16 | { type: TabViewType.default, title: 'react-native-scrollable-tab-view' }, 17 | ] 18 | }, 19 | { 20 | sectionTitle: 'Select the value of enableSnap in props', data: [ 21 | { type: EnableSnapType.disableSnap, title: 'false' }, 22 | { type: EnableSnapType.enableSnap, title: 'true' }, 23 | ] 24 | } 25 | ], 26 | 27 | refreshImg: require('../resource/refresh.png'), 28 | HeaderImg: require('../resource/header_img.png'), 29 | IconImg: require('../resource/img13.jpeg'), 30 | IconImg2: require('../resource/img11.jpeg'), 31 | DetailImg: require('../resource/img15.jpeg'), 32 | DetailImg2: require('../resource/img10.jpeg'), 33 | 34 | BannerData: [ 35 | require('../resource/img5.jpeg'), 36 | require('../resource/img4.jpeg'), 37 | require('../resource/img7.jpeg'), 38 | require('../resource/img8.jpeg') 39 | ], 40 | 41 | Page1Data: [ 42 | { image: require('../resource/img1.jpeg'), title: '深秋的黄昏' }, 43 | { image: require('../resource/img2.jpeg'), title: '神秘的庄园' }, 44 | { image: require('../resource/img3.jpeg'), title: '美丽的湖泊' }, 45 | { image: require('../resource/img4.jpeg'), title: '童话的世界' }, 46 | { image: require('../resource/img5.jpeg'), title: '享受的古镇' }, 47 | { image: require('../resource/img6.jpeg'), title: '夜晚的江景' }, 48 | { image: require('../resource/img7.jpeg'), title: '壮丽的晚霞' }, 49 | { image: require('../resource/img8.jpeg'), title: '昏暗的酒馆' }, 50 | { image: require('../resource/img9.jpg'), title: '浪漫的长道' }, 51 | { image: require('../resource/img1.jpeg'), title: '深秋的黄昏' }, 52 | { image: require('../resource/img2.jpeg'), title: '神秘的庄园' }, 53 | { image: require('../resource/img3.jpeg'), title: '美丽的湖泊' }, 54 | { image: require('../resource/img4.jpeg'), title: '童话的世界' }, 55 | { image: require('../resource/img5.jpeg'), title: '享受的古镇' }, 56 | { image: require('../resource/img6.jpeg'), title: '夜晚的江景' } 57 | ], 58 | Page2Data: [ 59 | { image: require('../resource/img11.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 60 | { image: require('../resource/img12.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 61 | { image: require('../resource/img13.jpeg'), height: 100, text: '这里是测试文字', directory: 'right', imgSize: 80 }, 62 | { image: require('../resource/img11.jpeg'), height: 100, text: '这里是测试文字', directory: 'right', imgSize: 80 }, 63 | { image: require('../resource/img12.jpeg'), height: 100, text: '这里是测试文字', directory: 'right', imgSize: 80 }, 64 | { image: require('../resource/img14.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 65 | { image: require('../resource/img11.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 66 | { image: require('../resource/img12.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 67 | { image: require('../resource/img15.jpeg'), height: 100, text: '这里是测试文字', directory: 'right', imgSize: 80 }, 68 | { image: require('../resource/img11.jpeg'), height: 100, text: '这里是测试文字', directory: 'right', imgSize: 80 }, 69 | { image: require('../resource/img12.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 70 | { image: require('../resource/img13.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 71 | { image: require('../resource/img11.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 72 | { image: require('../resource/img12.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 73 | { image: require('../resource/img14.jpeg'), height: 100, text: '这里是测试文字', directory: 'left', imgSize: 80 }, 74 | { image: require('../resource/img11.jpeg'), height: 100, text: '这里是测试文字', directory: 'right', imgSize: 80 }, 75 | { image: require('../resource/img12.jpeg'), height: 100, text: '这里是测试文字', directory: 'right', imgSize: 80 }, 76 | { image: require('../resource/img15.jpeg'), height: 100, text: '这里是测试文字', directory: 'right', imgSize: 80 }, 77 | ], 78 | 79 | Page3Data: [ 80 | { title: "测试标题1", data: ["测试文本测试文本测试文本1", "测试文本测试文本测试文本2", "测试文本测试文本测试文本3", "测试文本测试文本测试文本4", "测试文本测试文本测试文本5", "测试文本测试文本测试文本6"] }, 81 | { title: "测试标题2", data: ["测试文本测试文本测试文本7", "测试文本测试文本测试文本8", "测试文本测试文本测试文本9", "测试文本测试文本测试文本10", "测试文本测试文本测试文本11", "测试文本测试文本测试文本12", "测试文本测试文本测试文本13", "测试文本测试文本测试文本14", "测试文本测试文本测试文本15", "测试文本测试文本测试文本16"] }, 82 | { title: "测试标题3", data: ["测试文本测试文本测试文本17", "测试文本测试文本测试文本18", "测试文本测试文本测试文本19", "测试文本测试文本测试文本20", "测试文本测试文本测试文本21", "测试文本测试文本测试文本22"] }, 83 | { title: "测试标题1", data: ["测试文本测试文本测试文本1", "测试文本测试文本测试文本2", "测试文本测试文本测试文本3", "测试文本测试文本测试文本4", "测试文本测试文本测试文本5", "测试文本测试文本测试文本6"] }, 84 | { title: "测试标题2", data: ["测试文本测试文本测试文本7", "测试文本测试文本测试文本8", "测试文本测试文本测试文本9", "测试文本测试文本测试文本10", "测试文本测试文本测试文本11", "测试文本测试文本测试文本12", "测试文本测试文本测试文本13", "测试文本测试文本测试文本14", "测试文本测试文本测试文本15", "测试文本测试文本测试文本16"] }, 85 | { title: "测试标题3", data: ["测试文本测试文本测试文本17", "测试文本测试文本测试文本18", "测试文本测试文本测试文本19", "测试文本测试文本测试文本20", "测试文本测试文本测试文本21", "测试文本测试文本测试文本22"] }, 86 | { title: "测试标题1", data: ["测试文本测试文本测试文本1", "测试文本测试文本测试文本2", "测试文本测试文本测试文本3", "测试文本测试文本测试文本4", "测试文本测试文本测试文本5", "测试文本测试文本测试文本6"] }, 87 | { title: "测试标题2", data: ["测试文本测试文本测试文本7", "测试文本测试文本测试文本8", "测试文本测试文本测试文本9", "测试文本测试文本测试文本10", "测试文本测试文本测试文本11", "测试文本测试文本测试文本12", "测试文本测试文本测试文本13", "测试文本测试文本测试文本14", "测试文本测试文本测试文本15", "测试文本测试文本测试文本16"] }, 88 | { title: "测试标题3", data: ["测试文本测试文本测试文本17", "测试文本测试文本测试文本18", "测试文本测试文本测试文本19", "测试文本测试文本测试文本20", "测试文本测试文本测试文本21", "测试文本测试文本测试文本22"] }, 89 | { title: "测试标题1", data: ["测试文本测试文本测试文本1", "测试文本测试文本测试文本2", "测试文本测试文本测试文本3", "测试文本测试文本测试文本4", "测试文本测试文本测试文本5", "测试文本测试文本测试文本6"] }, 90 | { title: "测试标题2", data: ["测试文本测试文本测试文本7", "测试文本测试文本测试文本8", "测试文本测试文本测试文本9", "测试文本测试文本测试文本10", "测试文本测试文本测试文本11", "测试文本测试文本测试文本12", "测试文本测试文本测试文本13", "测试文本测试文本测试文本14", "测试文本测试文本测试文本15", "测试文本测试文本测试文本16"] }, 91 | { title: "测试标题3", data: ["测试文本测试文本测试文本17", "测试文本测试文本测试文本18", "测试文本测试文本测试文本19", "测试文本测试文本测试文本20", "测试文本测试文本测试文本21", "测试文本测试文本测试文本22"] }, 92 | ], 93 | TabData: [require('../resource/tab1.png'), require('../resource/tab2.png'), require('../resource/tab3.png')] 94 | 95 | } -------------------------------------------------------------------------------- /Example/src/hook.tsx: -------------------------------------------------------------------------------- 1 | import { TabViewType, EnableSnapType } from './types' 2 | import staticData from './config/staticData' 3 | 4 | export const useHomeConfig = (props: any) => { 5 | 6 | const tabviewIndex = props.route.params.configIndexs[0] 7 | const enableSnapIndex = props.route.params.configIndexs[1] 8 | const tabviewType: TabViewType = staticData.homeConfig[0].data[tabviewIndex].type as TabViewType 9 | const enableSnap: EnableSnapType = staticData.homeConfig[1].data[enableSnapIndex].type as EnableSnapType 10 | 11 | return { tabviewType, enableSnap: enableSnap === EnableSnapType.enableSnap ? true : false } 12 | } -------------------------------------------------------------------------------- /Example/src/resource/header_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/header_icon.png -------------------------------------------------------------------------------- /Example/src/resource/header_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/header_img.png -------------------------------------------------------------------------------- /Example/src/resource/img1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img1.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img10.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img11.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img12.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img13.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img13.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img14.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img14.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img15.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img15.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img2.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img3.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img4.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img5.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img6.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img7.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img8.jpeg -------------------------------------------------------------------------------- /Example/src/resource/img9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/img9.jpg -------------------------------------------------------------------------------- /Example/src/resource/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/refresh.png -------------------------------------------------------------------------------- /Example/src/resource/tab1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/tab1.png -------------------------------------------------------------------------------- /Example/src/resource/tab2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/tab2.png -------------------------------------------------------------------------------- /Example/src/resource/tab3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/Example/src/resource/tab3.png -------------------------------------------------------------------------------- /Example/src/styles/index.ts: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | StyleSheet, 4 | Dimensions, 5 | Platform 6 | } from 'react-native'; 7 | const G_WIN_WIDTH = Dimensions.get('window').width; 8 | const G_WIN_HEIGHT = Dimensions.get('window').height; 9 | const HEAD_HEIGHT = G_WIN_HEIGHT * 0.7 10 | 11 | export const styles = StyleSheet.create({ 12 | container: { 13 | flex: 1, 14 | backgroundColor: '#fff' 15 | }, 16 | tabbarStyle: { 17 | height: 60, 18 | borderTopLeftRadius: 14, 19 | borderTopRightRadius: 14, 20 | backgroundColor: '#fff' 21 | }, 22 | tabbarBtn: { 23 | justifyContent: 'center', 24 | alignItems: 'center' 25 | }, 26 | tabbarImage: { 27 | width: 15, 28 | height: 15 29 | }, 30 | tabviewLayout: { 31 | width: G_WIN_WIDTH 32 | }, 33 | headerStyle: { 34 | backgroundColor: '#fff', 35 | width: '100%', 36 | height: HEAD_HEIGHT 37 | }, 38 | titleStyle: { 39 | color: '#333', 40 | fontSize: 15 41 | }, 42 | detailStyle: { 43 | color: '#888', 44 | fontSize: 12 45 | }, 46 | sectionTitle: { 47 | color: '#4D4D4D', 48 | fontSize: 15, 49 | }, 50 | flatItem: { 51 | paddingLeft: 15, 52 | borderBottomWidth: 1, 53 | borderBottomColor: '#EAEAEA', 54 | flexDirection: 'row', 55 | alignItems: 'center' 56 | }, 57 | sectionItem: { 58 | height: 50, 59 | justifyContent: 'center', 60 | paddingLeft: 15, 61 | borderBottomWidth: 1, 62 | borderBottomColor: '#EAEAEA', 63 | }, 64 | cell: { 65 | flex: 1, 66 | justifyContent: 'center', 67 | alignItems: 'center' 68 | }, 69 | slide: { 70 | flex: 1 71 | }, 72 | carouselImage: { 73 | width: '100%', 74 | height: 200 75 | }, 76 | headerTitle: { 77 | fontSize: 18, 78 | ...Platform.select({ 79 | android: { 80 | fontFamily: '', 81 | } 82 | }) 83 | }, 84 | addHeaderTitle: { 85 | color: 'red', 86 | fontSize: 18 87 | }, 88 | subTitle: { 89 | color: '#848484', 90 | fontSize: 15, 91 | marginTop: 20, 92 | paddingHorizontal: 30, 93 | textAlign: 'center', 94 | ...Platform.select({ 95 | android: { 96 | fontFamily: '', 97 | } 98 | }) 99 | } 100 | }); -------------------------------------------------------------------------------- /Example/src/types.ts: -------------------------------------------------------------------------------- 1 | export enum TabViewType { 2 | tabview, 3 | default, 4 | } 5 | 6 | export enum EnableSnapType { 7 | disableSnap, 8 | enableSnap, 9 | } 10 | 11 | -------------------------------------------------------------------------------- /Example/src/wdyr.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import React from 'react'; 3 | 4 | if (process.env.NODE_ENV === 'development') { 5 | const whyDidYouRender = require('@welldone-software/why-did-you-render'); 6 | whyDidYouRender(React, { 7 | trackAllPureComponents: true, 8 | }); 9 | } -------------------------------------------------------------------------------- /Example/tsconfig.json: -------------------------------------------------------------------------------- 1 | // { 2 | // "compilerOptions": { 3 | // /* Basic Options */ 4 | // // "incremental": true, /* Enable incremental compilation */ 5 | // "target": "ES2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | // "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | // // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // // "checkJs": true, /* Report errors in .js files. */ 10 | // // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | // "outDir": "./src", /* Redirect output structure to the directory. */ 16 | // // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // // "composite": true, /* Enable project compilation */ 18 | // // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // // "removeComments": true, /* Do not emit comments to output. */ 20 | // // "noEmit": true, /* Do not emit outputs. */ 21 | // // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | // /* Strict Type-Checking Options */ 26 | // "strict": true, /* Enable all strict type-checking options. */ 27 | // // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | // /* Additional Checks */ 36 | // // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | // /* Module Resolution Options */ 42 | // // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // // "types": [], /* Type declaration files to be included in compilation. */ 48 | // // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | // "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | // /* Source Map Options */ 54 | // // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | // /* Experimental Options */ 60 | // // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | 63 | // /* Advanced Options */ 64 | // "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 65 | // } 66 | // } 67 | 68 | { 69 | "compilerOptions": { 70 | "allowJs": true, 71 | "allowSyntheticDefaultImports": true, 72 | "esModuleInterop": true, 73 | "isolatedModules": true, 74 | "jsx": "react", 75 | "lib": ["es6"], 76 | "moduleResolution": "node", 77 | "noEmit": true, 78 | "strict": true, 79 | "baseUrl": ".", 80 | "target": "esnext" 81 | }, 82 | "exclude": [ 83 | "node_modules", 84 | "babel.config.js", 85 | "metro.config.js", 86 | "jest.config.js" 87 | ] 88 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Head Tab View 2 | 3 | :tada::tada::tada: v4.0.0-rc.13 has been released, I hope you can help me test and collect questions. 4 | In this version, there is a big change. All animations will run on the UI thread, which will make the components much smoother. Unfortunately, the version requiring React Native is greater than 0.62.0. Because we rely on `react-native-reanimated2.0`, that's what it requires. 5 | 6 | Here are some changes and optimizations. 7 | **Disruptive Changes**: 8 | 9 | - Remove `makeHeaderHeight` and change it to `headerHeight` 10 | 11 | > It's not mandatory, but it would be nice if you did 12 | 13 | - Removed `SlideAnimated` mode 14 | 15 | > this mode was used for ScrollView/FlatList scrolling stalling when dragging headers, no longer needed. 16 | > 17 | > - Remove the scene's `refreshHeight` property 18 | > Both the TabView and Scene used to have the refreshHeight property. Now I think they are duplicate, just set refreshHeight on the TabView, its default value is 80 19 | 20 | - The usage of `HPageViewHoc` has changed 21 | 22 | ```tsx 23 | # Past usage: 24 | import { HPageViewHoc } from 'react-native-head-tab-view' 25 | const HScrollView = HPageViewHoc(ScrollView) 26 | const HFlatList = HPageViewHoc(FlatList) 27 | const HSectionList = HPageViewHoc(SectionList) 28 | # Current usage 29 | import { HScrollView,HFlatList,HSectionList } from 'react-native-head-tab-view' 30 | ``` 31 | 32 | 33 | 34 | **The following components are currently supported:** 35 | [react-native-scrollable-tab-view](https://github.com/ptomasroos/react-native-scrollable-tab-view) 36 | [react-native-tab-view](https://github.com/satya164/react-native-tab-view) 37 | 38 | For detailed usage, please refer to [Example](https://github.com/zyslife/react-native-head-tab-view#Example) and [Installation](https://github.com/zyslife/react-native-head-tab-view#Installation). 39 | 40 | ## Features 41 | 42 | #### v4.0.0-rc 43 | 44 | - **Fix for TAB slider stuttering when dragging headers** 45 | - **Optimized pull-down refresh for easier expansion and better performance** 46 | - **When switching tabbars, the scene is no longer re-rendered** 47 | - **`renderScrollHeader` can accept ReactElement.** 48 | - **Add `enableSnap` to props.** (*If it is true,it automatically switches to the folded and expanded states.)* 49 | 50 | #### dependencies: 51 | 52 | >1.react-native-gesture-handler 53 | >2.react-native-reanimated 54 | 55 | 56 | ###### v3.0 57 | 58 | - Support for extension of other Tabs components, support for shared collapsible headers 59 | - The built-in tabs component is discarded 60 | 61 | >#### dependencies: 62 | > 63 | >1.react-native-gesture-handler 64 | 65 | ###### v2.0 66 | 67 | - Add a pull-down refresh for the Tab page(v2.0~) 68 | - Add a pull-down refresh for the Tabview(v2.0.6~) 69 | - Add the new slide mode to Collapsible Headers and Tabview(v2.1.0~) 70 | 71 | ###### v1.0 72 | 73 | - Built-in Scrollable tabs 74 | - All Tab pages share collapsible headers 75 | - Collapsible Headers controls the slide of the Tabview in the vertical direction 76 | - Collapsible Headers can respond to an event 77 | 78 | >#### dependencies: 79 | > 80 | >1.react-native-gesture-handler 81 | >2.@react-native-community/viewpager 82 | 83 | 84 | 85 | 86 | ## Demo 87 | 88 | ![demo_ios.gif](https://github.com/zyslife/react-native-head-tab-view/blob/master/demoGIF/demo_ios.gif) 89 | 90 | ## Example 91 | 92 | If your tabs component is **react-native-scrollable-tab-view** 93 | 94 | ```js 95 | import * as React from 'react'; 96 | import { View } from 'react-native'; 97 | import { HScrollView } from 'react-native-head-tab-view' 98 | import { CollapsibleHeaderTabView } from 'react-native-scrollable-tab-view-collapsible-header' 99 | export default class ExampleBasic extends React.PureComponent { 100 | render() { 101 | return ( 102 | }> 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | ) 111 | } 112 | } 113 | ``` 114 | 115 | If your tabs component is **react-native-tab-view** 116 | 117 | ```js 118 | import * as React from 'react'; 119 | import { View, StyleSheet, Dimensions } from 'react-native'; 120 | import { SceneMap } from 'react-native-tab-view'; 121 | import { HScrollView } from 'react-native-head-tab-view' 122 | import { CollapsibleHeaderTabView } from 'react-native-tab-view-collapsible-header' 123 | 124 | const FirstRoute = () => ( 125 | 126 | 127 | 128 | ); 129 | 130 | const SecondRoute = () => ( 131 | 132 | 133 | 134 | ); 135 | 136 | const initialLayout = { width: Dimensions.get('window').width }; 137 | 138 | export default function TabViewExample() { 139 | const [index, setIndex] = React.useState(0); 140 | const [routes] = React.useState([ 141 | { key: 'first', title: 'First' }, 142 | { key: 'second', title: 'Second' }, 143 | ]); 144 | 145 | const renderScene = SceneMap({ 146 | first: FirstRoute, 147 | second: SecondRoute, 148 | }); 149 | 150 | return ( 151 | } 153 | navigationState={{ index, routes }} 154 | renderScene={renderScene} 155 | onIndexChange={setIndex} 156 | initialLayout={initialLayout} 157 | /> 158 | ); 159 | } 160 | 161 | const styles = StyleSheet.create({ 162 | scene: { 163 | flex: 1, 164 | }, 165 | }); 166 | ``` 167 | 168 | More examples:[Example](https://github.com/zyslife/react-native-head-tab-view/blob/master/Example/src) 169 | 170 | ## Run the example 171 | 172 | ```sh 173 | cd Example 174 | yarn or npm install 175 | 176 | //run Android 177 | react-native run-android 178 | 179 | //run iOS 180 | cd ios 181 | pod install 182 | cd ../ 183 | react-native run-ios 184 | ``` 185 | 186 | ## Installation 187 | 188 | - The first step is to add the base library and its dependencies 189 | 190 | ```sh 191 | yarn add react-native-head-tab-view react-native-gesture-handler react-native-reanimated 192 | or 193 | npm install react-native-head-tab-view react-native-gesture-handler react-native-reanimated --save 194 | ``` 195 | 196 | - The second step is to select the extension library based on the tabs component you are using 197 | 198 | ##### If your tabs component is react-native-scrollable-tab-view 199 | 200 | ``` 201 | yarn add react-native-scrollable-tab-view-collapsible-header 202 | ``` 203 | 204 | ##### If your tabs component is react-native-tab-view 205 | 206 | ``` 207 | yarn add react-native-tab-view-collapsible-header 208 | ``` 209 | 210 | ## Version 211 | 212 | | react-native-head-tab-view | react-native-scrollable-tab-view-collapsible-header | react-native-tab-view-collapsible-header | 213 | | :------------------------: | :-------------------------------------------------: | :--------------------------------------: | 214 | | v1 ~ v2 | - | - | 215 | | v3 | v0 | v0 | 216 | | v4-rc.1 | v1 | v1 | 217 | | v4-rc.2~ | v2 | v2 | 218 | 219 | ## Linking 220 | 221 | 1. react-native-gesture-handler [Refer to the official documentation](https://github.com/software-mansion/react-native-gesture-handler) 222 | 2. react-native-reanimated [Refer to the official documentation](https://github.com/software-mansion/react-native-reanimated) 223 | 224 | 225 | --- 226 | 227 | ## Documentation 228 | 229 |
230 | CollapsibleHeaderTabView 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | - If your tabs component is react-native-scrollable-tab-view 240 | 241 | >You need to add [this dependency library](https://github.com/zyslife/react-native-scrollable-tab-view-collapsible-header) 242 | > 243 | >```js 244 | >import { CollapsibleHeaderTabView } from 'react-native-scrollable-tab-view-collapsible-header' 245 | >``` 246 | 247 | - If your tabs component is react-native-tab-view 248 | 249 | > You need to add [this dependency library](https://github.com/zyslife/react-native-tab-view-collapsible-header) 250 | > 251 | > ```javascript 252 | > import { CollapsibleHeaderTabView } from 'react-native-tab-view-collapsible-header' 253 | > ``` 254 | 255 | `CollapsibleHeaderTabView` extends the props for the tabs component by adding the **CollapsibleHeaderProps** 256 | 257 | #### CollapsibleHeaderProps 258 | 259 | ##### `renderScrollHeader` _(React.ComponentType | React.ReactElement | null)_ (require) 260 | 261 | *render the collapsible header* 262 | 263 | ```js 264 | renderScrollHeader={()=>} 265 | ``` 266 | 267 | 268 | ##### `headerHeight` (optional) 269 | 270 | The height of collapsible header. 271 | 272 | 273 | ##### `tabbarHeight` (optional) 274 | 275 | The height of collapsible tabbar 276 | 277 | ##### `frozeTop` 278 | 279 | The height at which the top area of the Tabview is frozen 280 | 281 | 282 | ##### `overflowHeight` 283 | 284 | Sets the upward offset distance of the TabView and TabBar 285 | 286 | ##### `makeScrollTrans` _(scrollValue: Animated.ShareValue) => void_ 287 | 288 | Gets the animation value of the shared collapsible header. 289 | 290 | ```js 291 | { 293 | this.setState({ scrollValue }) 294 | }} 295 | /> 296 | ``` 297 | 298 | ##### `onStartRefresh` _(() => void)_ 299 | 300 | If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. 301 | Make sure to also set the isRefreshing prop correctly. 302 | 303 | ##### `isRefreshing` _(boolean)_ 304 | 305 | Whether the TabView is refreshing 306 | 307 | ##### `renderRefreshControl` _(() => React.ReactElement)_ 308 | 309 | A custom RefreshControl 310 | 311 | ##### `scrollEnabled` _(boolean)_ 312 | 313 | Whether to allow the scene to slide vertically 314 | 315 | ##### `refreshHeight` _(number)_ 316 | 317 | If this height is reached, a refresh event will be triggered (onStartRefresh) 318 | it defaults to 80 319 | 320 | ##### `overflowPull` _(number)_ 321 | 322 | It's the distance beyond the refreshHeight, the distance to continue the displacement, when the pull is long enough, 323 | it defaults to 50. 324 | 325 | ##### `pullExtendedCoefficient` _(number)_ 326 | 327 | When the maximum drop-down distance is reached(refreshHeight+overflowPull), the refreshControl moves the distance for each pixel the finger moves The recommended number is between 0 and 1. 328 | 329 | --- 330 | 331 | 332 |
333 | 334 | 335 |
336 | HScrollView \ HFlatList \ HSectionList 337 | 338 | 339 | 340 | 341 | 342 | 343 | ##### `index` _(number)_ (require) 344 | 345 | The number of the screen. 346 | If you use **react-native-scrollable-tab-view**, it should correspond to the number of the `children` element in the TabView. 347 | 348 | If you use **react-native-tab-view**, it should correspond to the index of the `navigationState` of the TabView 349 | Please check the [Example](https://github.com/zyslife/react-native-head-tab-view#Example) . 350 | 351 | 352 | ##### `onStartRefresh` _(() => void)_ 353 | 354 | If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. 355 | Make sure to also set the isRefreshing prop correctly. 356 | 357 | ##### `isRefreshing` _(boolean)_ 358 | 359 | Whether the scene is refreshing 360 | 361 | ##### `renderRefreshControl` _(() => React.ComponentType | React.ReactElement | null)_ 362 | 363 | A custom RefreshControl for scene 364 | 365 | ##### `renderLoadingView` _((headerHeight: number) => React.ReactElement)_ 366 | 367 | You can provide a LoadingView when the scene is transparent until the height of the onContentSizeRange callback is less than minHeight. 368 | 369 | ##### `enableSnap` _(boolean)_ 370 | 371 | When it stops sliding, it automatically switches to the folded and expanded states. 372 | 373 | 374 | 375 |
376 | 377 | ## Tips. 378 | 379 | 1. If you are not planning to upgrade to RN0.62 in the near future, you can use the v3 version. 380 | 2. You may encounter problems with `React-Native-Reanimated2.0` when you first start using V4. 381 | 382 | > [Refer to the official documentation](https://github.com/software-mansion/react-native-reanimated). 383 | > I'm sure it won't be difficult for you 384 | 385 | 386 | 387 | *Thank you for your effort.* -------------------------------------------------------------------------------- /demoGIF/demo_ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyslife/react-native-head-tab-view/7fe7a4c8c0dd3044e1f538e695a830b1b9538dfc/demoGIF/demo_ios.gif --------------------------------------------------------------------------------