├── .circleci └── config.yml ├── .editorconfig ├── .eslintrc.js ├── .github └── FUNDING.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── babel.config.js ├── commitlint.config.js ├── docs ├── API.md ├── Migration.md └── Navigation4.md ├── example ├── .buckconfig ├── .eslintrc.js ├── .expo-shared │ └── assets.json ├── .gitignore ├── README.md ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── nl │ │ │ │ └── ijzerenhein │ │ │ │ └── navigationsharedelementexample │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── nl │ │ │ │ └── ijzerenhein │ │ │ │ └── navigationsharedelementexample │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MainApplication.java │ │ │ │ └── generated │ │ │ │ └── BasePackageList.java │ │ │ └── res │ │ │ ├── drawable │ │ │ ├── splashscreen.xml │ │ │ └── splashscreen_image.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 │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle ├── app.json ├── assets │ ├── atrain.jpg │ ├── blacknoir.png │ ├── bloody.jpg │ ├── homelander.jpg │ ├── icon.png │ ├── logo.png │ ├── queenmaeve.jpg │ ├── splash.png │ ├── starlight.jpg │ ├── stormfront.jpg │ ├── theboys.jpg │ └── thedeep.jpeg ├── babel.config.js ├── index.js ├── ios │ ├── NavigationSharedElementExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── NavigationSharedElementExample.xcscheme │ ├── NavigationSharedElementExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── NavigationSharedElementExample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ └── SplashScreen.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── splashscreen.png │ │ ├── Info.plist │ │ ├── SplashScreen.storyboard │ │ ├── Supporting │ │ │ └── Expo.plist │ │ ├── main.m │ │ └── navigationsharedelementexample.entitlements │ ├── Podfile │ └── Podfile.lock ├── metro.config.js ├── package.json ├── src │ ├── App.tsx │ ├── components │ │ ├── Colors.ts │ │ ├── Icon.ts │ │ ├── Segment │ │ │ ├── Segment.tsx │ │ │ ├── SegmentControl.tsx │ │ │ └── index.ts │ │ ├── TabBarIcon.tsx │ │ ├── Test.tsx │ │ ├── Tests.tsx │ │ ├── TouchableScale.tsx │ │ └── index.ts │ ├── data │ │ ├── defaultItem.ts │ │ ├── index.ts │ │ ├── items.ts │ │ └── types.ts │ ├── screens │ │ ├── CardItem.tsx │ │ ├── CardScreen.tsx │ │ ├── DetailComponent.tsx │ │ ├── DetailComponentImageBackground.tsx │ │ ├── DetailPagerScreen.tsx │ │ ├── DetailScreen.tsx │ │ ├── DetailScreenImageBackground.tsx │ │ ├── ListScreen.tsx │ │ ├── MainScreen.tsx │ │ ├── createScreen.tsx │ │ ├── getDetailSharedElements.ts │ │ └── index.ts │ └── tests │ │ ├── BackOnly.tsx │ │ ├── BackOnly.v4.tsx │ │ ├── BottomTabs.tsx │ │ ├── BottomTabs.v4.tsx │ │ ├── BottomTabs2.tsx │ │ ├── BottomTabs2.v4.tsx │ │ ├── CardView.tsx │ │ ├── CardView.v4.tsx │ │ ├── DefaultStack.tsx │ │ ├── DefaultStack.v4.tsx │ │ ├── FadeFromBottomAndroid.tsx │ │ ├── FadeFromBottomAndroid.v4.tsx │ │ ├── ForwardOnly.tsx │ │ ├── ForwardOnly.v4.tsx │ │ ├── ImageBackground.tsx │ │ ├── ImageBackground.v4.tsx │ │ ├── ListView.tsx │ │ ├── ListView.v4.tsx │ │ ├── MaterialTopTabs.tsx │ │ ├── MaterialTopTabs.v4.tsx │ │ ├── ModalIOS13PageSheet.tsx │ │ ├── ModalIOS13PageSheet.v4.tsx │ │ ├── ModalSlideFromBottomIOS.tsx │ │ ├── ModalSlideFromBottomIOS.v4.tsx │ │ ├── ModalStack.tsx │ │ ├── ModalStack.v4.tsx │ │ ├── NestedStack.tsx │ │ ├── NestedStack.v4.tsx │ │ ├── NestedStack2.tsx │ │ ├── NestedStack2.v4.tsx │ │ ├── PushPopSameScreen.tsx │ │ ├── PushPopSameScreen.v4.tsx │ │ ├── RevealFromBottomAndroid.tsx │ │ ├── RevealFromBottomAndroid.v4.tsx │ │ ├── SafeAreaView.tsx │ │ ├── SafeAreaView.v4.tsx │ │ ├── ScaleFromCenterAndroid.tsx │ │ ├── ScaleFromCenterAndroid.v4.tsx │ │ ├── SlideFromRightIOS.tsx │ │ ├── SlideFromRightIOS.v4.tsx │ │ ├── TextInputStack.v4.tsx │ │ ├── WrongIds.tsx │ │ └── WrongIds.v4.tsx ├── tsconfig.json └── yarn.lock ├── package.json ├── rnse-android.gif ├── rnse-ios.gif ├── src ├── SharedElement.tsx ├── SharedElementCompatRouteProxy.ts ├── SharedElementFocusEvents.tsx ├── SharedElementRendererContext.ts ├── SharedElementRendererData.ts ├── SharedElementRendererProxy.ts ├── SharedElementRendererView.tsx ├── SharedElementSceneContext.ts ├── SharedElementSceneData.ts ├── createSharedElementScene.tsx ├── createSharedElementStackNavigator.tsx ├── index.ts ├── types.ts ├── utils.ts ├── utils │ └── EventEmitter.ts └── v4 │ ├── createSharedElementScene.tsx │ ├── createSharedElementStackNavigator.tsx │ ├── index.ts │ └── types.ts ├── tsconfig.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | defaults: &defaults 4 | docker: 5 | - image: circleci/node:10.9.0 6 | working_directory: ~/project 7 | 8 | jobs: 9 | install-dependencies: 10 | <<: *defaults 11 | steps: 12 | - checkout 13 | - attach_workspace: 14 | at: ~/project 15 | - restore_cache: 16 | keys: 17 | - v1-dependencies-{{ checksum "package.json" }} 18 | - v1-dependencies- 19 | - restore_cache: 20 | keys: 21 | - v1-dependencies-example-{{ checksum "example/package.json" }} 22 | - v1-dependencies-example- 23 | - run: | 24 | yarn install --frozen-lockfile 25 | yarn install --frozen-lockfile --cwd example 26 | - save_cache: 27 | key: v1-dependencies-{{ checksum "package.json" }} 28 | paths: node_modules 29 | - save_cache: 30 | key: v1-dependencies-example-{{ checksum "example/package.json" }} 31 | paths: example/node_modules 32 | - persist_to_workspace: 33 | root: . 34 | paths: . 35 | lint: 36 | <<: *defaults 37 | steps: 38 | - attach_workspace: 39 | at: ~/project 40 | - run: | 41 | yarn run lint 42 | unit-tests: 43 | <<: *defaults 44 | steps: 45 | - attach_workspace: 46 | at: ~/project 47 | - run: yarn test --coverage 48 | - store_artifacts: 49 | path: coverage 50 | destination: coverage 51 | build: 52 | <<: *defaults 53 | steps: 54 | - attach_workspace: 55 | at: ~/project 56 | - run: yarn prepare 57 | 58 | workflows: 59 | version: 2 60 | build-and-test: 61 | jobs: 62 | - install-dependencies 63 | - lint: 64 | requires: 65 | - install-dependencies 66 | - unit-tests: 67 | requires: 68 | - install-dependencies 69 | - build: 70 | requires: 71 | - install-dependencies 72 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // @generated by expo-module-scripts 2 | module.exports = require('expo-module-scripts/eslintrc.base.js'); 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: IjzerenHein 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | 12 | 13 | # Xcode 14 | # 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | *.moved-aside 27 | DerivedData 28 | *.hmap 29 | *.ipa 30 | *.xcuserstate 31 | project.xcworkspace 32 | 33 | 34 | # Android/IntelliJ 35 | # 36 | build/ 37 | .idea 38 | .gradle 39 | local.properties 40 | *.iml 41 | 42 | # BUCK 43 | buck-out/ 44 | \.buckd/ 45 | *.keystore 46 | 47 | # 48 | package-lock.json 49 | /lib 50 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hein Rutjes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LOOKING FOR A NEW HOME OR MAINTAINER 2 | 3 | Both [react-native-shared-element](https://github.com/IjzerenHein/react-native-shared-element) and [react-navigation-shared-element](https://github.com/IjzerenHein/react-navigation-shared-element) are looking for a new Home or Maintainer. 4 | It is no longer possible for me to maintain these repositories. Please reach out to [me personally](mailto:hrutjes@gmail.com) if you want to take over maintenance or have ideas or suggestions for a new home. 5 | 6 | > Read the [full statement here](https://github.com/IjzerenHein/react-navigation-shared-element/issues/14#issuecomment-1081601310). 7 | 8 | # React Navigation Shared Element 9 | 10 | React Navigation bindings for [react-native-shared-element](https://github.com/IjzerenHein/react-native-shared-element) 💫 11 | 12 | ![react-navigation-shared-element-gif-iOS](rnse-ios.gif) 13 | ![react-navigation-shared-element-gif-Android](rnse-android.gif) 14 | 15 | ## Documentation 16 | 17 | - [Shared element for the React Navigation 5/6 API](./docs/API.md) 18 | - [Shared element for the React Navigation 4 API](./docs/Navigation4.md) 19 | - [Migration guide](./docs/Migration.md) 20 | 21 | ## Compatibility 22 | 23 | The following versions or react-navigation and the stack navigator are supported. 24 | 25 | | Version | React-Navigation | Comments | 26 | | ----------------------------------------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 27 | | 3.x | 4, 5 & 6 | Import from `react-navigation-shared-element/build/v4` to use it with 4.x. | 28 | | [2.x](https://github.com/IjzerenHein/react-navigation-shared-element/tree/v2) | 3 & 4 | This version is compatible with `react-navigation-stack@2`. | 29 | | [1.x](https://github.com/IjzerenHein/react-navigation-shared-element/tree/v1) | 3 & 4 | This version is compatible with `react-navigation-stack@1`. | 30 | 31 | ## Things to know 32 | 33 | - `react-navigation-shared-element` uses the [JS based Stack Navigator](https://reactnavigation.org/docs/stack-navigator). The [Native Stack Navigator](https://reactnavigation.org/docs/native-stack-navigator) is currently [under development here](https://github.com/IjzerenHein/react-navigation-shared-element/tree/native-stack). Also see [this issue](https://github.com/IjzerenHein/react-navigation-shared-element/issues/14). 34 | - [React Navigation 6 `Group` components](https://reactnavigation.org/docs/group/) are not yet supported. 35 | - On [detaching inactive screens](https://reactnavigation.org/docs/stack-navigator/#detachinactivescreens) a blink may occur on Android. Because of this `detachInactiveScreens` is set to `false` by default on Android. 36 | 37 | ## Demo App 38 | 39 | - [./example](./example) 40 | 41 | ## Videos 42 | 43 | - [Snapchat Shared Transitions - “Can it be done in React Native?” (with react-navigation v5)](https://www.youtube.com/watch?v=NJZfRXs7nZs) 44 | - [Airbnb Shared Transition - “Can it be done in React Native?” (with react-navigation v4)](https://www.youtube.com/watch?v=83GNiMp-qq0) 45 | 46 | ## Sponsors 47 | 48 | This library is made possible by these wonderful people and companies. 49 | 50 | [![](https://github.com/expo.png?size=50)](https://github.com/expo) 51 | [![](https://github.com/react-navigation.png?size=50)](https://github.com/react-navigation) 52 | [![](https://github.com/Open-Source-Collective.png?size=50)](https://github.com/Open-Source-Collective) 53 | [![](https://github.com/haibert.png?size=50)](https://github.com/haibert) 54 | [![](https://github.com/Hirbod.png?size=50)](https://github.com/Hirbod) 55 | [![](https://github.com/github.png?size=50)](https://github.com/gustavo-nramires) 56 | [![](https://github.com/nandorojo.png?size=50)](https://github.com/nandorojo) 57 | [![](https://github.com/beatgig.png?size=50)](https://github.com/beatgig) 58 | [![](https://github.com/nuwave.png?size=50)](https://github.com/nuwave) 59 | [![](https://github.com/calendee.png?size=50)](https://github.com/calendee) 60 | [![](https://github.com/wibb36.png?size=50)](https://github.com/wibb36) 61 | [![](https://github.com/hannojg.png?size=50)](https://github.com/hannojg) 62 | [![](https://github.com/davitykale.png?size=50)](https://github.com/davitykale) 63 | [![](https://github.com/nightstomp.png?size=50)](https://github.com/nightstomp) 64 | [![](https://github.com/SteveGreenley.png?size=50)](https://github.com/SteveGreenley) 65 | 66 | 74 | 75 | ## License 76 | 77 | React navigation shared element library is licensed under [The MIT License](./LICENSE.md). 78 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // @generated by expo-module-scripts 2 | module.exports = require("expo-module-scripts/babel.config.base"); 3 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/Migration.md: -------------------------------------------------------------------------------- 1 | # Migration Guide 2 | 3 | - [2.x -> 3.x](#2x---3x) 4 | - [createSharedElementStackNavigator](#createsharedelementstacknavigator) 5 | - [Route arguments in `sharedElements` function](#route-arguments-in-sharedelements-function) 6 | - [3.0.0 (prerelease) -> 3.x](#300-prerelease---3x) 7 | - [5.0.0-alpha1 -> 3.x](#500-alpha1---3x) 8 | 9 | # 2.x -> 3.x 10 | 11 | ## createSharedElementStackNavigator 12 | 13 | As of version 3.x, `react-navigation-shared-element` supports both the react-navigation 4 and 5/6 APIs. React navigation 5/6 is considered the new default API and 4.x can be accessed using a version specific import. 14 | 15 | **Before** 16 | 17 | ```jsx 18 | // react-navigation-shared-element@1..2 19 | import { createSharedElementStackNavigator, SharedElement } from 'react-navigation-shared-element'; 20 | 21 | const stackNavigator = createSharedElementStackNavigator( 22 | ... 23 | ); 24 | ``` 25 | 26 | **After** 27 | 28 | ```jsx 29 | // react-navigation-shared-element@3 30 | import { createSharedElementStackNavigator, SharedElement } from 'react-navigation-shared-element/build/v4'; 31 | 32 | const stackNavigator = createSharedElementStackNavigator( 33 | ... 34 | ); 35 | ``` 36 | 37 | 38 | ## Route arguments in `sharedElements` function 39 | 40 | The `sharedElements` funtion has been updated to use `route` rather than the `navigation` prop. 41 | 42 | **Before** 43 | 44 | ```jsx 45 | // react-navigation-shared-element@1..2 46 | class DetailScreen extends React.Component { 47 | static sharedElements = (navigation, otherNavigation, showing) => { 48 | if (otherNavigation.state.routeName === 'List') { 49 | const item = navigation.getParam('item'); 50 | return [...]; 51 | } 52 | }; 53 | } 54 | ``` 55 | 56 | **After** 57 | 58 | ```jsx 59 | // react-navigation-shared-element@3 60 | class DetailScreen extends React.Component { 61 | static sharedElements = (route, otherRoute, showing) => { 62 | if (otherRoute.name === 'List') { 63 | const { item } = route.params; 64 | return [...]; 65 | } 66 | }; 67 | } 68 | ``` 69 | 70 | > To help migration, the `route` arguments are wrapped with a special [SharedElementCompatRouteProxy](../src/SharedElementCompatRouteProxy.ts) class which provides backwards compatibility support for `state` and `getParam`. This is a temporary solution and will be removed in the next major release. Is is strongly recommended to upgrade to the new `route` syntax. 71 | 72 | 73 | # 3.0.0 (prerelease) -> 3.x 74 | 75 | If you've been using the early 3.0.0 (prerelease) version with React Navigation 4, then you'll need to change the import and function name. 76 | 77 | **Before** 78 | 79 | ```jsx 80 | import { createSharedElementNavigator4, SharedElement } from 'react-navigation-shared-element'; 81 | ``` 82 | 83 | **After** 84 | 85 | ```jsx 86 | import { createSharedElementNavigator, SharedElement } from 'react-navigation-shared-element/build/v4'; 87 | ``` 88 | 89 | 90 | # 5.0.0-alpha1 -> 3.x 91 | 92 | If you've been using the early 5.0.0-alpha1 version, then you'll need to rename the `sharedElementsConfig` Screen prop to `sharedElements`. That's it! 93 | 94 | **Before** 95 | 96 | ```jsx 97 | // react-navigation-shared-element@5.0.0-alpha1 98 | {...}} 102 | /> 103 | ``` 104 | 105 | **After** 106 | 107 | ```jsx 108 | // react-navigation-shared-element@3 109 | {...}} 113 | /> 114 | ``` 115 | -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ["universe/native"], 4 | env: { browser: true }, 5 | overrides: [ 6 | { 7 | files: ["**/__tests__/*"], 8 | env: { node: true }, 9 | }, 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /example/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /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 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | !debug.keystore 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://docs.fastlane.tools/best-practices/source-control/ 51 | 52 | */fastlane/report.xml 53 | */fastlane/Preview.html 54 | */fastlane/screenshots 55 | 56 | # Bundle artifacts 57 | *.jsbundle 58 | 59 | # CocoaPods 60 | /ios/Pods/ 61 | 62 | # Expo 63 | .expo/* 64 | web-build/ 65 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example & Test app 2 | 3 | ## Usage 4 | 5 | **Open a terminal in the root folder and build the library code** 6 | 7 | ```bash 8 | # Install dependencies 9 | yarn 10 | 11 | # Build the js code 12 | yarn build 13 | ``` 14 | 15 | **Open the example app in another terminal** 16 | 17 | ```bash 18 | # Install dependencies 19 | cd example 20 | yarn 21 | 22 | # Start packager 23 | yarn start 24 | 25 | # Run for ios 26 | cd ios && npx pod install && cd .. 27 | yarn ios 28 | 29 | # Run for android 30 | yarn android 31 | ``` 32 | -------------------------------------------------------------------------------- /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 = "nl.ijzerenhein.navigationsharedelementexample", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "nl.ijzerenhein.navigationsharedelementexample", 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_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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/nl/ijzerenhein/navigationsharedelementexample/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 nl.ijzerenhein.navigationsharedelementexample; 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 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 32 | client.addPlugin(new ReactFlipperPlugin()); 33 | client.addPlugin(new DatabasesFlipperPlugin(context)); 34 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 35 | client.addPlugin(CrashReporterPlugin.getInstance()); 36 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 37 | NetworkingModule.setCustomClientBuilder( 38 | new NetworkingModule.CustomClientBuilder() { 39 | @Override 40 | public void apply(OkHttpClient.Builder builder) { 41 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 42 | } 43 | }); 44 | client.addPlugin(networkFlipperPlugin); 45 | client.start(); 46 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 47 | // Hence we run if after all native modules have been initialized 48 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 49 | if (reactContext == null) { 50 | reactInstanceManager.addReactInstanceEventListener( 51 | new ReactInstanceManager.ReactInstanceEventListener() { 52 | @Override 53 | public void onReactContextInitialized(ReactContext reactContext) { 54 | reactInstanceManager.removeReactInstanceEventListener(this); 55 | reactContext.runOnNativeModulesQueueThread( 56 | new Runnable() { 57 | @Override 58 | public void run() { 59 | client.addPlugin(new FrescoFlipperPlugin()); 60 | } 61 | }); 62 | } 63 | }); 64 | } else { 65 | client.addPlugin(new FrescoFlipperPlugin()); 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 32 | 33 | 34 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/nl/ijzerenhein/navigationsharedelementexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package nl.ijzerenhein.navigationsharedelementexample; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.facebook.react.ReactActivity; 6 | import com.facebook.react.ReactActivityDelegate; 7 | import com.facebook.react.ReactRootView; 8 | import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; 9 | 10 | import expo.modules.splashscreen.singletons.SplashScreen; 11 | import expo.modules.splashscreen.SplashScreenImageResizeMode; 12 | 13 | public class MainActivity extends ReactActivity { 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | // Set the theme to AppTheme BEFORE onCreate to support 17 | // coloring the background, status bar, and navigation bar. 18 | // This is required for expo-splash-screen. 19 | setTheme(R.style.AppTheme); 20 | super.onCreate(null); 21 | // SplashScreen.show(...) has to be called after super.onCreate(...) 22 | // Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually 23 | SplashScreen.show(this, SplashScreenImageResizeMode.CONTAIN, ReactRootView.class, false); 24 | } 25 | 26 | 27 | /** 28 | * Returns the name of the main component registered from JavaScript. 29 | * This is used to schedule rendering of the component. 30 | */ 31 | @Override 32 | protected String getMainComponentName() { 33 | return "main"; 34 | } 35 | 36 | @Override 37 | protected ReactActivityDelegate createReactActivityDelegate() { 38 | return new ReactActivityDelegate(this, getMainComponentName()) { 39 | @Override 40 | protected ReactRootView createRootView() { 41 | return new RNGestureHandlerEnabledRootView(MainActivity.this); 42 | } 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/nl/ijzerenhein/navigationsharedelementexample/MainApplication.java: -------------------------------------------------------------------------------- 1 | package nl.ijzerenhein.navigationsharedelementexample; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.net.Uri; 6 | 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.shell.MainReactPackage; 13 | import com.facebook.soloader.SoLoader; 14 | import nl.ijzerenhein.navigationsharedelementexample.generated.BasePackageList; 15 | 16 | import org.unimodules.adapters.react.ReactAdapterPackage; 17 | import org.unimodules.adapters.react.ModuleRegistryAdapter; 18 | import org.unimodules.adapters.react.ReactModuleRegistryProvider; 19 | import org.unimodules.core.interfaces.Package; 20 | import org.unimodules.core.interfaces.SingletonModule; 21 | import expo.modules.updates.UpdatesController; 22 | 23 | import com.facebook.react.bridge.JSIModulePackage; 24 | import com.swmansion.reanimated.ReanimatedJSIModulePackage; 25 | 26 | import java.lang.reflect.InvocationTargetException; 27 | import java.util.Arrays; 28 | import java.util.List; 29 | import javax.annotation.Nullable; 30 | 31 | public class MainApplication extends Application implements ReactApplication { 32 | private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider( 33 | new BasePackageList().getPackageList() 34 | ); 35 | 36 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 37 | @Override 38 | public boolean getUseDeveloperSupport() { 39 | return BuildConfig.DEBUG; 40 | } 41 | 42 | @Override 43 | protected List getPackages() { 44 | List packages = new PackageList(this).getPackages(); 45 | packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider)); 46 | return packages; 47 | } 48 | 49 | @Override 50 | protected String getJSMainModuleName() { 51 | return "index"; 52 | } 53 | 54 | @Override 55 | protected JSIModulePackage getJSIModulePackage() { 56 | return new ReanimatedJSIModulePackage(); 57 | } 58 | 59 | @Override 60 | protected @Nullable String getJSBundleFile() { 61 | if (BuildConfig.DEBUG) { 62 | return super.getJSBundleFile(); 63 | } else { 64 | return UpdatesController.getInstance().getLaunchAssetFile(); 65 | } 66 | } 67 | 68 | @Override 69 | protected @Nullable String getBundleAssetName() { 70 | if (BuildConfig.DEBUG) { 71 | return super.getBundleAssetName(); 72 | } else { 73 | return UpdatesController.getInstance().getBundleAssetName(); 74 | } 75 | } 76 | }; 77 | 78 | @Override 79 | public ReactNativeHost getReactNativeHost() { 80 | return mReactNativeHost; 81 | } 82 | 83 | @Override 84 | public void onCreate() { 85 | super.onCreate(); 86 | SoLoader.init(this, /* native exopackage */ false); 87 | 88 | if (!BuildConfig.DEBUG) { 89 | UpdatesController.initialize(this); 90 | } 91 | 92 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 93 | } 94 | 95 | /** 96 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 97 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 98 | * 99 | * @param context 100 | * @param reactInstanceManager 101 | */ 102 | private static void initializeFlipper( 103 | Context context, ReactInstanceManager reactInstanceManager) { 104 | if (BuildConfig.DEBUG) { 105 | try { 106 | /* 107 | We use reflection here to pick up the class that initializes Flipper, 108 | since Flipper library is not available in release mode 109 | */ 110 | Class aClass = Class.forName("com.39eject.ReactNativeFlipper"); 111 | aClass 112 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 113 | .invoke(null, context, reactInstanceManager); 114 | } catch (ClassNotFoundException e) { 115 | e.printStackTrace(); 116 | } catch (NoSuchMethodException e) { 117 | e.printStackTrace(); 118 | } catch (IllegalAccessException e) { 119 | e.printStackTrace(); 120 | } catch (InvocationTargetException e) { 121 | e.printStackTrace(); 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/nl/ijzerenhein/navigationsharedelementexample/generated/BasePackageList.java: -------------------------------------------------------------------------------- 1 | package nl.ijzerenhein.navigationsharedelementexample.generated; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import org.unimodules.core.interfaces.Package; 6 | 7 | public class BasePackageList { 8 | public List getPackageList() { 9 | return Arrays.asList( 10 | new expo.modules.application.ApplicationPackage(), 11 | new expo.modules.constants.ConstantsPackage(), 12 | new expo.modules.errorrecovery.ErrorRecoveryPackage(), 13 | new expo.modules.filesystem.FileSystemPackage(), 14 | new expo.modules.font.FontLoaderPackage(), 15 | new expo.modules.imageloader.ImageLoaderPackage(), 16 | new expo.modules.keepawake.KeepAwakePackage(), 17 | new expo.modules.lineargradient.LinearGradientPackage(), 18 | new expo.modules.splashscreen.SplashScreenPackage(), 19 | new expo.modules.updates.UpdatesPackage() 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/splashscreen.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/splashscreen_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/android/app/src/main/res/drawable/splashscreen_image.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #023c69 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | NavigationSharedElementExample 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /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.3" 6 | minSdkVersion = 21 7 | compileSdkVersion = 30 8 | targetSdkVersion = 30 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:4.1.0") 16 | 17 | // NOTE: Do not place your application dependencies here; they belong 18 | // in the individual module build.gradle files 19 | } 20 | } 21 | 22 | allprojects { 23 | repositories { 24 | mavenLocal() 25 | maven { 26 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 27 | url("$rootDir/../node_modules/react-native/android") 28 | } 29 | maven { 30 | // Android JSC is installed from npm 31 | url("$rootDir/../node_modules/jsc-android/dist") 32 | } 33 | 34 | google() 35 | jcenter() 36 | maven { url 'https://www.jitpack.io' } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /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 | 25 | # Automatically convert third-party libraries to use AndroidX 26 | android.enableJetifier=true 27 | 28 | # Version of flipper SDK to use with React Native 29 | FLIPPER_VERSION=0.54.0 30 | 31 | # The hosted JavaScript engine 32 | # Supported values: expo.jsEngine = "hermes" | "jsc" 33 | expo.jsEngine=jsc 34 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/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.8-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/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /example/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'NavigationSharedElementExample' 2 | 3 | apply from: '../node_modules/react-native-unimodules/gradle.groovy' 4 | includeUnimodulesProjects() 5 | 6 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); 7 | applyNativeModulesSettingsGradle(settings) 8 | 9 | include ':app' 10 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "NavigationSharedElementExample", 4 | "slug": "NavigationSharedElementExample", 5 | "platforms": ["ios", "android", "web"], 6 | "version": "1.0.0", 7 | "orientation": "portrait", 8 | "icon": "./assets/icon.png", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "assetBundlePatterns": ["**/*"], 15 | "ios": { 16 | "supportsTablet": true, 17 | "bundleIdentifier": "nl.ijzerenhein.navigationsharedelementexample" 18 | }, 19 | "android": { 20 | "package": "nl.ijzerenhein.navigationsharedelementexample" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/assets/atrain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/atrain.jpg -------------------------------------------------------------------------------- /example/assets/blacknoir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/blacknoir.png -------------------------------------------------------------------------------- /example/assets/bloody.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/bloody.jpg -------------------------------------------------------------------------------- /example/assets/homelander.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/homelander.jpg -------------------------------------------------------------------------------- /example/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/icon.png -------------------------------------------------------------------------------- /example/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/logo.png -------------------------------------------------------------------------------- /example/assets/queenmaeve.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/queenmaeve.jpg -------------------------------------------------------------------------------- /example/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/splash.png -------------------------------------------------------------------------------- /example/assets/starlight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/starlight.jpg -------------------------------------------------------------------------------- /example/assets/stormfront.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/stormfront.jpg -------------------------------------------------------------------------------- /example/assets/theboys.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/theboys.jpg -------------------------------------------------------------------------------- /example/assets/thedeep.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/assets/thedeep.jpeg -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ["babel-preset-expo"], 5 | plugins: ["react-native-reanimated/plugin"], 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import "react-native-gesture-handler"; 2 | import { registerRootComponent } from "expo"; 3 | 4 | import App from "./src/App"; 5 | 6 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App); 7 | // It also ensures that whether you load the app in the Expo client or in a native build, 8 | // the environment is set up appropriately 9 | registerRootComponent(App); 10 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample.xcodeproj/xcshareddata/xcschemes/NavigationSharedElementExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 65 | 66 | 67 | 68 | 70 | 76 | 77 | 78 | 79 | 80 | 90 | 92 | 98 | 99 | 100 | 101 | 107 | 109 | 115 | 116 | 117 | 118 | 120 | 121 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | #import 7 | 8 | @interface AppDelegate : UMAppDelegateWrapper 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | #import 7 | 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | #if defined(FB_SONARKIT_ENABLED) && __has_include() 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | 22 | static void InitializeFlipper(UIApplication *application) { 23 | FlipperClient *client = [FlipperClient sharedClient]; 24 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 25 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 26 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 27 | [client addPlugin:[FlipperKitReactPlugin new]]; 28 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 29 | [client start]; 30 | } 31 | #endif 32 | 33 | @interface AppDelegate () 34 | 35 | @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; 36 | @property (nonatomic, strong) NSDictionary *launchOptions; 37 | 38 | @end 39 | 40 | @implementation AppDelegate 41 | 42 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 43 | { 44 | #if defined(FB_SONARKIT_ENABLED) && __has_include() 45 | InitializeFlipper(application); 46 | #endif 47 | 48 | self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]]; 49 | self.launchOptions = launchOptions; 50 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 51 | #ifdef DEBUG 52 | [self initializeReactNativeApp]; 53 | #else 54 | EXUpdatesAppController *controller = [EXUpdatesAppController sharedInstance]; 55 | controller.delegate = self; 56 | [controller startAndShowLaunchScreen:self.window]; 57 | #endif 58 | 59 | [super application:application didFinishLaunchingWithOptions:launchOptions]; 60 | 61 | return YES; 62 | } 63 | 64 | - (RCTBridge *)initializeReactNativeApp 65 | { 66 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:self.launchOptions]; 67 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil]; 68 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 69 | 70 | UIViewController *rootViewController = [UIViewController new]; 71 | rootViewController.view = rootView; 72 | self.window.rootViewController = rootViewController; 73 | [self.window makeKeyAndVisible]; 74 | 75 | return bridge; 76 | } 77 | 78 | - (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge 79 | { 80 | NSArray> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge]; 81 | // If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here! 82 | return extraModules; 83 | } 84 | 85 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { 86 | #ifdef DEBUG 87 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 88 | #else 89 | return [[EXUpdatesAppController sharedInstance] launchAssetUrl]; 90 | #endif 91 | } 92 | 93 | - (void)appController:(EXUpdatesAppController *)appController didStartWithSuccess:(BOOL)success { 94 | appController.bridge = [self initializeReactNativeApp]; 95 | EXSplashScreenService *splashScreenService = (EXSplashScreenService *)[UMModuleRegistryProvider getSingletonModuleForClass:[EXSplashScreenService class]]; 96 | [splashScreenService showSplashScreenFor:self.window.rootViewController]; 97 | } 98 | 99 | // Linking API 100 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { 101 | return [RCTLinkingManager application:application openURL:url options:options]; 102 | } 103 | 104 | // Universal Links 105 | - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { 106 | return [RCTLinkingManager application:application 107 | continueUserActivity:userActivity 108 | restorationHandler:restorationHandler]; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/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/NavigationSharedElementExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/Images.xcassets/SplashScreen.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "universal", 5 | "filename": "splashscreen.png", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "scale": "2x" 11 | }, 12 | { 13 | "idiom": "universal", 14 | "scale": "3x" 15 | } 16 | ], 17 | "info": { 18 | "version": 1, 19 | "author": "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/Images.xcassets/SplashScreen.imageset/splashscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/example/ios/NavigationSharedElementExample/Images.xcassets/SplashScreen.imageset/splashscreen.png -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | NavigationSharedElementExample 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.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 | NSCalendarsUsageDescription 41 | Allow NavigationSharedElementExample to access your calendar 42 | NSCameraUsageDescription 43 | Allow NavigationSharedElementExample to use the camera 44 | NSContactsUsageDescription 45 | Allow NavigationSharedElementExample experiences to access your contacts 46 | NSLocationAlwaysAndWhenInUseUsageDescription 47 | Allow NavigationSharedElementExample to use your location 48 | NSLocationAlwaysUsageDescription 49 | Allow NavigationSharedElementExample to use your location 50 | NSLocationWhenInUseUsageDescription 51 | Allow NavigationSharedElementExample to use your location 52 | NSMicrophoneUsageDescription 53 | Allow NavigationSharedElementExample to access your microphone 54 | NSMotionUsageDescription 55 | Allow NavigationSharedElementExample to access your device's accelerometer 56 | NSPhotoLibraryAddUsageDescription 57 | Give NavigationSharedElementExample periences permission to save photos 58 | NSPhotoLibraryUsageDescription 59 | Give NavigationSharedElementExample periences permission to access your photos 60 | NSRemindersUsageDescription 61 | Allow NavigationSharedElementExample to access your reminders 62 | UILaunchStoryboardName 63 | LaunchScreen 64 | UIRequiredDeviceCapabilities 65 | 66 | armv7 67 | 68 | UIRequiresFullScreen 69 | 70 | UISupportedInterfaceOrientations 71 | 72 | UIInterfaceOrientationPortrait 73 | UIInterfaceOrientationPortraitUpsideDown 74 | 75 | UIViewControllerBasedStatusBarAppearance 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/SplashScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/Supporting/Expo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EXUpdatesSDKVersion 6 | 42.0.0 7 | EXUpdatesEnabled 8 | 9 | EXUpdatesURL 10 | YOUR-APP-URL-HERE 11 | EXUpdatesCheckOnLaunch 12 | ALWAYS 13 | EXUpdatesLaunchWaitMs 14 | 0 15 | 16 | 17 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/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 | 11 | -------------------------------------------------------------------------------- /example/ios/NavigationSharedElementExample/navigationsharedelementexample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/react-native-unimodules/cocoapods.rb' 3 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 4 | 5 | platform :ios, '11.0' 6 | 7 | target 'NavigationSharedElementExample' do 8 | use_unimodules! 9 | config = use_native_modules! 10 | 11 | use_react_native!(:path => config["reactNativePath"]) 12 | 13 | # Uncomment to opt-in to using Flipper 14 | # 15 | # if !ENV['CI'] 16 | # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') 17 | # post_install do |installer| 18 | # flipper_post_install(installer) 19 | # end 20 | # end 21 | end 22 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | // Learn more https://docs.expo.io/guides/customizing-metro 2 | const { getDefaultConfig } = require("expo/metro-config"); 3 | const path = require("path"); 4 | 5 | const defaultConfig = getDefaultConfig(__dirname); 6 | 7 | const resolvers = { 8 | "react-navigation-shared-element": "..", 9 | "@react-navigation/core": "../node_modules", 10 | "@react-navigation/native": "../node_modules", 11 | "@react-navigation/elements": "../node_modules", 12 | "@react-navigation/stack": "../node_modules", 13 | "@react-navigation/routers": "../node_modules", 14 | "@react-navigation/bottom-tabs": "../node_modules", 15 | "@react-navigation/material-top-tabs": "../node_modules", 16 | // react-navigation@4 17 | "react-navigation": "../node_modules", 18 | "react-navigation-stack": "../node_modules", 19 | "react-navigation-tabs": "../node_modules", 20 | }; 21 | 22 | // Add custom resolver and watch-folders because 23 | // Metro doesn't work well with the link to the library. 24 | defaultConfig.resolver.extraNodeModules = new Proxy( 25 | {}, 26 | { 27 | get: (_, name) => path.resolve(resolvers[name] || "./node_modules", name), 28 | } 29 | ); 30 | defaultConfig.watchFolders.push(path.resolve("./node_modules")); 31 | defaultConfig.watchFolders.push(path.resolve("..")); 32 | 33 | module.exports = defaultConfig; 34 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js", 3 | "scripts": { 4 | "start": "react-native start", 5 | "android": "react-native run-android", 6 | "ios": "react-native run-ios", 7 | "web": "expo start --web", 8 | "lint": "eslint ." 9 | }, 10 | "dependencies": { 11 | "@expo/vector-icons": "^12.0.0", 12 | "@react-native-community/masked-view": "^0.1.11", 13 | "expo": "^42.0.1", 14 | "expo-linear-gradient": "^9.2.0", 15 | "expo-splash-screen": "~0.11.2", 16 | "expo-updates": "~0.8.2", 17 | "react": "16.13.1", 18 | "react-dom": "16.13.1", 19 | "react-native": "0.63.4", 20 | "react-native-gesture-handler": "~1.10.2", 21 | "react-native-pager-view": "^5.4.0", 22 | "react-native-reanimated": "~2.10.0", 23 | "react-native-safe-area-context": "3.2.0", 24 | "react-native-screens": "~3.4.0", 25 | "react-native-shared-element": "0.8.2", 26 | "react-native-touchable-scale": "^2.1.1", 27 | "react-native-unimodules": "~0.14.5", 28 | "react-native-vector-icons": "^6.6.0", 29 | "react-native-web": "~0.13.12", 30 | "react-navigation-shared-element": "link:../" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "~7.9.0", 34 | "@types/react": "~16.9.35", 35 | "@types/react-native": "~0.63.2", 36 | "babel-jest": "~25.2.6", 37 | "babel-preset-expo": "8.3.0", 38 | "eslint": "^7.32.0", 39 | "jest": "~25.2.6", 40 | "metro-react-native-babel-preset": "~0.56.4", 41 | "react-test-renderer": "~16.9.0", 42 | "typescript": "~4.0.0" 43 | }, 44 | "private": true 45 | } 46 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Platform, StatusBar } from "react-native"; 3 | import { SafeAreaProvider } from "react-native-safe-area-context"; 4 | 5 | import { Tests, Test } from "./components"; 6 | import BackOnly from "./tests/BackOnly"; 7 | import BackOnlyV4 from "./tests/BackOnly.v4"; 8 | import BottomTabs from "./tests/BottomTabs"; 9 | import BottomTabsV4 from "./tests/BottomTabs.v4"; 10 | import BottomTabs2 from "./tests/BottomTabs2"; 11 | import BottomTabs2V4 from "./tests/BottomTabs2.v4"; 12 | import CardView from "./tests/CardView"; 13 | import CardViewV4 from "./tests/CardView.v4"; 14 | import DefaultStack from "./tests/DefaultStack"; 15 | import DefaultStackV4 from "./tests/DefaultStack.v4"; 16 | import FadeFromBottomAndroid from "./tests/FadeFromBottomAndroid"; 17 | import FadeFromBottomAndroidV4 from "./tests/FadeFromBottomAndroid.v4"; 18 | import ForwardOnly from "./tests/ForwardOnly"; 19 | import ForwardOnlyV4 from "./tests/ForwardOnly.v4"; 20 | import ImageBackground from "./tests/ImageBackground"; 21 | import ImageBackgroundV4 from "./tests/ImageBackground.v4"; 22 | import ListView from "./tests/ListView"; 23 | import ListViewV4 from "./tests/ListView.v4"; 24 | import MaterialTopTabs from "./tests/MaterialTopTabs"; 25 | import MaterialTopTabsV4 from "./tests/MaterialTopTabs.v4"; 26 | import ModalIOS13PageSheet from "./tests/ModalIOS13PageSheet"; 27 | import ModalIOS13PageSheetV4 from "./tests/ModalIOS13PageSheet.v4"; 28 | import ModalSlideFromBottomIOS from "./tests/ModalSlideFromBottomIOS"; 29 | import ModalSlideFromBottomIOSV4 from "./tests/ModalSlideFromBottomIOS.v4"; 30 | import ModalStack from "./tests/ModalStack"; 31 | import ModalStackV4 from "./tests/ModalStack.v4"; 32 | import NestedStack from "./tests/NestedStack"; 33 | import NestedStackV4 from "./tests/NestedStack.v4"; 34 | import NestedStack2 from "./tests/NestedStack2"; 35 | import NestedStack2V4 from "./tests/NestedStack2.v4"; 36 | import PushPopSameScreen from "./tests/PushPopSameScreen"; 37 | import PushPopSameScreenV4 from "./tests/PushPopSameScreen.v4"; 38 | import RevealFromBottomAndroid from "./tests/RevealFromBottomAndroid"; 39 | import RevealFromBottomAndroidV4 from "./tests/RevealFromBottomAndroid.v4"; 40 | import SafeAreaView from "./tests/SafeAreaView"; 41 | import SafeAreaViewV4 from "./tests/SafeAreaView.v4"; 42 | import ScaleFromCenterAndroid from "./tests/ScaleFromCenterAndroid"; 43 | import ScaleFromCenterAndroidV4 from "./tests/ScaleFromCenterAndroid.v4"; 44 | import SlideFromRightIOS from "./tests/SlideFromRightIOS"; 45 | import SlideFromRightIOSV4 from "./tests/SlideFromRightIOS.v4"; 46 | import TextInputStackV4 from "./tests/TextInputStack.v4"; 47 | import WrongIds from "./tests/WrongIds"; 48 | import WrongIdsV4 from "./tests/WrongIds.v4"; 49 | 50 | if (Platform.OS === "android") { 51 | StatusBar.setTranslucent(true); 52 | StatusBar.setBackgroundColor("transparent"); 53 | } 54 | 55 | export default () => ( 56 | 57 | 58 | 63 | 64 | 65 | 70 | 75 | 80 | 85 | 90 | 91 | 97 | 102 | 107 | 112 | 117 | 123 | 128 | 133 | 138 | 144 | 145 | 151 | 152 | 153 | 154 | ); 155 | -------------------------------------------------------------------------------- /example/src/components/Colors.ts: -------------------------------------------------------------------------------- 1 | export const Colors = { 2 | blue: "#6495ed", 3 | white: "white", 4 | gray: "#AAAAAA", 5 | red: "#FA7354", 6 | }; 7 | -------------------------------------------------------------------------------- /example/src/components/Icon.ts: -------------------------------------------------------------------------------- 1 | import { Ionicons as Icon } from "@expo/vector-icons"; 2 | 3 | export { Icon }; 4 | -------------------------------------------------------------------------------- /example/src/components/Segment/Segment.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet, View, TouchableOpacity, Text } from "react-native"; 3 | 4 | import { Colors } from "../Colors"; 5 | 6 | type Props = { 7 | label: string; 8 | active: boolean; 9 | location: "start" | "middle" | "end"; 10 | onPress?: () => any; 11 | }; 12 | 13 | export const Segment = (props: Props) => { 14 | const { label, active, location, onPress } = props; 15 | return ( 16 | 23 | 24 | 28 | {label} 29 | 30 | 31 | 32 | ); 33 | }; 34 | 35 | Segment.defaultProps = { 36 | location: "middle", 37 | active: false, 38 | badgeVisible: false, 39 | badgeColor: Colors.blue, 40 | }; 41 | 42 | const styles = StyleSheet.create({ 43 | container: { 44 | flex: 1, 45 | flexDirection: "row", 46 | height: 32, 47 | borderColor: Colors.white, 48 | borderWidth: 1, 49 | borderEndWidth: 0, 50 | }, 51 | start: { 52 | borderTopStartRadius: 8, 53 | borderBottomStartRadius: 8, 54 | }, 55 | end: { 56 | borderEndWidth: 1, 57 | borderTopEndRadius: 8, 58 | borderBottomEndRadius: 8, 59 | }, 60 | content: { 61 | flex: 1, 62 | flexDirection: "row", 63 | justifyContent: "center", 64 | alignItems: "center", 65 | }, 66 | text: { 67 | fontSize: 15, 68 | color: Colors.white, 69 | marginHorizontal: 4, 70 | }, 71 | active: { 72 | backgroundColor: Colors.white, 73 | }, 74 | activeText: { 75 | color: Colors.blue, 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /example/src/components/Segment/SegmentControl.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet, View, ViewStyle } from "react-native"; 3 | 4 | import { Segment } from "./Segment"; 5 | 6 | type Props = { 7 | style?: ViewStyle; 8 | selectedIndex: number; 9 | onValueChange: (index: number) => any; 10 | children: 11 | | React.ReactElement> 12 | | React.ReactElement>[]; 13 | }; 14 | 15 | export const SegmentControl = (props: Props) => { 16 | const { style, children, selectedIndex, onValueChange } = props; 17 | const childCount = React.Children.count(children); 18 | return ( 19 | 20 | {React.Children.map(children, (segment, index) => { 21 | return React.cloneElement(segment, { 22 | key: `segment${index}`, 23 | active: selectedIndex === index, 24 | location: !index 25 | ? "start" 26 | : index === childCount - 1 27 | ? "end" 28 | : "middle", 29 | onPress: () => onValueChange(index), 30 | }); 31 | })} 32 | 33 | ); 34 | }; 35 | 36 | const styles = StyleSheet.create({ 37 | container: { 38 | flexDirection: "row", 39 | justifyContent: "center", 40 | alignItems: "center", 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /example/src/components/Segment/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Segment"; 2 | export * from "./SegmentControl"; 3 | -------------------------------------------------------------------------------- /example/src/components/TabBarIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { Icon } from "./Icon"; 4 | 5 | type Props = { 6 | color?: string; 7 | tintColor?: string; // react-navigation@4 8 | }; 9 | 10 | export function TabBarIcon(props: Props) { 11 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /example/src/components/Test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet, Text, TouchableOpacity, View, Alert } from "react-native"; 3 | 4 | import { Colors } from "./Colors"; 5 | import { Icon } from "./Icon"; 6 | 7 | type Issue = "android" | "ios" | "v4" | "v5" | "v6"; 8 | 9 | type Props = { 10 | title: string; 11 | ComponentV4?: React.ComponentType | null; 12 | Component?: React.ComponentType | null; 13 | issue?: boolean | Issue[]; 14 | onPress?: () => any; 15 | v4?: boolean; 16 | }; 17 | 18 | function onPressInvalidTest() { 19 | Alert.alert( 20 | "No test available", 21 | "Please help out by creating a PR for this test-case." 22 | ); 23 | } 24 | 25 | export const Test = (props: Props) => { 26 | const { title, onPress, Component, ComponentV4, v4, issue } = props; 27 | const isValid = (v4 && ComponentV4) || (!v4 && Component); 28 | if (!Component && !ComponentV4) { 29 | return ( 30 | 31 | {title.toUpperCase()} 32 | 33 | ); 34 | } 35 | const hasIssue = 36 | issue === true || 37 | (Array.isArray(issue) && 38 | ((v4 && issue.includes("v4")) || 39 | (!v4 && (issue.includes("v5") || issue.includes("v6"))))); 40 | return ( 41 | 46 | {title} 47 | {hasIssue ? ( 48 | 49 | 50 | {`issue${Array.isArray(issue) ? ": " + issue.join(",") : ""}`} 51 | 52 | 53 | ) : undefined} 54 | 58 | 59 | ); 60 | }; 61 | 62 | const styles = StyleSheet.create({ 63 | container: { 64 | flexDirection: "row", 65 | height: 44, 66 | alignItems: "center", 67 | borderBottomColor: "#CCCCCC", 68 | borderBottomWidth: StyleSheet.hairlineWidth, 69 | }, 70 | text: { 71 | fontSize: 16, 72 | flex: 1, 73 | marginStart: 20, 74 | }, 75 | textInvalid: { 76 | color: Colors.gray, 77 | }, 78 | icon: { 79 | fontSize: 19, 80 | marginStart: 6, 81 | marginEnd: 20, 82 | }, 83 | iconInvalid: { 84 | color: Colors.gray, 85 | }, 86 | sectionHeader: { 87 | marginTop: 20, 88 | paddingBottom: 10, 89 | flexDirection: "row", 90 | borderBottomColor: "#CCCCCC", 91 | borderBottomWidth: StyleSheet.hairlineWidth, 92 | }, 93 | sectionText: { 94 | fontSize: 13, 95 | flex: 1, 96 | marginStart: 20, 97 | opacity: 0.5, 98 | }, 99 | badgeContainer: { 100 | backgroundColor: Colors.red, 101 | paddingHorizontal: 8, 102 | paddingVertical: 4, 103 | borderRadius: 16, 104 | }, 105 | badge: { 106 | fontSize: 12, 107 | fontWeight: "bold", 108 | color: Colors.white, 109 | }, 110 | }); 111 | -------------------------------------------------------------------------------- /example/src/components/Tests.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | View, 4 | StyleSheet, 5 | ScrollView, 6 | Text, 7 | TouchableOpacity, 8 | StatusBar, 9 | } from "react-native"; 10 | import { useSafeAreaInsets } from "react-native-safe-area-context"; 11 | 12 | import { Colors } from "./Colors"; 13 | import { Icon } from "./Icon"; 14 | import { SegmentControl, Segment } from "./Segment"; 15 | 16 | type Props = { 17 | children: any; 18 | }; 19 | 20 | export const Tests = (props: Props) => { 21 | const insets = useSafeAreaInsets(); 22 | const [test, setTest] = React.useState(undefined); 23 | const [v4, setV4] = React.useState(false); 24 | 25 | return ( 26 | 27 | 32 | {test && (v4 ? : )} 33 | {!test && ( 34 | 35 | react-navigation-shared-element 36 | setV4(index === 0)} 40 | > 41 | 42 | 43 | 44 | 45 | )} 46 | {!test && ( 47 | 48 | {React.Children.map(props.children, (test) => 49 | React.cloneElement(test, { 50 | v4, 51 | onPress: (Component: React.ComponentType) => setTest(test), 52 | }) 53 | )} 54 | 55 | )} 56 | 63 | setTest(undefined)} 68 | > 69 | 70 | 71 | 72 | 73 | ); 74 | }; 75 | 76 | const styles = StyleSheet.create({ 77 | container: { 78 | flex: 1, 79 | }, 80 | header: { 81 | borderBottomColor: "gray", 82 | borderBottomWidth: StyleSheet.hairlineWidth, 83 | backgroundColor: Colors.blue, 84 | paddingBottom: 10, 85 | justifyContent: "flex-end", 86 | alignItems: "center", 87 | }, 88 | content: { 89 | flex: 1, 90 | }, 91 | title: { 92 | fontSize: 17, 93 | fontWeight: "bold", 94 | color: Colors.white, 95 | }, 96 | back: { 97 | ...StyleSheet.absoluteFillObject, 98 | paddingLeft: 10, 99 | }, 100 | backContainer: { 101 | height: 32, 102 | width: 32, 103 | backgroundColor: Colors.blue, 104 | borderRadius: 16, 105 | justifyContent: "center", 106 | alignItems: "center", 107 | }, 108 | backIcon: { 109 | color: "white", 110 | fontSize: 20, 111 | }, 112 | segments: { 113 | marginTop: 10, 114 | marginHorizontal: 20, 115 | }, 116 | }); 117 | -------------------------------------------------------------------------------- /example/src/components/TouchableScale.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { TouchableOpacity } from "react-native-gesture-handler"; 3 | // @ts-ignore 4 | import RawTouchableScale from "react-native-touchable-scale"; 5 | 6 | type Props = React.ComponentProps & { 7 | children: any; 8 | }; 9 | 10 | export function TouchableScale(props: Props) { 11 | return ( 12 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /example/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Icon"; 2 | export * from "./Test"; 3 | export * from "./Tests"; 4 | export * from "./Colors"; 5 | export * from "./TabBarIcon"; 6 | export * from "./TouchableScale"; 7 | -------------------------------------------------------------------------------- /example/src/data/defaultItem.ts: -------------------------------------------------------------------------------- 1 | import { Item } from "./types"; 2 | 3 | export const defaultItem: Item = { 4 | id: "theboys", 5 | title: "The Boys", 6 | image: require("../../assets/theboys.jpg"), 7 | }; 8 | -------------------------------------------------------------------------------- /example/src/data/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./defaultItem"; 2 | export * from "./items"; 3 | export * from "./types"; 4 | -------------------------------------------------------------------------------- /example/src/data/items.ts: -------------------------------------------------------------------------------- 1 | import { Item } from "./types"; 2 | 3 | export const items: Item[] = [ 4 | { 5 | id: "homelander", 6 | title: "Homelander", 7 | image: require("../../assets/homelander.jpg"), 8 | description: 9 | "The Homelander (John) is a fictional supervillain in the comic book series The Boys, created by Garth Ennis and Darick Robertson. The leader of a group of hedonistic and reckless superheroes funded by Vought dubbed The Seven, and the archenemy of CIA black ops agent Billy Butcher, the Homelander is depicted as an arrogant narcissist thought by Butcher to be a sadistic psychopath who cares little about the well-being of those he professes to protect.", 10 | }, 11 | { 12 | id: "stormfront", 13 | title: "Stormfront", 14 | image: require("../../assets/stormfront.jpg"), 15 | description: 16 | "A female interpretation of the character appears in the second season of the television series, portrayed by Aya Cash. This version's real name is Klara Risinger. Despite her youthful looks and command of social media, she was born in 1919, Frederick Vought's first successful test subject for Compound V, and supported Nazi Germany.", 17 | }, 18 | { 19 | id: "atrain", 20 | title: "A-Train", 21 | image: require("../../assets/atrain.jpg"), 22 | description: 23 | "A-Train is a superhero with super-speed whose carelessness was responsible for the accidental death of Wee Hughie's girlfriend in the first issue. A-Train was formerly a member of the Teenage Kix, but was promoted to the Seven as a replacement to Mister Marathon. He is the most juvenile and crudest member of the Seven, being the one who most openly enjoys humiliating Starlight.", 24 | }, 25 | { 26 | id: "blacknoir", 27 | title: "Black Noir", 28 | image: require("../../assets/blacknoir.png"), 29 | description: 30 | 'Black Noir is a long-time member of the Seven, almost always shown in silhouette with his face obscured. His powers include super strength and supposed skills as a pilot. He is stronger than even the Homelander; Mother\'s Milk states he can "[...] bench a dozen Mack trucks". While initially an enigma, it is revealed at the climax of the series that Black Noir is actually a clone of the Homelander, developed by Vought-American as a contingency, in case the leader of the Seven became a liability.', 31 | }, 32 | { 33 | id: "queenmaeve", 34 | title: "Queen Maeve", 35 | image: require("../../assets/queenmaeve.jpg"), 36 | description: 37 | "Queen Maeve is a long-time member of the Seven; her powers include super-strength, flight, and invulnerability. It is suggested that Queen Maeve was more passionate about the Seven's mission than the other superheroes at one point, but found her spirit broken by the team's disastrous handling of the 9/11 attacks. It is also suggested that the 9/11 debacle is the source of her alcoholism.", 38 | }, 39 | { 40 | id: "thedeep", 41 | title: "The Deep", 42 | image: require("../../assets/thedeep.jpeg"), 43 | description: 44 | 'A long-time member of the Seven, The Deep is marketed by Vought-American as the "King of the Seas" and claims he cannot remove his helmet due to an Atlantean curse. The Deep is actually a man in a diving suit. His powers include super-strength, flight, and durability. The Deep is the most mature, civilized member of the team and often bears the brunt of other characters\' contempt, disregard, and racism.', 45 | }, 46 | { 47 | id: "starlight", 48 | title: "Starlight", 49 | image: require("../../assets/starlight.jpg"), 50 | description: 51 | "Starlight, aka Annie January, is the latest member of the Seven. Her known powers are flight and the ability to project blinding light, though she is also hinted to have super-hearing. Starlight was formerly a member of the Young Americans superhero organization, and a conservative Christian. Upon joining the Seven, she is shocked to discover the other members' true nature. On her first visit, the Homelander gives her the choice of providing him, A-Train, and Black Noir with oral sex, or leaving the group.", 52 | }, 53 | ]; 54 | -------------------------------------------------------------------------------- /example/src/data/types.ts: -------------------------------------------------------------------------------- 1 | export interface Item { 2 | id: string; 3 | title: string; 4 | image: any; 5 | description?: string; 6 | } 7 | -------------------------------------------------------------------------------- /example/src/screens/CardItem.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet, Text, Image, Platform } from "react-native"; 3 | import { SharedElement } from "react-navigation-shared-element"; 4 | import { NavigationStackProp } from "react-navigation-stack"; 5 | 6 | import { TouchableScale } from "../components"; 7 | import { Item } from "../data"; 8 | 9 | type Props = { 10 | navigation: NavigationStackProp; // v4 11 | routeName: string; 12 | item: Item; 13 | }; 14 | 15 | export function CardItem(props: Props) { 16 | const { item, navigation, routeName = "Detail" } = props; 17 | return ( 18 | 20 | navigation.navigate(routeName, { 21 | item, 22 | }) 23 | } 24 | > 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {`${item.title}`} 34 | 35 | 36 | 37 | ); 38 | } 39 | 40 | const styles = StyleSheet.create({ 41 | container: { 42 | height: 300, 43 | marginHorizontal: 20, 44 | marginVertical: 10, 45 | }, 46 | card: { 47 | flex: 1, 48 | borderRadius: 20, 49 | backgroundColor: "white", 50 | ...Platform.select({ 51 | android: { 52 | elevation: 3, 53 | }, 54 | default: { 55 | shadowColor: "#000000", 56 | shadowOffset: { 57 | width: 0, 58 | height: 1, 59 | }, 60 | shadowRadius: 2, 61 | shadowOpacity: 0.3, 62 | }, 63 | }), 64 | }, 65 | imageContainer: { 66 | flex: 1, 67 | }, 68 | image: { 69 | width: "100%", 70 | height: "100%", 71 | resizeMode: "cover", 72 | borderTopLeftRadius: 20, 73 | borderTopRightRadius: 20, 74 | }, 75 | textContainer: { 76 | alignSelf: "flex-start", 77 | }, 78 | text: { 79 | fontSize: 24, 80 | color: "black", 81 | marginLeft: 20, 82 | marginVertical: 5, 83 | }, 84 | }); 85 | -------------------------------------------------------------------------------- /example/src/screens/CardScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet, FlatList } from "react-native"; 3 | import { NavigationStackProp } from "react-navigation-stack"; 4 | 5 | import { items, Item } from "../data"; 6 | import { CardItem } from "./CardItem"; 7 | 8 | type Props = { 9 | navigation: NavigationStackProp; 10 | routeName: string; 11 | }; 12 | 13 | export class CardScreen extends React.Component { 14 | render() { 15 | return ( 16 | item.id} 21 | /> 22 | ); 23 | } 24 | 25 | private renderItem = (event: any) => { 26 | const { navigation, routeName } = this.props; 27 | const item: Item = event.item; 28 | return ( 29 | 30 | ); 31 | }; 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | container: { 36 | flex: 1, 37 | }, 38 | }); 39 | -------------------------------------------------------------------------------- /example/src/screens/DetailComponent.tsx: -------------------------------------------------------------------------------- 1 | import { LinearGradient } from "expo-linear-gradient"; 2 | import * as React from "react"; 3 | import { View, StyleSheet, Text, Image, TouchableOpacity } from "react-native"; 4 | import { SharedElement } from "react-navigation-shared-element"; 5 | import { NavigationStackProp } from "react-navigation-stack"; 6 | 7 | import { Icon } from "../components"; 8 | import { Item } from "../data"; 9 | 10 | type Props = { 11 | navigation: NavigationStackProp; 12 | item: Item; 13 | modal: "none" | "full" | "sheet"; 14 | }; 15 | 16 | export const DetailComponent = (props: Props) => { 17 | const { item, navigation, modal = "none" } = props; 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 37 | 41 | 42 | 43 | {item.title} 44 | 45 | 46 | 47 | {item.description} 48 | 49 | 50 | 51 | {modal !== "none" ? ( 52 | 58 | navigation.goBack()} 61 | > 62 | 63 | 64 | 65 | 66 | 67 | ) : undefined} 68 | 69 | ); 70 | }; 71 | 72 | const styles = StyleSheet.create({ 73 | container: { 74 | flex: 1, 75 | alignItems: "center", 76 | }, 77 | header: { 78 | position: "absolute", 79 | right: 16, 80 | top: 32, 81 | }, 82 | sheetHeader: { 83 | right: 16, 84 | top: 16, 85 | }, 86 | icon: { 87 | fontSize: 40, 88 | color: "white", 89 | }, 90 | image: { 91 | width: "100%", 92 | height: "100%", 93 | }, 94 | logoContainer: { 95 | position: "absolute", 96 | left: 4, 97 | top: 12, 98 | height: 60, 99 | width: 160, 100 | }, 101 | logo: { 102 | width: "100%", 103 | height: "100%", 104 | }, 105 | content: { 106 | position: "absolute", 107 | left: 0, 108 | right: 0, 109 | bottom: 0, 110 | padding: 20, 111 | }, 112 | title: { 113 | marginTop: 20, 114 | color: "white", 115 | fontSize: 60, 116 | fontWeight: "bold", 117 | textShadowColor: "black", 118 | textShadowRadius: 8, 119 | textShadowOffset: { 120 | width: 0, 121 | height: 1, 122 | }, 123 | }, 124 | description: { 125 | color: "white", 126 | fontSize: 17, 127 | fontWeight: "bold", 128 | textShadowColor: "black", 129 | textShadowRadius: 4, 130 | textShadowOffset: { 131 | width: 0, 132 | height: 1, 133 | }, 134 | }, 135 | }); 136 | -------------------------------------------------------------------------------- /example/src/screens/DetailComponentImageBackground.tsx: -------------------------------------------------------------------------------- 1 | import { LinearGradient } from "expo-linear-gradient"; 2 | import * as React from "react"; 3 | import { 4 | View, 5 | StyleSheet, 6 | Text, 7 | ImageBackground, 8 | TouchableOpacity, 9 | } from "react-native"; 10 | import { SharedElement } from "react-navigation-shared-element"; 11 | import { NavigationStackProp } from "react-navigation-stack"; 12 | 13 | import { Icon } from "../components"; 14 | import { Item } from "../data"; 15 | 16 | type Props = { 17 | navigation: NavigationStackProp; 18 | item: Item; 19 | modal: "none" | "full" | "sheet"; 20 | }; 21 | 22 | export const DetailComponentImageBackground = (props: Props) => { 23 | const { item, navigation, modal = "none" } = props; 24 | return ( 25 | 26 | 27 | 32 | 33 | Content inside ImageBackground 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 51 | 55 | 56 | 57 | {item.title} 58 | 59 | 60 | 61 | {item.description} 62 | 63 | 64 | 65 | {modal !== "none" ? ( 66 | 72 | navigation.goBack()} 75 | > 76 | 77 | 78 | 79 | 80 | 81 | ) : undefined} 82 | 83 | ); 84 | }; 85 | 86 | const styles = StyleSheet.create({ 87 | container: { 88 | flex: 1, 89 | alignItems: "center", 90 | }, 91 | header: { 92 | position: "absolute", 93 | right: 16, 94 | top: 32, 95 | }, 96 | sheetHeader: { 97 | right: 16, 98 | top: 16, 99 | }, 100 | icon: { 101 | fontSize: 40, 102 | color: "white", 103 | }, 104 | image: { 105 | width: "100%", 106 | height: "100%", 107 | }, 108 | logoContainer: { 109 | position: "absolute", 110 | left: 4, 111 | top: 12, 112 | height: 60, 113 | width: 160, 114 | }, 115 | logo: { 116 | width: "100%", 117 | height: "100%", 118 | }, 119 | content: { 120 | position: "absolute", 121 | left: 0, 122 | right: 0, 123 | bottom: 0, 124 | padding: 20, 125 | }, 126 | title: { 127 | marginTop: 20, 128 | color: "white", 129 | fontSize: 60, 130 | fontWeight: "bold", 131 | textShadowColor: "black", 132 | textShadowRadius: 8, 133 | textShadowOffset: { 134 | width: 0, 135 | height: 1, 136 | }, 137 | }, 138 | description: { 139 | color: "white", 140 | fontSize: 17, 141 | fontWeight: "bold", 142 | textShadowColor: "black", 143 | textShadowRadius: 4, 144 | textShadowOffset: { 145 | width: 0, 146 | height: 1, 147 | }, 148 | }, 149 | imageBackgroundTextContent: { 150 | color: "white", 151 | fontSize: 17, 152 | fontWeight: "bold", 153 | textShadowColor: "black", 154 | textShadowRadius: 4, 155 | textShadowOffset: { 156 | width: 0, 157 | height: 1, 158 | }, 159 | alignSelf: "center", 160 | marginTop: "auto", 161 | marginBottom: "auto", 162 | }, 163 | }); 164 | -------------------------------------------------------------------------------- /example/src/screens/DetailPagerScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet, View } from "react-native"; 3 | import PagerView, { 4 | PagerViewOnPageSelectedEvent, 5 | } from "react-native-pager-view"; 6 | import { SharedElementsComponentConfig } from "react-navigation-shared-element"; 7 | import { NavigationStackProp } from "react-navigation-stack"; 8 | 9 | import { Item, items } from "../data"; 10 | import { DetailComponent } from "./DetailComponent"; 11 | import { getDetailSharedElements } from "./getDetailSharedElements"; 12 | 13 | type Props = { 14 | navigation: NavigationStackProp; 15 | route: any; 16 | }; 17 | 18 | export class DetailPagerScreen extends React.Component { 19 | static sharedElements: SharedElementsComponentConfig = 20 | getDetailSharedElements; 21 | 22 | render() { 23 | const { navigation, route } = this.props; 24 | const params = route?.params || navigation?.state?.params; 25 | const initialItem: Item = params?.item; 26 | const initialIndex = items.indexOf(initialItem); 27 | return ( 28 | 33 | {items.map((item) => this.renderItem(item))} 34 | 35 | ); 36 | } 37 | 38 | private renderItem(item: Item) { 39 | return ( 40 | 41 | 46 | 47 | ); 48 | } 49 | 50 | private onPageSelected = (e: PagerViewOnPageSelectedEvent) => { 51 | const { position } = e.nativeEvent; 52 | const { navigation } = this.props; 53 | navigation.setParams({ 54 | item: items[position], 55 | }); 56 | }; 57 | } 58 | 59 | const styles = StyleSheet.create({ 60 | container: { 61 | flex: 1, 62 | }, 63 | }); 64 | -------------------------------------------------------------------------------- /example/src/screens/DetailScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet } from "react-native"; 3 | import { NavigationStackProp } from "react-navigation-stack"; 4 | 5 | import { TouchableScale } from "../components"; 6 | import { Item, defaultItem } from "../data"; 7 | import { DetailComponent } from "./DetailComponent"; 8 | import { getDetailSharedElements } from "./getDetailSharedElements"; 9 | 10 | type Props = { 11 | navigation: NavigationStackProp; 12 | route: any; // v5 13 | modal: "none" | "full" | "sheet"; 14 | onPress?: ({ 15 | navigation, 16 | item, 17 | }: { 18 | navigation: NavigationStackProp; 19 | item: Item; 20 | }) => void; 21 | }; 22 | 23 | export const DetailScreen = (props: Props) => { 24 | const { navigation, route, modal, onPress } = props; 25 | const params = route?.params || navigation?.state?.params; 26 | const item: Item = params?.item || defaultItem; 27 | const content = ( 28 | 29 | ); 30 | return onPress ? ( 31 | onPress({ navigation, item })} 33 | style={StyleSheet.absoluteFill} 34 | > 35 | {content} 36 | 37 | ) : ( 38 | content 39 | ); 40 | }; 41 | 42 | DetailScreen.navigationOptions = { 43 | title: "Boys will be boys", 44 | }; 45 | 46 | // Add the `sharedElements` function to the component, which 47 | // should return a list of shared-elements to transition. 48 | // The `sharedElements` function is called whenever you navigate 49 | // to or from this screen. You can use the provided navigation 50 | // states or trigger or disable animations. 51 | DetailScreen.sharedElements = getDetailSharedElements; 52 | -------------------------------------------------------------------------------- /example/src/screens/DetailScreenImageBackground.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet } from "react-native"; 3 | import { NavigationStackProp } from "react-navigation-stack"; 4 | 5 | import { TouchableScale } from "../components"; 6 | import { Item, defaultItem } from "../data"; 7 | import { DetailComponentImageBackground } from "./DetailComponentImageBackground"; 8 | import { getDetailSharedElements } from "./getDetailSharedElements"; 9 | 10 | type Props = { 11 | navigation: NavigationStackProp; 12 | route: any; // v5 13 | modal: "none" | "full" | "sheet"; 14 | onPress?: ({ 15 | navigation, 16 | item, 17 | }: { 18 | navigation: NavigationStackProp; 19 | item: Item; 20 | }) => void; 21 | }; 22 | 23 | export const DetailScreenImageBackground = (props: Props) => { 24 | const { navigation, route, modal, onPress } = props; 25 | const params = route?.params || navigation?.state?.params; 26 | const item: Item = params?.item || defaultItem; 27 | const content = ( 28 | 33 | ); 34 | return onPress ? ( 35 | onPress({ navigation, item })} 37 | style={StyleSheet.absoluteFill} 38 | > 39 | {content} 40 | 41 | ) : ( 42 | content 43 | ); 44 | }; 45 | 46 | DetailScreenImageBackground.navigationOptions = { 47 | title: "Boys will be boys", 48 | }; 49 | 50 | // Add the `sharedElements` function to the component, which 51 | // should return a list of shared-elements to transition. 52 | // The `sharedElements` function is called whenever you navigate 53 | // to or from this screen. You can use the provided navigation 54 | // states or trigger or disable animations. 55 | DetailScreenImageBackground.sharedElements = getDetailSharedElements; 56 | -------------------------------------------------------------------------------- /example/src/screens/ListScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet, Image, FlatList, Text } from "react-native"; 3 | import { SharedElement } from "react-navigation-shared-element"; 4 | import { NavigationStackProp } from "react-navigation-stack"; 5 | 6 | import { TouchableScale } from "../components"; 7 | import { items, Item } from "../data"; 8 | 9 | type Props = { 10 | navigation: NavigationStackProp; 11 | routeName: string; 12 | }; 13 | 14 | export class ListScreen extends React.Component { 15 | static defaultProps = { 16 | routeName: "Detail", 17 | }; 18 | 19 | render() { 20 | return ( 21 | item.id} 26 | /> 27 | ); 28 | } 29 | 30 | private renderItem = (event: any) => { 31 | const { navigation, routeName } = this.props; 32 | const item: Item = event.item; 33 | // Wrap the component that you want to transition in 34 | return ( 35 | { 38 | navigation.navigate(routeName, { 39 | item, 40 | }); 41 | }} 42 | > 43 | 44 | 45 | 46 | 47 | {item.title} 48 | 49 | 50 | ); 51 | }; 52 | } 53 | 54 | const styles = StyleSheet.create({ 55 | container: { 56 | flex: 1, 57 | }, 58 | item: { 59 | height: 200, 60 | }, 61 | image: { 62 | width: "100%", 63 | height: 200, 64 | resizeMode: "cover", 65 | }, 66 | title: { 67 | position: "absolute", 68 | right: 10, 69 | bottom: 10, 70 | fontSize: 14, 71 | fontWeight: "bold", 72 | textTransform: "uppercase", 73 | color: "black", 74 | textShadowColor: "white", 75 | textShadowRadius: 6, 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /example/src/screens/MainScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet, Text, Image } from "react-native"; 3 | import { SharedElement } from "react-navigation-shared-element"; 4 | import { NavigationStackProp } from "react-navigation-stack"; 5 | 6 | import { TouchableScale } from "../components"; 7 | import { defaultItem, Item } from "../data"; 8 | 9 | type Props = { 10 | navigation: NavigationStackProp; // v4 11 | route: any; // v5 12 | routeName: string; 13 | }; 14 | 15 | export class MainScreen extends React.Component { 16 | static defaultProps = { 17 | routeName: "Detail", 18 | }; 19 | 20 | render() { 21 | const { navigation, route } = this.props; 22 | const params = route?.params || navigation?.state?.params; 23 | const item: Item = params?.item || defaultItem; 24 | return ( 25 | <> 26 | 27 | 28 | 29 | 30 | 31 | 32 | {`${item.title}`} 33 | 34 | tap me 35 | 36 | 37 | 38 | ); 39 | } 40 | 41 | private onPress = () => { 42 | const { navigation, routeName } = this.props; 43 | navigation.navigate(routeName); 44 | }; 45 | } 46 | 47 | const styles = StyleSheet.create({ 48 | flex: { 49 | flex: 1, 50 | }, 51 | container: { 52 | flex: 1, 53 | justifyContent: "center", 54 | alignItems: "center", 55 | }, 56 | text: { 57 | fontSize: 40, 58 | }, 59 | caption: { 60 | fontSize: 20, 61 | opacity: 0.5, 62 | }, 63 | image: { 64 | width: 240, 65 | height: 160, 66 | resizeMode: "cover", 67 | borderRadius: 20, 68 | }, 69 | }); 70 | -------------------------------------------------------------------------------- /example/src/screens/createScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export function createScreen( 4 | Component: React.ComponentType, 5 | title?: string, 6 | sharedElements?: any, 7 | overriddenProps?: any, 8 | render?: (props: any) => any 9 | ) { 10 | const WrappedComponent = (props: any) => { 11 | const allProps = { 12 | ...props, 13 | ...(overriddenProps ?? {}), 14 | }; 15 | return render?.(allProps) ?? ; 16 | }; 17 | // @ts-ignore 18 | const { navigationOptions } = Component; 19 | WrappedComponent.navigationOptions = title 20 | ? { 21 | title, 22 | } 23 | : navigationOptions; 24 | // @ts-ignore 25 | WrappedComponent.sharedElements = sharedElements || Component.sharedElements; 26 | WrappedComponent.displayName = Component.displayName || Component.name; 27 | return WrappedComponent; 28 | } 29 | -------------------------------------------------------------------------------- /example/src/screens/getDetailSharedElements.ts: -------------------------------------------------------------------------------- 1 | import type { SharedElementsComponentConfig } from "react-navigation-shared-element"; 2 | 3 | import { defaultItem } from "../data"; 4 | 5 | export const getDetailSharedElements: SharedElementsComponentConfig = ( 6 | route, 7 | otherRoute, 8 | showing 9 | ) => { 10 | const item = route.params.item || defaultItem; 11 | return [ 12 | { id: `${item.id}.image` }, 13 | { id: `${item.id}.logo`, animation: "fade" }, 14 | { id: `${item.id}.gradient`, animation: "fade" }, 15 | { id: `${item.id}.title`, animation: "fade" }, 16 | { id: `${item.id}.description`, animation: "fade" }, 17 | { id: "close", animation: "fade" }, 18 | ]; 19 | }; 20 | -------------------------------------------------------------------------------- /example/src/screens/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MainScreen"; 2 | export * from "./DetailScreen"; 3 | export * from "./DetailScreenImageBackground"; 4 | export * from "./DetailPagerScreen"; 5 | export * from "./ListScreen"; 6 | export * from "./createScreen"; 7 | export * from "./CardScreen"; 8 | export * from "./CardItem"; 9 | -------------------------------------------------------------------------------- /example/src/tests/BackOnly.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { MainScreen, DetailScreen } from "../screens"; 6 | import { getDetailSharedElements } from "../screens/getDetailSharedElements"; 7 | 8 | const name = "BackOnly"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | export default () => ( 16 | 17 | 18 | 19 | { 23 | // Only return the shared-elements when hiding this route 24 | if (showing) return; 25 | return getDetailSharedElements(route, otherRoute, showing); 26 | }} 27 | /> 28 | 29 | 30 | ); 31 | -------------------------------------------------------------------------------- /example/src/tests/BackOnly.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { 3 | createSharedElementStackNavigator, 4 | SharedElementsComponentConfig, 5 | } from "react-navigation-shared-element/build/v4"; 6 | 7 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 8 | import { getDetailSharedElements } from "../screens/getDetailSharedElements"; 9 | 10 | const name = "BackOnly"; 11 | 12 | const sharedElements: SharedElementsComponentConfig = ( 13 | route, 14 | otherRoute, 15 | showing 16 | ) => { 17 | // Only return the shared-elements when hiding this route 18 | if (showing) return; 19 | return getDetailSharedElements(route, otherRoute, showing); 20 | }; 21 | 22 | const StackNavigator = createSharedElementStackNavigator( 23 | { 24 | Main: createScreen(MainScreen, name), 25 | Detail: createScreen(DetailScreen, undefined, sharedElements), 26 | }, 27 | undefined, 28 | { 29 | name, 30 | debug: true, 31 | } 32 | ); 33 | 34 | export default createAppContainer(StackNavigator); 35 | -------------------------------------------------------------------------------- /example/src/tests/BottomTabs.tsx: -------------------------------------------------------------------------------- 1 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; 2 | import { NavigationContainer } from "@react-navigation/native"; 3 | import * as React from "react"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { TabBarIcon } from "../components"; 7 | import { MainScreen, DetailScreen } from "../screens"; 8 | 9 | const name = "BottomTabs"; 10 | 11 | const BottomTab = createBottomTabNavigator(); 12 | 13 | const Stack1 = createSharedElementStackNavigator({ 14 | name, 15 | debug: true, 16 | }); 17 | 18 | const Stack2 = createSharedElementStackNavigator({ 19 | name, 20 | debug: true, 21 | }); 22 | 23 | const Stack1Screen = () => ( 24 | 25 | 26 | 27 | 28 | ); 29 | 30 | const Stack2Screen = () => ( 31 | 32 | 33 | 34 | 35 | ); 36 | 37 | export default () => ( 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | -------------------------------------------------------------------------------- /example/src/tests/BottomTabs.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { createBottomTabNavigator } from "react-navigation-tabs"; 4 | 5 | import { TabBarIcon } from "../components"; 6 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 7 | 8 | const StackNavigator1 = createSharedElementStackNavigator( 9 | { 10 | Main: createScreen(MainScreen, "BottomStack1"), 11 | Detail: DetailScreen, 12 | }, 13 | undefined, 14 | { 15 | name: "BottomStack1", 16 | debug: true, 17 | } 18 | ); 19 | 20 | const StackNavigator2 = createSharedElementStackNavigator( 21 | { 22 | Main: createScreen(MainScreen, "BottomStack2"), 23 | Detail: DetailScreen, 24 | }, 25 | undefined, 26 | { 27 | name: "BottomStack2", 28 | debug: true, 29 | } 30 | ); 31 | 32 | const TabNavigator = createBottomTabNavigator({ 33 | Tab1: { 34 | screen: StackNavigator1, 35 | navigationOptions: { 36 | title: "Stack 1", 37 | tabBarIcon: TabBarIcon, 38 | }, 39 | }, 40 | Tab2: { 41 | screen: StackNavigator2, 42 | navigationOptions: { 43 | title: "Stack 2", 44 | tabBarIcon: TabBarIcon, 45 | }, 46 | }, 47 | }); 48 | 49 | export default createAppContainer(TabNavigator); 50 | -------------------------------------------------------------------------------- /example/src/tests/BottomTabs2.tsx: -------------------------------------------------------------------------------- 1 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; 2 | import { NavigationContainer } from "@react-navigation/native"; 3 | import * as React from "react"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { TabBarIcon } from "../components"; 7 | import { MainScreen, DetailScreen } from "../screens"; 8 | 9 | const name = "BottomTabs2"; 10 | 11 | const BottomTab = createBottomTabNavigator(); 12 | 13 | const Stack = createSharedElementStackNavigator({ 14 | name, 15 | debug: true, 16 | }); 17 | 18 | const Stack2 = createSharedElementStackNavigator({ 19 | name: "Stack2", 20 | debug: true, 21 | }); 22 | 23 | const StackScreen = () => ( 24 | 25 | 26 | 27 | ); 28 | 29 | // In order to ensure that Tab1 works correctly, it is wrapped 30 | // with a shared-element stack navigator. This is a workaround that 31 | // ensures that the screen is wrapped in a shared-element scene. 32 | // In the example below, Tab2 is not wrapped and therefore doesn't 33 | // perform shared-element transitions. 34 | const TabScreen = () => ( 35 | 36 | 37 | 38 | 39 | ); 40 | 41 | export default () => ( 42 | 43 | 44 | 45 | 46 | 47 | 48 | ); 49 | -------------------------------------------------------------------------------- /example/src/tests/BottomTabs2.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { createBottomTabNavigator } from "react-navigation-tabs"; 4 | 5 | import { TabBarIcon } from "../components"; 6 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 7 | 8 | const ChildStack = createSharedElementStackNavigator( 9 | { 10 | Main: createScreen(MainScreen, "BottomTabs2"), 11 | }, 12 | { 13 | headerMode: "none", 14 | }, 15 | { 16 | name: "BottomTabs2", 17 | debug: true, 18 | } 19 | ); 20 | 21 | // In order to ensure that Tab1 works correctly, it is wrapped 22 | // with a shared-element stack navigator. This is a workaround that 23 | // ensures that the screen is wrapped in a shared-element scene. 24 | // In the example below, Tab2 is not wrapped and therefore doesn't 25 | // perform shared-element transitions. 26 | const TabNavigator = createBottomTabNavigator({ 27 | Tab1: { 28 | screen: ChildStack, 29 | navigationOptions: { 30 | title: "Stack 1", 31 | tabBarIcon: TabBarIcon, 32 | }, 33 | }, 34 | Tab2: { 35 | screen: MainScreen, 36 | navigationOptions: { 37 | title: "Stack 2", 38 | tabBarIcon: TabBarIcon, 39 | }, 40 | }, 41 | }); 42 | 43 | const MainStack = createSharedElementStackNavigator( 44 | { 45 | Main: TabNavigator, 46 | Detail: DetailScreen, 47 | }, 48 | undefined, 49 | { 50 | name: "MainStack", 51 | debug: true, 52 | } 53 | ); 54 | 55 | export default createAppContainer(MainStack); 56 | -------------------------------------------------------------------------------- /example/src/tests/CardView.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { CardScreen, DetailPagerScreen } from "../screens"; 6 | 7 | const name = "CardView"; 8 | 9 | const Stack = createSharedElementStackNavigator({ 10 | name, 11 | debug: true, 12 | }); 13 | 14 | export default () => ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /example/src/tests/CardView.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | 4 | import { createScreen, CardScreen, DetailPagerScreen } from "../screens"; 5 | 6 | const name = "CardView"; 7 | 8 | const StackNavigator = createSharedElementStackNavigator( 9 | { 10 | List: createScreen(CardScreen, name), 11 | Detail: DetailPagerScreen, 12 | }, 13 | undefined, 14 | { 15 | name, 16 | debug: true, 17 | } 18 | ); 19 | 20 | export default createAppContainer(StackNavigator); 21 | -------------------------------------------------------------------------------- /example/src/tests/DefaultStack.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { MainScreen, DetailScreen } from "../screens"; 6 | 7 | const name = "DefaultStack"; 8 | 9 | const Stack = createSharedElementStackNavigator({ 10 | name, 11 | debug: true, 12 | }); 13 | 14 | export default () => ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /example/src/tests/DefaultStack.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | 4 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 5 | 6 | const name = "SimpleStack"; 7 | 8 | const StackNavigator = createSharedElementStackNavigator( 9 | { 10 | Main: createScreen(MainScreen, name), 11 | Detail: DetailScreen, 12 | }, 13 | undefined, 14 | { 15 | name, 16 | debug: true, 17 | } 18 | ); 19 | 20 | export default createAppContainer(StackNavigator); 21 | -------------------------------------------------------------------------------- /example/src/tests/FadeFromBottomAndroid.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import { TransitionPresets } from "@react-navigation/stack"; 3 | import * as React from "react"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { MainScreen, DetailScreen } from "../screens"; 7 | 8 | const name = "FadeFromBottomAndroid"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | export default () => ( 16 | 17 | 22 | 23 | 24 | 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /example/src/tests/FadeFromBottomAndroid.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { TransitionPresets } from "react-navigation-stack"; 4 | 5 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 6 | 7 | const name = "FadeFromBottomAndroid"; 8 | 9 | const StackNavigator = createSharedElementStackNavigator( 10 | { 11 | Main: createScreen(MainScreen, name), 12 | Detail: DetailScreen, 13 | }, 14 | { 15 | defaultNavigationOptions: { 16 | ...TransitionPresets[name], 17 | }, 18 | }, 19 | { 20 | name, 21 | debug: true, 22 | } 23 | ); 24 | 25 | export default createAppContainer(StackNavigator); 26 | -------------------------------------------------------------------------------- /example/src/tests/ForwardOnly.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { MainScreen, DetailScreen } from "../screens"; 6 | import { getDetailSharedElements } from "../screens/getDetailSharedElements"; 7 | 8 | const name = "ForwardOnly"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | export default () => ( 16 | 17 | 18 | 19 | { 23 | // Only return the shared-elements when showing this route 24 | if (!showing) return; 25 | return getDetailSharedElements(route, otherRoute, showing); 26 | }} 27 | /> 28 | 29 | 30 | ); 31 | -------------------------------------------------------------------------------- /example/src/tests/ForwardOnly.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { 3 | createSharedElementStackNavigator, 4 | SharedElementsComponentConfig, 5 | } from "react-navigation-shared-element/build/v4"; 6 | 7 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 8 | import { getDetailSharedElements } from "../screens/getDetailSharedElements"; 9 | 10 | const name = "ForwardOnly"; 11 | 12 | const sharedElements: SharedElementsComponentConfig = ( 13 | route, 14 | otherRoute, 15 | showing 16 | ) => { 17 | // Only return the shared-elements when showing this route 18 | if (!showing) return; 19 | return getDetailSharedElements(route, otherRoute, showing); 20 | }; 21 | 22 | const StackNavigator = createSharedElementStackNavigator( 23 | { 24 | Main: createScreen(MainScreen, name), 25 | Detail: createScreen(DetailScreen, undefined, sharedElements), 26 | }, 27 | undefined, 28 | { 29 | name, 30 | debug: true, 31 | } 32 | ); 33 | 34 | export default createAppContainer(StackNavigator); 35 | -------------------------------------------------------------------------------- /example/src/tests/ImageBackground.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { MainScreen, DetailScreenImageBackground } from "../screens"; 6 | 7 | const name = "DefaultStack"; 8 | 9 | const Stack = createSharedElementStackNavigator({ 10 | name, 11 | debug: true, 12 | }); 13 | 14 | export default () => ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /example/src/tests/ImageBackground.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | 4 | import { 5 | createScreen, 6 | MainScreen, 7 | DetailScreenImageBackground, 8 | } from "../screens"; 9 | 10 | const name = "SimpleStack"; 11 | 12 | const StackNavigator = createSharedElementStackNavigator( 13 | { 14 | Main: createScreen(MainScreen, name), 15 | Detail: DetailScreenImageBackground, 16 | }, 17 | undefined, 18 | { 19 | name, 20 | debug: true, 21 | } 22 | ); 23 | 24 | export default createAppContainer(StackNavigator); 25 | -------------------------------------------------------------------------------- /example/src/tests/ListView.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { ListScreen, DetailPagerScreen } from "../screens"; 6 | 7 | const name = "ListView"; 8 | 9 | const Stack = createSharedElementStackNavigator({ 10 | name, 11 | debug: true, 12 | }); 13 | 14 | export default () => ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /example/src/tests/ListView.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | 4 | import { createScreen, ListScreen, DetailPagerScreen } from "../screens"; 5 | 6 | const name = "ListView"; 7 | 8 | const StackNavigator = createSharedElementStackNavigator( 9 | { 10 | List: createScreen(ListScreen, name), 11 | Detail: DetailPagerScreen, 12 | }, 13 | undefined, 14 | { 15 | name, 16 | debug: true, 17 | } 18 | ); 19 | 20 | export default createAppContainer(StackNavigator); 21 | -------------------------------------------------------------------------------- /example/src/tests/MaterialTopTabs.tsx: -------------------------------------------------------------------------------- 1 | import { createMaterialTopTabNavigator } from "@react-navigation/material-top-tabs"; 2 | import { NavigationContainer } from "@react-navigation/native"; 3 | import * as React from "react"; 4 | import { SafeAreaView } from "react-native-safe-area-context"; 5 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 6 | 7 | import { MainScreen, DetailScreen } from "../screens"; 8 | 9 | const name = "MaterialTopTabs"; 10 | 11 | const MaterialTopTab = createMaterialTopTabNavigator(); 12 | 13 | const Stack1 = createSharedElementStackNavigator({ 14 | name, 15 | debug: true, 16 | }); 17 | 18 | const Stack2 = createSharedElementStackNavigator({ 19 | name, 20 | debug: true, 21 | }); 22 | 23 | const Stack1Screen = () => ( 24 | 25 | 26 | 27 | 28 | ); 29 | 30 | const Stack2Screen = () => ( 31 | 32 | 33 | 34 | 35 | ); 36 | 37 | export default () => ( 38 | 39 | 40 | {/* @ts-ignore: No overload matches this call when using react navigation 6 */} 41 | 42 | 43 | 44 | 45 | 46 | 47 | ); 48 | -------------------------------------------------------------------------------- /example/src/tests/MaterialTopTabs.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { createMaterialTopTabNavigator } from "react-navigation-tabs"; 4 | 5 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 6 | 7 | const StackNavigator1 = createSharedElementStackNavigator( 8 | { 9 | Main: createScreen(MainScreen, "TopStack1"), 10 | Detail: DetailScreen, 11 | }, 12 | undefined, 13 | { 14 | name: "TopStack1", 15 | debug: true, 16 | } 17 | ); 18 | 19 | const StackNavigator2 = createSharedElementStackNavigator( 20 | { 21 | Main: createScreen(MainScreen, "TopStack2"), 22 | Detail: DetailScreen, 23 | }, 24 | undefined, 25 | { 26 | name: "TopStack2", 27 | debug: true, 28 | } 29 | ); 30 | 31 | const TabNavigator = createMaterialTopTabNavigator({ 32 | Tab1: { 33 | screen: StackNavigator1, 34 | navigationOptions: { 35 | title: "Stack 1", 36 | }, 37 | }, 38 | Tab2: { 39 | screen: StackNavigator2, 40 | navigationOptions: { 41 | title: "Stack 2", 42 | }, 43 | }, 44 | }); 45 | 46 | export default createAppContainer(TabNavigator); 47 | -------------------------------------------------------------------------------- /example/src/tests/ModalIOS13PageSheet.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import { TransitionPresets } from "@react-navigation/stack"; 3 | import * as React from "react"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { MainScreen, DetailScreen, createScreen } from "../screens"; 7 | 8 | const name = "ModalIOS13PageSheet"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | const ModalDetailScreen = createScreen(DetailScreen, undefined, undefined, { 16 | modal: "sheet", 17 | }); 18 | 19 | export default () => ( 20 | 21 | ({ 26 | headerShown: false, 27 | presentation: "modal", 28 | cardOverlayEnabled: true, 29 | ...TransitionPresets.ModalPresentationIOS, 30 | })} 31 | > 32 | 33 | 34 | 35 | 36 | ); 37 | -------------------------------------------------------------------------------- /example/src/tests/ModalIOS13PageSheet.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { TransitionPresets } from "react-navigation-stack"; 4 | 5 | import { MainScreen, DetailScreen, createScreen } from "../screens"; 6 | 7 | const name = "ModalIOS13PageSheet"; 8 | 9 | const StackNavigator = createSharedElementStackNavigator( 10 | { 11 | Main: createScreen(MainScreen, name), 12 | Detail: createScreen(DetailScreen, undefined, undefined, { 13 | modal: "sheet", 14 | }), 15 | }, 16 | { 17 | mode: "modal", 18 | headerMode: "none", 19 | defaultNavigationOptions: { 20 | gestureEnabled: true, 21 | cardOverlayEnabled: true, 22 | ...TransitionPresets.ModalPresentationIOS, 23 | }, 24 | }, 25 | { 26 | name, 27 | debug: true, 28 | } 29 | ); 30 | 31 | export default createAppContainer(StackNavigator); 32 | -------------------------------------------------------------------------------- /example/src/tests/ModalSlideFromBottomIOS.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import { TransitionPresets } from "@react-navigation/stack"; 3 | import * as React from "react"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { MainScreen, DetailScreen } from "../screens"; 7 | 8 | const name = "ModalSlideFromBottomIOS"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | export default () => ( 16 | 17 | 22 | 23 | 24 | 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /example/src/tests/ModalSlideFromBottomIOS.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { TransitionPresets } from "react-navigation-stack"; 4 | 5 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 6 | 7 | const name = "ModalSlideFromBottomIOS"; 8 | 9 | const StackNavigator = createSharedElementStackNavigator( 10 | { 11 | Main: createScreen(MainScreen, name), 12 | Detail: DetailScreen, 13 | }, 14 | { 15 | defaultNavigationOptions: { 16 | ...TransitionPresets[name], 17 | }, 18 | }, 19 | { 20 | name, 21 | debug: true, 22 | } 23 | ); 24 | 25 | export default createAppContainer(StackNavigator); 26 | -------------------------------------------------------------------------------- /example/src/tests/ModalStack.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { MainScreen, DetailScreen, createScreen } from "../screens"; 6 | 7 | const name = "ModalStack"; 8 | 9 | const Stack = createSharedElementStackNavigator({ 10 | name, 11 | debug: true, 12 | }); 13 | 14 | const ModalDetailScreen = createScreen(DetailScreen, undefined, undefined, { 15 | modal: "full", 16 | }); 17 | 18 | export default () => ( 19 | 20 | ({ 22 | headerShown: false, 23 | presentation: "modal", 24 | })} 25 | > 26 | 27 | 28 | 29 | 30 | ); 31 | -------------------------------------------------------------------------------- /example/src/tests/ModalStack.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | 4 | import { MainScreen, DetailScreen, createScreen } from "../screens"; 5 | 6 | const ModalStackNavigator = createSharedElementStackNavigator( 7 | { 8 | Main: createScreen(MainScreen, "ModalStack"), 9 | Detail: createScreen(DetailScreen, undefined, undefined, { modal: "full" }), 10 | }, 11 | { 12 | mode: "modal", 13 | headerMode: "none", 14 | defaultNavigationOptions: { 15 | gestureEnabled: true, 16 | cardOverlayEnabled: true, 17 | }, 18 | }, 19 | { 20 | name: "ModalStack", 21 | debug: true, 22 | } 23 | ); 24 | 25 | export default createAppContainer(ModalStackNavigator); 26 | -------------------------------------------------------------------------------- /example/src/tests/NestedStack.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { MainScreen, DetailScreen } from "../screens"; 6 | 7 | const name = "NestedStack"; 8 | 9 | const RootStack = createSharedElementStackNavigator({ 10 | name, 11 | debug: true, 12 | }); 13 | 14 | const NestedStack = createSharedElementStackNavigator({ 15 | name, 16 | debug: true, 17 | }); 18 | 19 | const NestedStackScreen = () => ( 20 | 21 | 22 | 23 | 24 | ); 25 | 26 | export default () => ( 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | -------------------------------------------------------------------------------- /example/src/tests/NestedStack.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | 4 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 5 | 6 | const NestedStackNavigator = createSharedElementStackNavigator( 7 | { 8 | Main: createScreen(MainScreen, "NestedStack"), 9 | Detail: DetailScreen, 10 | }, 11 | {}, 12 | { 13 | name: "NestedStack", 14 | debug: true, 15 | } 16 | ); 17 | 18 | const RootStackNavigator = createSharedElementStackNavigator( 19 | { 20 | Nested: { 21 | screen: NestedStackNavigator, 22 | navigationOptions: { 23 | title: "RootStack", 24 | }, 25 | }, 26 | }, 27 | {}, 28 | { 29 | name: "RootStack", 30 | debug: true, 31 | } 32 | ); 33 | 34 | export default createAppContainer(RootStackNavigator); 35 | -------------------------------------------------------------------------------- /example/src/tests/NestedStack2.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { MainScreen, DetailScreen, createScreen } from "../screens"; 6 | 7 | const name = "NestedStack"; 8 | 9 | const RootStack = createSharedElementStackNavigator({ 10 | name, 11 | debug: true, 12 | }); 13 | 14 | const NestedStack = createSharedElementStackNavigator({ 15 | name, 16 | debug: true, 17 | }); 18 | 19 | const ModalDetailScreen = createScreen(DetailScreen, undefined, undefined, { 20 | modal: "full", 21 | }); 22 | 23 | const NestedStackScreen = () => ( 24 | 25 | 26 | 27 | ); 28 | 29 | export default () => ( 30 | 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | -------------------------------------------------------------------------------- /example/src/tests/NestedStack2.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | 4 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 5 | 6 | const NestedStackNavigator = createSharedElementStackNavigator( 7 | { 8 | Main: createScreen(MainScreen, "NestedStack2"), 9 | }, 10 | undefined, 11 | { 12 | name: "NestedStack2", 13 | debug: true, 14 | } 15 | ); 16 | 17 | const RootStackNavigator = createSharedElementStackNavigator( 18 | { 19 | Nested: { 20 | screen: NestedStackNavigator, 21 | navigationOptions: { 22 | title: "RootStack", 23 | }, 24 | }, 25 | Detail: createScreen(DetailScreen, undefined, undefined, { modal: "full" }), 26 | }, 27 | { 28 | headerMode: "none", 29 | }, 30 | { 31 | name: "RootStack", 32 | debug: true, 33 | } 34 | ); 35 | 36 | export default createAppContainer(RootStackNavigator); 37 | -------------------------------------------------------------------------------- /example/src/tests/PushPopSameScreen.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { Item, items } from "../data"; 6 | import { createScreen, DetailScreen } from "../screens"; 7 | 8 | const sameIdItems = items.map((item) => ({ ...item, id: "sameId" })); 9 | 10 | const name = "PushPopSameScreen"; 11 | 12 | const Stack = createSharedElementStackNavigator({ 13 | name, 14 | debug: true, 15 | }); 16 | 17 | const PressableDetailScreen = createScreen(DetailScreen, undefined, undefined, { 18 | onPress: (event: any) => { 19 | const item: Item = event.item; 20 | const itemIdx = sameIdItems.indexOf(item); 21 | const nextItem = 22 | sameIdItems[itemIdx < sameIdItems.length - 2 ? itemIdx + 1 : 0]; 23 | event.navigation.push("Detail", { 24 | item: nextItem, 25 | }); 26 | }, 27 | }); 28 | 29 | export default () => ( 30 | 31 | 32 | { 37 | const item = route.params.item; 38 | return [ 39 | { id: `${item.id}.image`, animation: "fade" }, 40 | { id: `${item.id}.logo` }, 41 | { id: `${item.id}.gradient`, animation: "fade" }, 42 | { id: `${item.id}.title`, animation: "fade" }, 43 | { id: `${item.id}.description`, animation: "fade" }, 44 | { id: "close" }, 45 | ]; 46 | }} 47 | /> 48 | 49 | 50 | ); 51 | -------------------------------------------------------------------------------- /example/src/tests/PushPopSameScreen.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { 3 | createSharedElementStackNavigator, 4 | SharedElementsComponentConfig, 5 | } from "react-navigation-shared-element/build/v4"; 6 | 7 | import { Item, items } from "../data"; 8 | import { createScreen, DetailScreen } from "../screens"; 9 | 10 | const sameIdItems = items.map((item) => ({ ...item, id: "sameId" })); 11 | 12 | const name = "PushPopSameScreen"; 13 | 14 | const sharedElements: SharedElementsComponentConfig = ( 15 | route, 16 | otherRoute, 17 | showing 18 | ) => { 19 | const item = route.params.item; 20 | return [ 21 | { id: `${item.id}.image`, animation: "fade" }, 22 | { id: `${item.id}.logo` }, 23 | { id: `${item.id}.gradient`, animation: "fade" }, 24 | { id: `${item.id}.title`, animation: "fade" }, 25 | { id: `${item.id}.description`, animation: "fade" }, 26 | { id: "close" }, 27 | ]; 28 | }; 29 | 30 | const StackNavigator = createSharedElementStackNavigator( 31 | { 32 | Detail: createScreen(DetailScreen, undefined, sharedElements, { 33 | onPress: (event: any) => { 34 | const item: Item = event.item; 35 | const itemIdx = sameIdItems.indexOf(item); 36 | const nextItem = 37 | sameIdItems[itemIdx < sameIdItems.length - 2 ? itemIdx + 1 : 0]; 38 | event.navigation.push("Detail", { 39 | item: nextItem, 40 | }); 41 | }, 42 | }), 43 | }, 44 | { 45 | initialRouteParams: { 46 | item: sameIdItems[0], 47 | }, 48 | }, 49 | { 50 | name, 51 | debug: true, 52 | } 53 | ); 54 | 55 | export default createAppContainer(StackNavigator); 56 | -------------------------------------------------------------------------------- /example/src/tests/RevealFromBottomAndroid.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import { TransitionPresets } from "@react-navigation/stack"; 3 | import * as React from "react"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { MainScreen, DetailScreen } from "../screens"; 7 | 8 | const name = "RevealFromBottomAndroid"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | export default () => ( 16 | 17 | 22 | 23 | 24 | 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /example/src/tests/RevealFromBottomAndroid.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { TransitionPresets } from "react-navigation-stack"; 4 | 5 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 6 | 7 | const name = "RevealFromBottomAndroid"; 8 | 9 | const StackNavigator = createSharedElementStackNavigator( 10 | { 11 | Main: createScreen(MainScreen, name), 12 | Detail: DetailScreen, 13 | }, 14 | { 15 | defaultNavigationOptions: { 16 | ...TransitionPresets[name], 17 | }, 18 | }, 19 | { 20 | name, 21 | debug: true, 22 | } 23 | ); 24 | 25 | export default createAppContainer(StackNavigator); 26 | -------------------------------------------------------------------------------- /example/src/tests/SafeAreaView.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { SafeAreaView } from "react-native-safe-area-context"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 7 | 8 | const name = "SafeAreaView"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | const SafeAreaMainScreen = createScreen( 16 | MainScreen, 17 | undefined, 18 | undefined, 19 | undefined, 20 | (props: any) => ( 21 | 22 | 23 | 24 | ) 25 | ); 26 | 27 | const SafeAreaDetailScreen = createScreen( 28 | DetailScreen, 29 | undefined, 30 | undefined, 31 | undefined, 32 | (props: any) => ( 33 | 34 | 35 | 36 | ) 37 | ); 38 | 39 | export default () => ( 40 | 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | -------------------------------------------------------------------------------- /example/src/tests/SafeAreaView.v4.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { SafeAreaView } from "react-native-safe-area-context"; 3 | import { createAppContainer } from "react-navigation"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 5 | 6 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 7 | 8 | const name = "SafeAreaView"; 9 | 10 | const SafeAreaMainScreen = createScreen( 11 | MainScreen, 12 | undefined, 13 | undefined, 14 | undefined, 15 | (props: any) => ( 16 | 17 | 18 | 19 | ) 20 | ); 21 | 22 | const SafeAreaDetailScreen = createScreen( 23 | DetailScreen, 24 | undefined, 25 | undefined, 26 | undefined, 27 | (props: any) => ( 28 | 29 | 30 | 31 | ) 32 | ); 33 | 34 | const StackNavigator = createSharedElementStackNavigator( 35 | { 36 | Main: SafeAreaMainScreen, 37 | Detail: SafeAreaDetailScreen, 38 | }, 39 | undefined, 40 | { 41 | name, 42 | debug: true, 43 | } 44 | ); 45 | 46 | export default createAppContainer(StackNavigator); 47 | -------------------------------------------------------------------------------- /example/src/tests/ScaleFromCenterAndroid.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import { TransitionPresets } from "@react-navigation/stack"; 3 | import * as React from "react"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { MainScreen, DetailScreen } from "../screens"; 7 | 8 | const name = "ScaleFromCenterAndroid"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | export default () => ( 16 | 17 | 22 | 23 | 24 | 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /example/src/tests/ScaleFromCenterAndroid.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { TransitionPresets } from "react-navigation-stack"; 4 | 5 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 6 | 7 | const name = "ScaleFromCenterAndroid"; 8 | 9 | const StackNavigator = createSharedElementStackNavigator( 10 | { 11 | Main: createScreen(MainScreen, name), 12 | Detail: DetailScreen, 13 | }, 14 | { 15 | defaultNavigationOptions: { 16 | ...TransitionPresets[name], 17 | }, 18 | }, 19 | { 20 | name, 21 | debug: true, 22 | } 23 | ); 24 | 25 | export default createAppContainer(StackNavigator); 26 | -------------------------------------------------------------------------------- /example/src/tests/SlideFromRightIOS.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import { TransitionPresets } from "@react-navigation/stack"; 3 | import * as React from "react"; 4 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 5 | 6 | import { MainScreen, DetailScreen } from "../screens"; 7 | 8 | const name = "SlideFromRightIOS"; 9 | 10 | const Stack = createSharedElementStackNavigator({ 11 | name, 12 | debug: true, 13 | }); 14 | 15 | export default () => ( 16 | 17 | 22 | 23 | 24 | 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /example/src/tests/SlideFromRightIOS.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { createSharedElementStackNavigator } from "react-navigation-shared-element/build/v4"; 3 | import { TransitionPresets } from "react-navigation-stack"; 4 | 5 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 6 | 7 | const name = "SlideFromRightIOS"; 8 | 9 | const StackNavigator = createSharedElementStackNavigator( 10 | { 11 | Main: createScreen(MainScreen, name), 12 | Detail: DetailScreen, 13 | }, 14 | { 15 | defaultNavigationOptions: { 16 | ...TransitionPresets[name], 17 | }, 18 | }, 19 | { 20 | name, 21 | debug: true, 22 | } 23 | ); 24 | 25 | export default createAppContainer(StackNavigator); 26 | -------------------------------------------------------------------------------- /example/src/tests/TextInputStack.v4.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, TextInput } from "react-native"; 3 | import { useSafeAreaInsets } from "react-native-safe-area-context"; 4 | import { createAppContainer } from "react-navigation"; 5 | import { 6 | SharedElement, 7 | createSharedElementStackNavigator, 8 | } from "react-navigation-shared-element/build/v4"; 9 | import { HeaderBackButton } from "react-navigation-stack"; 10 | 11 | const SearchInput = (props: React.ComponentProps) => ( 12 | 23 | ); 24 | 25 | const HomeScreen = ({ navigation }: any) => { 26 | return ( 27 | 28 | 29 | navigation.navigate("Search")} /> 30 | 31 | 32 | ); 33 | }; 34 | 35 | const SearchScreen = ({ navigation }: any) => { 36 | const { top: topInsect } = useSafeAreaInsets(); 37 | return ( 38 | 39 | { 41 | // buggy if we just goBack 42 | navigation.goBack(); 43 | 44 | // Hide keyboard and setTimeout to 45 | // wait for the keyboard to close 46 | // Keyboard.dismiss(); 47 | // setTimeout(() => navigation.goBack(), 500); 48 | }} 49 | /> 50 | 51 | 52 | 53 | 54 | ); 55 | }; 56 | 57 | SearchScreen.navigationOptions = { headerShown: false }; 58 | SearchScreen.sharedElements = () => [{ id: "searchBar" }]; 59 | 60 | const TextInputStackNavigator = createSharedElementStackNavigator( 61 | { 62 | Home: HomeScreen, 63 | Search: SearchScreen, 64 | }, 65 | undefined, 66 | { 67 | name: "TextInputStack", 68 | debug: true, 69 | } 70 | ); 71 | 72 | export default createAppContainer(TextInputStackNavigator); 73 | -------------------------------------------------------------------------------- /example/src/tests/WrongIds.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import * as React from "react"; 3 | import { createSharedElementStackNavigator } from "react-navigation-shared-element"; 4 | 5 | import { MainScreen, DetailScreen } from "../screens"; 6 | 7 | const name = "WrongIds"; 8 | 9 | const Stack = createSharedElementStackNavigator({ 10 | name, 11 | debug: true, 12 | }); 13 | 14 | export default () => ( 15 | 16 | 17 | 18 | { 22 | return [ 23 | { id: "unknownId1" }, 24 | { id: "unknownId2", animation: "move" }, 25 | { id: "unknownId3", animation: "fade" }, 26 | ]; 27 | }} 28 | /> 29 | 30 | 31 | ); 32 | -------------------------------------------------------------------------------- /example/src/tests/WrongIds.v4.tsx: -------------------------------------------------------------------------------- 1 | import { createAppContainer } from "react-navigation"; 2 | import { 3 | createSharedElementStackNavigator, 4 | SharedElementsComponentConfig, 5 | } from "react-navigation-shared-element/build/v4"; 6 | 7 | import { createScreen, MainScreen, DetailScreen } from "../screens"; 8 | 9 | const name = "WrongIds"; 10 | 11 | const sharedElements: SharedElementsComponentConfig = ( 12 | route, 13 | otherRoute, 14 | showing 15 | ) => { 16 | return [ 17 | { id: "unknownId1" }, 18 | { id: "unknownId2", animation: "move" }, 19 | { id: "unknownId3", animation: "fade" }, 20 | ]; 21 | }; 22 | 23 | const StackNavigator = createSharedElementStackNavigator( 24 | { 25 | Main: createScreen(MainScreen, name), 26 | Detail: createScreen(DetailScreen, undefined, sharedElements), 27 | }, 28 | undefined, 29 | { 30 | name, 31 | debug: true, 32 | } 33 | ); 34 | 35 | export default createAppContainer(StackNavigator); 36 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "jsx": "react-native", 5 | "lib": ["dom", "esnext"], 6 | "moduleResolution": "node", 7 | "noEmit": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true, 10 | "strict": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-navigation-shared-element", 3 | "version": "3.1.3", 4 | "description": "react-native-shared-element bindings for React Navigation", 5 | "main": "build/index.js", 6 | "types": "build/index.d.ts", 7 | "sideEffects": false, 8 | "scripts": { 9 | "build": "expo-module build", 10 | "clean": "expo-module clean", 11 | "lint": "expo-module lint", 12 | "test": "expo-module test", 13 | "prepare": "expo-module prepare", 14 | "prepublishOnly": "expo-module prepublishOnly", 15 | "expo-module": "expo-module", 16 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", 17 | "install:5": "yarn add --dev @react-navigation/bottom-tabs@5 @react-navigation/material-top-tabs@5 @react-navigation/native@5 @react-navigation/stack@5" 18 | }, 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/" 21 | }, 22 | "keywords": [ 23 | "react-navigation-shared-element", 24 | "navigation-shared-element", 25 | "navigation", 26 | "shared-element" 27 | ], 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/IjzerenHein/react-navigation-shared-element.git" 31 | }, 32 | "author": "IjzerenHein ", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/IjzerenHein/react-navigation-shared-element/issues" 36 | }, 37 | "homepage": "https://github.com/IjzerenHein/react-navigation-shared-element#readme", 38 | "files": [ 39 | "src/", 40 | "build/" 41 | ], 42 | "dependencies": { 43 | "hoist-non-react-statics": "^3.3.2" 44 | }, 45 | "devDependencies": { 46 | "@commitlint/config-conventional": "^15.0.0", 47 | "@react-navigation/bottom-tabs": "^6.0.9", 48 | "@react-navigation/material-top-tabs": "^6.0.6", 49 | "@react-navigation/native": "^6.0.6", 50 | "@react-navigation/stack": "^6.0.11", 51 | "@types/react": "^17.0.15", 52 | "@types/react-native": "^0.64.12", 53 | "commitlint": "^15.0.0", 54 | "conventional-changelog-cli": "^2.1.1", 55 | "eslint": "^7.32.0", 56 | "expo-module-scripts": "^2.0.0", 57 | "prettier": "^2.4.1", 58 | "react-native-shared-element": "^0.8.8", 59 | "react-navigation": "^4.4.4", 60 | "react-navigation-stack": "^2.10.4", 61 | "react-navigation-tabs": "^2.11.1" 62 | }, 63 | "peerDependencies": { 64 | "react": "*", 65 | "react-native": "*", 66 | "react-native-shared-element": "*" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /rnse-android.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/rnse-android.gif -------------------------------------------------------------------------------- /rnse-ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IjzerenHein/react-navigation-shared-element/e432c6cef59504d2f75eae11bc49eefa31c2bef8/rnse-ios.gif -------------------------------------------------------------------------------- /src/SharedElement.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | SharedElement as RawSharedElement, 4 | SharedElementProps as RawSharedElementProps, 5 | } from "react-native-shared-element"; 6 | 7 | import SharedElementSceneContext from "./SharedElementSceneContext"; 8 | import SharedElementSceneData from "./SharedElementSceneData"; 9 | import { SharedElementNode } from "./types"; 10 | // import invariant from '../utils/invariant'; 11 | 12 | export type SharedElementProps = Omit & { 13 | id: string; 14 | }; 15 | 16 | class SharedElement extends React.Component { 17 | private node: SharedElementNode | null = null; 18 | private sharedId: string = ""; 19 | private sceneData: SharedElementSceneData | null = null; 20 | 21 | constructor(props: SharedElementProps) { 22 | super(props); 23 | this.sharedId = props.id; 24 | } 25 | 26 | render() { 27 | const { 28 | id, //eslint-disable-line @typescript-eslint/no-unused-vars 29 | ...otherProps 30 | } = this.props; 31 | return ( 32 | 33 | {(sceneData) => { 34 | /*invariant( 35 | sceneData != null, 36 | 'The SharedElementSceneContext is not set, did you forget to wrap your scene component with `createSharedElementScene(..)`?' 37 | );*/ 38 | this.sceneData = sceneData; 39 | return ; 40 | }} 41 | 42 | ); 43 | } 44 | 45 | componentDidUpdate() { 46 | const { id } = this.props; 47 | if (this.sharedId !== id) { 48 | if (this.sceneData && this.sharedId && this.node) { 49 | this.sceneData.removeNode(this.sharedId, this.node); 50 | } 51 | this.sharedId = id; 52 | if (this.sceneData && this.sharedId && this.node) { 53 | this.sceneData.addNode(this.sharedId, this.node); 54 | } 55 | } 56 | } 57 | 58 | private onSetNode = (node: SharedElementNode | null) => { 59 | if (this.node === node) { 60 | return; 61 | } 62 | if (this.sceneData && this.node && this.sharedId) { 63 | this.sceneData.removeNode(this.sharedId, this.node); 64 | } 65 | this.node = node; 66 | if (this.sceneData && this.node && this.sharedId) { 67 | this.sceneData.addNode(this.sharedId, this.node); 68 | } 69 | this.node = node; 70 | }; 71 | } 72 | 73 | export default SharedElement; 74 | -------------------------------------------------------------------------------- /src/SharedElementCompatRouteProxy.ts: -------------------------------------------------------------------------------- 1 | import { SharedElementRoute, SharedElementCompatRoute } from "./types"; 2 | 3 | export class SharedElementCompatRouteProxy implements SharedElementCompatRoute { 4 | static isParamWarningSilenced = false; 5 | static isStateWarningSilenced = false; 6 | 7 | private route: SharedElementRoute; 8 | private deprecatedStateCache: any; 9 | 10 | constructor(route: SharedElementRoute) { 11 | this.route = route; 12 | } 13 | 14 | get key(): string { 15 | return this.route.key; 16 | } 17 | 18 | get name(): string { 19 | return this.route.name; 20 | } 21 | 22 | get params(): { [key: string]: any } { 23 | return this.route.params || {}; 24 | } 25 | 26 | // As of react-navigation-shared-element@3, the `sharedElements` function 27 | // receives a route rather than a navigator. In order to easy the code transition 28 | // both the `navigation` and `route` forms are supported. When using `navigation`. 29 | 30 | getParam(name: string): any { 31 | if (!SharedElementCompatRouteProxy.isParamWarningSilenced) { 32 | SharedElementCompatRouteProxy.isParamWarningSilenced = true; 33 | console.warn( 34 | 'SharedElementNavigation: `navigation.getParam() is deprecated, use `route.params` instead. See TODO"' 35 | ); 36 | } 37 | return this.params[name]; 38 | } 39 | 40 | get state(): any { 41 | if (!SharedElementCompatRouteProxy.isStateWarningSilenced) { 42 | SharedElementCompatRouteProxy.isStateWarningSilenced = true; 43 | console.warn( 44 | 'SharedElementNavigation: `navigation.state[key/routeName] is deprecated, use `route[key/name]` instead. See TODO"' 45 | ); 46 | } 47 | this.deprecatedStateCache = this.deprecatedStateCache || { 48 | key: this.key, 49 | routeName: this.name, 50 | params: this.params, 51 | }; 52 | return this.deprecatedStateCache; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/SharedElementFocusEvents.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | NavigationContext, 3 | StackNavigationState, 4 | } from "@react-navigation/native"; 5 | import * as React from "react"; 6 | 7 | import { EventEmitter } from "./utils/EventEmitter"; 8 | 9 | /** 10 | * A variation of useFocusEvents that uses a custom emitter 11 | * and emits events using useLayoutEffect instead of useEffect. 12 | * This enables shared element to respond to focus events in 13 | * a timely manner. 14 | * https://github.com/react-navigation/react-navigation/blob/master/packages/core/src/useFocusEvents.tsx 15 | */ 16 | export function useSharedElementFocusEvents< 17 | ParamList extends Record 18 | >({ 19 | state, 20 | emitter, 21 | }: { 22 | state: StackNavigationState; 23 | emitter: EventEmitter; 24 | }) { 25 | const navigation = React.useContext(NavigationContext); 26 | const lastFocusedKeyRef = React.useRef(); 27 | 28 | const currentFocusedKey = state.routes[state.index].key; 29 | 30 | // When the parent screen changes its focus state, we also need to change child's focus 31 | // Coz the child screen can't be focused if the parent screen is out of focus 32 | React.useLayoutEffect( 33 | () => 34 | navigation?.addListener("focus", () => { 35 | lastFocusedKeyRef.current = currentFocusedKey; 36 | emitter.emit("focus", currentFocusedKey); 37 | }), 38 | [currentFocusedKey, emitter, navigation] 39 | ); 40 | 41 | React.useLayoutEffect( 42 | () => 43 | navigation?.addListener("blur", () => { 44 | lastFocusedKeyRef.current = undefined; 45 | emitter.emit("blur", currentFocusedKey); 46 | }), 47 | [currentFocusedKey, emitter, navigation] 48 | ); 49 | 50 | React.useLayoutEffect(() => { 51 | const lastFocusedKey = lastFocusedKeyRef.current; 52 | 53 | lastFocusedKeyRef.current = currentFocusedKey; 54 | 55 | // We wouldn't have `lastFocusedKey` on initial mount 56 | // Fire focus event for the current route on mount if there's no parent navigator 57 | if (lastFocusedKey === undefined && !navigation) { 58 | emitter.emit("focus", currentFocusedKey); 59 | } 60 | 61 | // We should only emit events when the focused key changed and navigator is focused 62 | // When navigator is not focused, screens inside shouldn't receive focused status either 63 | if ( 64 | lastFocusedKey === currentFocusedKey || 65 | !(navigation ? navigation.isFocused() : true) 66 | ) { 67 | return; 68 | } 69 | 70 | if (lastFocusedKey === undefined) { 71 | // Only fire events after initial mount 72 | return; 73 | } 74 | 75 | emitter.emit("blur", lastFocusedKey); 76 | emitter.emit("focus", currentFocusedKey); 77 | }, [currentFocusedKey, emitter, navigation]); 78 | } 79 | -------------------------------------------------------------------------------- /src/SharedElementRendererContext.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { ISharedElementRendererData } from "./SharedElementRendererData"; 4 | 5 | const SharedElementRendererContext = 6 | React.createContext(null); 7 | 8 | export default SharedElementRendererContext; 9 | -------------------------------------------------------------------------------- /src/SharedElementRendererProxy.ts: -------------------------------------------------------------------------------- 1 | import { ISharedElementRendererData } from "./SharedElementRendererData"; 2 | import SharedElementSceneData, { 3 | SharedElementSceneEventType, 4 | } from "./SharedElementSceneData"; 5 | 6 | export class SharedElementRendererProxy implements ISharedElementRendererData { 7 | private data: ISharedElementRendererData | null = null; 8 | 9 | startTransition(closing: boolean, navigatorId: string, nestingDepth: number) { 10 | if (!this.data) { 11 | console.warn( 12 | "SharedElementRendererProxy.startTransition called before Proxy was initialized" 13 | ); 14 | return; 15 | } 16 | return this.data.startTransition(closing, navigatorId, nestingDepth); 17 | } 18 | 19 | endTransition(closing: boolean, navigatorId: string, nestingDepth: number) { 20 | if (!this.data) { 21 | console.warn( 22 | "SharedElementRendererProxy.endTransition called before Proxy was initialized" 23 | ); 24 | return; 25 | } 26 | return this.data.endTransition(closing, navigatorId, nestingDepth); 27 | } 28 | 29 | updateSceneState( 30 | scene: SharedElementSceneData, 31 | eventType: SharedElementSceneEventType 32 | ) { 33 | if (!this.data) { 34 | console.warn( 35 | "SharedElementRendererProxy.updateSceneState called before Proxy was initialized" 36 | ); 37 | return; 38 | } 39 | return this.data.updateSceneState(scene, eventType); 40 | } 41 | 42 | get source(): ISharedElementRendererData | null { 43 | return this.data; 44 | } 45 | 46 | set source(data: ISharedElementRendererData | null) { 47 | this.data = data; 48 | } 49 | 50 | get nestingDepth(): number { 51 | if (!this.data) { 52 | console.warn( 53 | "SharedElementRendererProxy.nestingDepth called before Proxy was initialized" 54 | ); 55 | return 0; 56 | } 57 | return this.data.nestingDepth + 1; 58 | } 59 | 60 | addDebugRef(): number { 61 | if (!this.data) { 62 | console.warn( 63 | "SharedElementRendererProxy.addDebugRef called before Proxy was initialized" 64 | ); 65 | return 0; 66 | } 67 | return this.data.addDebugRef(); 68 | } 69 | 70 | releaseDebugRef(): number { 71 | if (!this.data) { 72 | console.warn( 73 | "SharedElementRendererProxy.relaseDebugRef called before Proxy was initialized" 74 | ); 75 | return 0; 76 | } 77 | return this.data.releaseDebugRef(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/SharedElementRendererView.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import { SharedElementTransition } from "react-native-shared-element"; 4 | 5 | import SharedElementRendererData from "./SharedElementRendererData"; 6 | import { SharedElementEventSubscription } from "./types"; 7 | 8 | type PropsType = { 9 | rendererData: SharedElementRendererData; 10 | }; 11 | 12 | export default class SharedElementRendererView extends React.PureComponent { 13 | private subscription?: SharedElementEventSubscription; 14 | 15 | componentDidMount() { 16 | this.subscription = this.props.rendererData.addUpdateListener(() => { 17 | this.forceUpdate(); 18 | }); 19 | } 20 | 21 | componentWillUnmount() { 22 | if (this.subscription) { 23 | this.subscription(); 24 | this.subscription = undefined; 25 | } 26 | } 27 | 28 | render() { 29 | const transitions = this.props.rendererData.getTransitions(); 30 | // console.log('SharedElementRendererView.render: ', transitions); 31 | return ( 32 | 33 | {transitions.map( 34 | ( 35 | // @ts-ignore 36 | { key, ...props }, 37 | index 38 | ) => ( 39 | 40 | ) 41 | )} 42 | 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/SharedElementSceneContext.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import SharedElementSceneData from "./SharedElementSceneData"; 4 | 5 | const SharedElementSceneContext = 6 | React.createContext(null); 7 | 8 | export default SharedElementSceneContext; 9 | -------------------------------------------------------------------------------- /src/SharedElementSceneData.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SharedElementNode, 3 | SharedElementEventSubscription, 4 | SharedElementAnimatedValue, 5 | SharedElementRoute, 6 | SharedElementSceneComponent, 7 | SharedElementsComponentConfig, 8 | } from "./types"; 9 | 10 | export type SharedElementSceneUpdateHandlerEventType = 11 | | "ancestor" 12 | | "add" 13 | | "remove"; 14 | 15 | export type SharedElementSceneUpdateHandler = ( 16 | eventType: SharedElementSceneUpdateHandlerEventType, 17 | node: SharedElementNode | undefined, 18 | id: string 19 | ) => any; 20 | 21 | const INVERT_OPTIONS = { 22 | inputRange: [0, 1], 23 | outputRange: [1, 0], 24 | }; 25 | 26 | export type SharedElementSceneEventType = 27 | | "willFocus" 28 | | "didFocus" 29 | | "willBlur" 30 | | "didBlur"; 31 | 32 | export default class SharedElementSceneData { 33 | private updateSubscribers = new Set(); 34 | private ancestorNode?: SharedElementNode = undefined; 35 | private nodes: { 36 | [key: string]: SharedElementNode; 37 | } = {}; 38 | private animationContextValue: any; 39 | public readonly getSharedElements: () => SharedElementsComponentConfig | void; 40 | public readonly name: string; 41 | public readonly navigatorId: string; 42 | public readonly nestingDepth: number; 43 | public readonly debug: boolean; 44 | public readonly route: SharedElementRoute; 45 | 46 | constructor( 47 | Component: SharedElementSceneComponent, 48 | getSharedElements: () => SharedElementsComponentConfig | void, 49 | route: SharedElementRoute, 50 | navigatorId: string, 51 | nestingDepth: number, 52 | debug: boolean 53 | ) { 54 | this.getSharedElements = getSharedElements; 55 | this.route = route; 56 | this.navigatorId = navigatorId; 57 | this.nestingDepth = nestingDepth; 58 | this.debug = debug; 59 | this.name = 60 | Component.displayName || 61 | Component.name || 62 | (Component.constructor ? Component.constructor.name : undefined) || 63 | ""; 64 | } 65 | 66 | public updateRoute(route: SharedElementRoute) { 67 | if (route.key !== this.route.key) { 68 | throw new Error( 69 | "SharedElementNavigation: Integrity error, route key should never change" 70 | ); 71 | } 72 | // @ts-ignore 73 | this.route = route; 74 | } 75 | 76 | setAnimimationContextValue(value: any) { 77 | this.animationContextValue = value; 78 | } 79 | 80 | getAnimValue(closing: boolean): SharedElementAnimatedValue | undefined { 81 | const { animationContextValue } = this; 82 | if (!animationContextValue) return; 83 | const { progress } = animationContextValue.current; 84 | return closing ? progress.interpolate(INVERT_OPTIONS) : progress; 85 | } 86 | 87 | getAncestor(): SharedElementNode | undefined { 88 | return this.ancestorNode; 89 | } 90 | 91 | setAncestor(ancestorNode: SharedElementNode | null) { 92 | // console.log('SharedElementSceneData.setAncestor'); 93 | if (this.ancestorNode === ancestorNode) return; 94 | this.ancestorNode = ancestorNode || undefined; 95 | this.emitUpdateEvent("ancestor", this.ancestorNode, ""); 96 | } 97 | 98 | addNode(id: string, node: SharedElementNode): void { 99 | // console.log('SharedElementSceneData.addNode: ', id); 100 | this.nodes[id] = node; 101 | this.emitUpdateEvent("add", node, id); 102 | } 103 | 104 | removeNode(id: string, node: SharedElementNode): void { 105 | // console.log('SharedElementSceneData.removeNode: ', id); 106 | delete this.nodes[id]; 107 | this.emitUpdateEvent("remove", node, id); 108 | } 109 | 110 | getNode(id: string): SharedElementNode | undefined { 111 | return this.nodes[id]; 112 | } 113 | 114 | addUpdateListener( 115 | handler: SharedElementSceneUpdateHandler 116 | ): SharedElementEventSubscription { 117 | this.updateSubscribers.add(handler); 118 | return () => this.updateSubscribers.delete(handler); 119 | } 120 | 121 | private emitUpdateEvent( 122 | eventType: SharedElementSceneUpdateHandlerEventType, 123 | node: SharedElementNode | undefined, 124 | id: string 125 | ): void { 126 | this.updateSubscribers.forEach((handler) => handler(eventType, node, id)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | 3 | export { default as createSharedElementStackNavigator } from "./createSharedElementStackNavigator"; 4 | 5 | export { default as SharedElement } from "./SharedElement"; 6 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SharedElementNode, 3 | SharedElementAnimation, 4 | SharedElementResize, 5 | SharedElementAlign, 6 | SharedElementTransitionProps, 7 | } from "react-native-shared-element"; 8 | 9 | export { 10 | SharedElementNode, 11 | SharedElementAnimation, 12 | SharedElementTransitionProps, 13 | }; 14 | 15 | export type SharedElementEventSubscription = () => void; 16 | 17 | export type SharedElementStrictConfig = { 18 | readonly id: string; 19 | readonly otherId: string; 20 | readonly animation: SharedElementAnimation; 21 | readonly resize?: SharedElementResize; 22 | readonly align?: SharedElementAlign; 23 | readonly debug?: boolean; 24 | }; 25 | 26 | export type SharedElementsStrictConfig = SharedElementStrictConfig[]; 27 | 28 | export type SharedElementConfig = 29 | | { 30 | readonly id: string; 31 | readonly otherId?: string; 32 | readonly animation?: SharedElementAnimation; 33 | readonly resize?: SharedElementResize; 34 | readonly align?: SharedElementAlign; 35 | readonly debug?: boolean; 36 | } 37 | | string; 38 | 39 | export type SharedElementsConfig = SharedElementConfig[]; 40 | 41 | export type SharedElementAnimatedValue = any; 42 | 43 | export type SharedElementRoute = { 44 | key: string; 45 | name: string; 46 | params: { 47 | [key: string]: any; 48 | }; 49 | }; 50 | 51 | export interface SharedElementCompatRoute { 52 | /** 53 | * Key of the screen. 54 | */ 55 | readonly key: string; 56 | 57 | /** 58 | * Route name of this screen. 59 | */ 60 | readonly name: string; 61 | 62 | /** 63 | * Params for this route. 64 | */ 65 | readonly params: { 66 | [key: string]: any; 67 | }; 68 | 69 | /** 70 | * @deprecated 71 | * Gets the parameter by its name. 72 | */ 73 | getParam(name: string): any; 74 | 75 | /** 76 | * @deprecated 77 | * Gets the navigation state. 78 | */ 79 | readonly state: { 80 | readonly key: string; 81 | readonly routeName: string; 82 | readonly params: { 83 | [key: string]: any; 84 | }; 85 | }; 86 | } 87 | 88 | export type SharedElementsComponentConfig = ( 89 | route: SharedElementCompatRoute, 90 | otherRoute: SharedElementCompatRoute, 91 | showing: boolean 92 | ) => SharedElementsConfig | undefined; 93 | 94 | export type SharedElementSceneComponent

= React.ComponentType

& { 95 | sharedElements?: SharedElementsComponentConfig; 96 | }; 97 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SharedElementConfig, 3 | SharedElementsConfig, 4 | SharedElementStrictConfig, 5 | SharedElementsStrictConfig, 6 | } from "./types"; 7 | 8 | export function normalizeSharedElementConfig( 9 | sharedElementConfig: SharedElementConfig 10 | ): SharedElementStrictConfig { 11 | if (typeof sharedElementConfig === "string") { 12 | return { 13 | id: sharedElementConfig, 14 | otherId: sharedElementConfig, 15 | animation: "move", 16 | }; 17 | } else { 18 | const { id, otherId, animation, ...other } = sharedElementConfig; 19 | return { 20 | id, 21 | otherId: otherId || id, 22 | animation: animation || "move", 23 | ...other, 24 | }; 25 | } 26 | } 27 | 28 | export function normalizeSharedElementsConfig( 29 | sharedElementsConfig: SharedElementsConfig | undefined 30 | ): SharedElementsStrictConfig | null { 31 | if (!sharedElementsConfig || !sharedElementsConfig.length) return null; 32 | return sharedElementsConfig.map(normalizeSharedElementConfig); 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/EventEmitter.ts: -------------------------------------------------------------------------------- 1 | export class EventEmitter { 2 | private listeners: { 3 | [key: string]: ((any) => void)[]; 4 | } = {}; 5 | 6 | emit(name: string, e: any): void { 7 | if (this.listeners[name]) { 8 | this.listeners[name].forEach((callback) => callback(e)); 9 | } 10 | } 11 | 12 | addListener(name: string, callback: (any) => any): () => void { 13 | this.listeners[name] = this.listeners[name] || []; 14 | this.listeners[name].push(callback); 15 | return () => 16 | this.listeners[name].splice(this.listeners[name].indexOf(callback), 1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/v4/createSharedElementScene.tsx: -------------------------------------------------------------------------------- 1 | import hoistNonReactStatics from "hoist-non-react-statics"; 2 | import * as React from "react"; 3 | import { View, StyleSheet } from "react-native"; 4 | import { nodeFromRef } from "react-native-shared-element"; 5 | 6 | import { ISharedElementRendererData } from "../SharedElementRendererData"; 7 | import SharedElementSceneContext from "../SharedElementSceneContext"; 8 | import SharedElementSceneData from "../SharedElementSceneData"; 9 | import { SharedElementSceneComponent, SharedElementRoute } from "../types"; 10 | import { NavigationProp, Route } from "./types"; 11 | 12 | const styles = StyleSheet.create({ 13 | container: { 14 | flex: 1, 15 | }, 16 | }); 17 | 18 | type PropsType = { 19 | navigation: NavigationProp; 20 | }; 21 | 22 | function routeFromNavigation(navigation: any): SharedElementRoute { 23 | return { 24 | key: navigation.state.key, 25 | name: navigation.state.routeName, 26 | params: navigation.state.params || {}, 27 | }; 28 | } 29 | 30 | export function getActiveRouteState(route: any): Route { 31 | if ( 32 | !route.routes || 33 | route.routes.length === 0 || 34 | route.index >= route.routes.length 35 | ) { 36 | return route; 37 | } else { 38 | return getActiveRouteState(route.routes[route.index]); 39 | } 40 | } 41 | 42 | function createSharedElementScene( 43 | Component: SharedElementSceneComponent, 44 | rendererData: ISharedElementRendererData, 45 | AnimationContext: any, 46 | navigatorId: string, 47 | verbose: boolean 48 | ): React.ComponentType { 49 | class SharedElementSceneView extends React.PureComponent { 50 | private subscriptions: { 51 | [key: string]: { 52 | remove(): void; 53 | }; 54 | } = {}; 55 | private sceneData: SharedElementSceneData = new SharedElementSceneData( 56 | Component, 57 | () => Component.sharedElements, 58 | routeFromNavigation(this.props.navigation), 59 | navigatorId, 60 | rendererData.nestingDepth, 61 | verbose 62 | ); 63 | 64 | componentDidMount() { 65 | const { navigation } = this.props; 66 | this.subscriptions = { 67 | willFocus: navigation.addListener("willFocus", this.onWillFocus), 68 | didFocus: navigation.addListener("didFocus", this.onDidFocus), 69 | willBlur: navigation.addListener("willBlur", this.onWillBlur), 70 | }; 71 | } 72 | 73 | componentWillUnmount() { 74 | Object.values(this.subscriptions).forEach((subscription) => 75 | subscription.remove() 76 | ); 77 | } 78 | 79 | render() { 80 | // console.log('SharedElementSceneView.render'); 81 | return ( 82 | 83 | 88 | 89 | {this.onRenderAnimationContext} 90 | 91 | 92 | 93 | 94 | ); 95 | } 96 | 97 | private onRenderAnimationContext = (value: any) => { 98 | this.sceneData.setAnimimationContextValue(value); 99 | }; 100 | 101 | componentDidUpdate() { 102 | this.sceneData.updateRoute(routeFromNavigation(this.props.navigation)); 103 | } 104 | 105 | private onSetRef = (ref: any) => { 106 | this.sceneData.setAncestor(nodeFromRef(ref)); 107 | }; 108 | 109 | private onWillFocus = () => { 110 | const { navigation } = this.props; 111 | const activeRoute = getActiveRouteState(navigation.state); 112 | //console.log('onWillFocus: ', navigation.state, activeRoute); 113 | if (navigation.state.routeName === activeRoute.routeName) { 114 | this.sceneData.updateRoute(routeFromNavigation(navigation)); 115 | rendererData.updateSceneState(this.sceneData, "willFocus"); 116 | } 117 | }; 118 | 119 | private onDidFocus = () => { 120 | const { navigation } = this.props; 121 | const activeRoute = getActiveRouteState(navigation.state); 122 | if (navigation.state.routeName === activeRoute.routeName) { 123 | // console.log('onDidFocus: ', this.sceneData.name, navigation); 124 | this.sceneData.updateRoute(routeFromNavigation(navigation)); 125 | rendererData.updateSceneState(this.sceneData, "didFocus"); 126 | } 127 | }; 128 | 129 | private onWillBlur = () => { 130 | const { navigation } = this.props; 131 | const activeRoute = getActiveRouteState(navigation.state); 132 | //console.log('onWillBlur: ', navigation.state, activeRoute); 133 | if (navigation.state.routeName === activeRoute.routeName) { 134 | this.sceneData.updateRoute(routeFromNavigation(navigation)); 135 | rendererData.updateSceneState(this.sceneData, "willBlur"); 136 | } 137 | }; 138 | } 139 | 140 | hoistNonReactStatics(SharedElementSceneView, Component); 141 | return SharedElementSceneView; 142 | } 143 | 144 | export default createSharedElementScene; 145 | -------------------------------------------------------------------------------- /src/v4/createSharedElementStackNavigator.tsx: -------------------------------------------------------------------------------- 1 | import hoistNonReactStatics from "hoist-non-react-statics"; 2 | import * as React from "react"; 3 | import { Platform } from "react-native"; 4 | import { 5 | NavigationNavigator, 6 | NavigationProp, 7 | NavigationState, 8 | } from "react-navigation"; 9 | import { 10 | createStackNavigator, 11 | CardAnimationContext, 12 | } from "react-navigation-stack"; 13 | 14 | import SharedElementRendererContext from "../SharedElementRendererContext"; 15 | import SharedElementRendererData, { 16 | ISharedElementRendererData, 17 | } from "../SharedElementRendererData"; 18 | import { SharedElementRendererProxy } from "../SharedElementRendererProxy"; 19 | import SharedElementRendererView from "../SharedElementRendererView"; 20 | import createSharedElementScene from "./createSharedElementScene"; 21 | 22 | let _navigatorId = 1; 23 | 24 | function createSharedElementStackSceneNavigator( 25 | routeConfigs: Parameters[0], 26 | stackConfig: Parameters[1], 27 | rendererData: ISharedElementRendererData, 28 | navigatorId: string, 29 | debug: boolean 30 | ) { 31 | //console.log('createSharedElementStackSceneNavigator...', navigatorId); 32 | 33 | const wrappedRouteConfigs = { 34 | ...routeConfigs, 35 | }; 36 | for (const key in routeConfigs) { 37 | const routeConfig: any = wrappedRouteConfigs[key]; 38 | const component = 39 | typeof routeConfig === "object" && routeConfig.screen 40 | ? routeConfig.screen 41 | : routeConfig; 42 | const wrappedComponent = createSharedElementScene( 43 | component, 44 | rendererData, 45 | CardAnimationContext, 46 | navigatorId, 47 | debug 48 | ); 49 | if (component === routeConfig) { 50 | wrappedRouteConfigs[key] = wrappedComponent; 51 | } else { 52 | wrappedRouteConfigs[key] = { 53 | ...routeConfig, 54 | screen: wrappedComponent, 55 | }; 56 | } 57 | } 58 | 59 | // Override `onTransitionStart` and `onTransitionEnd` and 60 | // hook in into the transition lifecycle events. 61 | const defaultNavigationOptions = stackConfig?.defaultNavigationOptions; 62 | function defaultNavigationOptionsFn(props: any) { 63 | const defaultNavigationOptionsResult = 64 | typeof defaultNavigationOptions === "function" 65 | ? defaultNavigationOptions(props) 66 | : defaultNavigationOptions; 67 | return { 68 | ...defaultNavigationOptionsResult, 69 | onTransitionStart: (transitionProps: { closing: boolean }) => { 70 | rendererData.startTransition( 71 | transitionProps.closing, 72 | navigatorId, 73 | rendererData.nestingDepth 74 | ); 75 | defaultNavigationOptionsResult?.onTransitionStart?.(transitionProps); 76 | }, 77 | onTransitionEnd: (transitionProps: { closing: boolean }) => { 78 | rendererData.endTransition( 79 | transitionProps.closing, 80 | navigatorId, 81 | rendererData.nestingDepth 82 | ); 83 | defaultNavigationOptionsResult?.onTransitionEnd?.(transitionProps); 84 | }, 85 | }; 86 | } 87 | 88 | return createStackNavigator(wrappedRouteConfigs, { 89 | detachInactiveScreens: Platform.OS !== "android", 90 | ...stackConfig, 91 | defaultNavigationOptions: 92 | typeof defaultNavigationOptions === "function" 93 | ? defaultNavigationOptionsFn 94 | : defaultNavigationOptionsFn({}), 95 | }); 96 | } 97 | 98 | function createSharedElementStackNavigator( 99 | routeConfigs: Parameters[0], 100 | stackConfig: Parameters[1], 101 | options?: { 102 | name?: string; 103 | debug?: boolean; 104 | } 105 | ): NavigationNavigator> { 106 | const navigatorId = 107 | options && options.name ? options.name : `stack${_navigatorId}`; 108 | _navigatorId++; 109 | const debug = options?.debug || false; 110 | 111 | // Create a proxy which is later updated to link 112 | // to the renderer 113 | const rendererDataProxy = new SharedElementRendererProxy(); 114 | 115 | const SharedElementNavigator = createSharedElementStackSceneNavigator( 116 | routeConfigs, 117 | stackConfig, 118 | rendererDataProxy, 119 | navigatorId, 120 | debug 121 | ); 122 | 123 | class SharedElementRenderer extends React.Component { 124 | private rendererData?: SharedElementRendererData; 125 | 126 | componentDidMount() { 127 | if (debug) { 128 | rendererDataProxy.addDebugRef(); 129 | } 130 | } 131 | 132 | componentWillUnmount() { 133 | if (debug) { 134 | rendererDataProxy.releaseDebugRef(); 135 | } 136 | } 137 | 138 | render() { 139 | return ( 140 | 141 | {(rendererData) => { 142 | // In case a renderer is already present higher up in the chain 143 | // then don't bother creating a renderer here, but use that one instead 144 | if (!rendererData) { 145 | this.rendererData = 146 | this.rendererData || new SharedElementRendererData(); 147 | rendererDataProxy.source = this.rendererData; 148 | } else { 149 | rendererDataProxy.source = rendererData; 150 | } 151 | return ( 152 | 153 | 154 | {this.rendererData ? ( 155 | 156 | ) : undefined} 157 | 158 | ); 159 | }} 160 | 161 | ); 162 | } 163 | } 164 | hoistNonReactStatics(SharedElementRenderer, SharedElementNavigator); 165 | // @ts-ignore 166 | return SharedElementRenderer; 167 | } 168 | 169 | export default createSharedElementStackNavigator; 170 | -------------------------------------------------------------------------------- /src/v4/index.ts: -------------------------------------------------------------------------------- 1 | export * from "../types"; 2 | 3 | export { default as createSharedElementStackNavigator } from "./createSharedElementStackNavigator"; 4 | 5 | export { default as SharedElement } from "../SharedElement"; 6 | -------------------------------------------------------------------------------- /src/v4/types.ts: -------------------------------------------------------------------------------- 1 | export type Route = { 2 | key: string; 3 | routeName: string; 4 | }; 5 | 6 | export type NavigationEventName = 7 | | "willFocus" 8 | | "didFocus" 9 | | "willBlur" 10 | | "didBlur"; 11 | 12 | export type NavigationState = { 13 | key: string; 14 | index: number; 15 | routes: Route[]; 16 | routeName: string; 17 | transitions: { 18 | pushing: string[]; 19 | popping: string[]; 20 | }; 21 | params?: { [key: string]: unknown }; 22 | }; 23 | 24 | export type NavigationProp = { 25 | navigate(routeName: RouteName): void; 26 | goBack(): void; 27 | goBack(key: string | null): void; 28 | addListener: ( 29 | event: NavigationEventName, 30 | callback: () => void 31 | ) => { remove: () => void }; 32 | isFocused(): boolean; 33 | state: NavigationState; 34 | setParams(params: Params): void; 35 | getParam(): Params; 36 | dispatch(action: { type: string }): void; 37 | isFirstRouteInParent(): boolean; 38 | dangerouslyGetParent(): NavigationProp | undefined; 39 | }; 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | // @generated by expo-module-scripts 2 | { 3 | "extends": "expo-module-scripts/tsconfig.base", 4 | "compilerOptions": { 5 | "outDir": "./build" 6 | }, 7 | "include": ["./src"], 8 | "exclude": ["**/__mocks__/*", "**/__tests__/*"] 9 | } 10 | --------------------------------------------------------------------------------