diff --git a/.flowconfig b/.flowconfig index 3e5608c4e3bfc2..7eec6f4162328f 100644 --- a/.flowconfig +++ b/.flowconfig @@ -41,12 +41,12 @@ suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-1]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-1]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-3]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-3]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError unsafe.enable_getters_and_setters=true [version] -^0.41.0 +^0.43.1 diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index c7b878bad1c8c9..0911213387bfbd 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,29 +1,36 @@ Please read the following carefully before opening a new issue. +Your issue may be closed if it does not provide the information required by this template. -We use GitHub Issues for tracking bugs in React Native. +We use GitHub Issues exclusively for tracking bugs in React Native. -- If you have a non-bug question, ask on Stack Overflow: http://stackoverflow.com/questions/tagged/react-native +- If you have a question, ask on Stack Overflow: http://stackoverflow.com/questions/tagged/react-native - If you have a feature request, post it on Canny: https://react-native.canny.io/feature-requests -Your issue may be closed without explanation if it does not provide the information required by this template. +Make sure your issue reproduces on master. Your issue may already have been fixed! +If your issue is present in master and in the stable release, try to reproduce your bug on https://snack.expo.io/ +If you can't reproduce the bug on Snack, provide a sample project. +At the very least, provide an example of your code. ---- Please use this template, and delete everything above this line before submitting your issue --- +--- Delete everything above this line --- ### Description -[FILL THIS OUT: Explain what you did, what you expected to happen, and what actually happens.] +Explain what you did, what you expected to happen, and what actually happens. -### Reproduction +### Reproduction Steps and Sample Code + +List all the steps required to reproduce the issue you're reporting. These steps should be clear and concise. + + ***An example of your code is REQUIRED*** -[FILL THIS OUT: Try to reproduce your bug on rnplay.org and provide a link. If you can't reproduce the bug on rnplay.org, provide a sample project.] ### Solution -[FILL THIS OUT: What needs to be done to address this issue? Ideally, provide a pull request with a fix.] +What needs to be done to address this issue? Ideally, provide a pull request with a fix. ### Additional Information -* React Native version: [FILL THIS OUT: Does the bug reproduce on the latest RN release?] +* React Native version: [FILL THIS OUT: Be specific, filling out "latest" here is not enough.] * Platform: [FILL THIS OUT: iOS, Android, or both?] -* Operating System: [FILL THIS OUT: MacOS, Linux, or Windows?] +* Development Operating System: [FILL THIS OUT: Are you developing on MacOS, Linux, or Windows?] * Dev tools: [FILL THIS OUT: Xcode or Android Studio version, iOS or Android SDK version, if applicable] diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7da24f09442644..de18092adff89b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,19 +1,32 @@ -Thanks for submitting a pull request! Please provide enough information so that others can review your pull request: +Thanks for submitting a PR! Please read these instructions carefully: -> **Unless you are a React Native release maintainer and cherry-picking an *existing* commit into a current release, ensure your pull request is targeting the `master` React Native branch.** +- [ ] Explain the **motivation** for making this change. +- [ ] Provide a **test plan** demonstrating that the code is solid. +- [ ] Match the **code formatting** of the rest of the codebase. +- [ ] Target the `master` branch, NOT a "stable" branch. -Explain the **motivation** for making this change. What existing problem does the pull request solve? +## Motivation (required) -Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. +What existing problem does the pull request solve? -**Test plan (required)** +## Test Plan (required) -Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. +A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI or updates the website. See [What is a Test Plan?][1] to learn more. -Make sure tests pass on both Travis and Circle CI. +If you have added code that should be tested, add tests. -**Code formatting** +## Next Steps -Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide). +Sign the [CLA][2], if you haven't already. -For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#pull-requests). +Small pull requests are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. + +Make sure all **tests pass** on both [Travis][3] and [Circle CI][4]. PRs that break tests are unlikely to be merged. + +For more info, see the ["Pull Requests"][5] section of our "Contributing" guidelines. + +[1]: https://medium.com/@martinkonicek/what-is-a-test-plan-8bfc840ec171#.y9lcuqqi9 +[2]: https://code.facebook.com/cla +[3]: https://travis-ci.org/facebook/react-native +[4]: http://circleci.com/gh/facebook/react-native +[5]: https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#pull-requests diff --git a/.gitignore b/.gitignore index 1ecdbc1bd3fa7d..57b9cbcb1cadc1 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,5 @@ node_modules /ReactAndroid/src/androidTest/assets/AndroidTestBundle.js *.js.meta +/coverage /third-party diff --git a/.project b/.project new file mode 100644 index 00000000000000..3e7147e4fbcebd --- /dev/null +++ b/.project @@ -0,0 +1,12 @@ + + + react-native + Project react-native created by Buildship. + + + + + + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000000000..03931c0c1b825f --- /dev/null +++ b/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,3 @@ +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 diff --git a/.travis.yml b/.travis.yml index 5f0caaabc938ee..0cbf424aacd8f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ install: - $yarn install script: - - if [[ "$TEST_TYPE" = objc-ios ]]; then travis_retry travis_wait ./scripts/objc-test-ios.sh; fi + - if [[ "$TEST_TYPE" = objc-ios ]]; then travis_retry travis_wait ./scripts/objc-test-ios.sh test; fi - if [[ "$TEST_TYPE" = objc-tvos ]]; then travis_retry travis_wait ./scripts/objc-test-tvos.sh; fi - if [[ "$TEST_TYPE" = e2e-objc ]]; then node ./scripts/run-ci-e2e-tests.js --ios --js --retries 3; fi - if [[ ( "$TEST_TYPE" = podspecs ) && ( "$TRAVIS_PULL_REQUEST" = "false" ) ]]; then gem install cocoapods && ./scripts/process-podspecs.sh; fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53f7c5c889af66..59731909c8a3cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,29 +14,30 @@ We will do our best to keep `master` in good shape, with tests passing at all ti The core team will be monitoring for pull requests. When we get one, we'll run some Facebook-specific integration tests on it first. From here, we'll need to get another person to sign off on the changes and then merge the pull request. For API changes we may need to fix internal uses, which could cause some delay. We'll do our best to provide updates and feedback throughout the process. -**Please submit your pull request on the `master` branch**. If the fix is critical and should be included in a stable branch please mention it and it will be cherry picked into it. +**Please submit your pull request on the `master` branch**. If the fix is critical and should be included in a stable branch please mention it and it will be cherry picked into it by a project maintainer. *Before* submitting a pull request, please make sure the following is done… 1. Fork the repo and create your branch from `master`. -2. **Describe your test plan in your commit.** If you've added code that should be tested, add tests! -3. If you've changed APIs, update the documentation. -4. If you've updated the docs, verify the website locally and submit screenshots if applicable. - - ``` - $ cd website - $ npm install && npm start - go to: http://localhost:8079/react-native/index.html - ``` - -5. Add the copyright notice to the top of any new files you've added. -6. Ensure tests pass on Travis and Circle CI. -7. Make sure your code lints (`node linter.js `). -8. If you haven't already, sign the [CLA](https://code.facebook.com/cla). -9. Squash your commits (`git rebase -i`). - one intent alongs with one commit makes it clearer for people to review and easier to understand your intention - -Note: It is not necessary to keep clicking `Merge master to your branch` on PR page. You would want to merge master if there are conflicts or tests are failing. The facebook-bot ultimately squashes all commits to a single one before merging your PR. +2. **Describe your test plan in your commit.** + - If you've added code that should be tested, add tests! + - If you've changed APIs, update the documentation. + - If you've updated the docs, verify the website locally and submit screenshots if applicable. + + ``` + $ cd website + $ npm install && npm start + Open the following in your browser: http://localhost:8079/react-native/index.html + ``` + +3. Add the copyright notice to the top of any new files you've added. +4. Ensure tests pass on Travis and Circle CI. +5. Make sure your code lints (`node linter.js `). +6. If you haven't already, sign the [CLA](https://code.facebook.com/cla). +7. Squash your commits (`git rebase -i`). + One intent alongside one commit makes it clearer for people to review and easier to understand your intention. + +> **Note:** It is not necessary to keep clicking `Merge master to your branch` on the PR page. You would want to merge master if there are conflicts or tests are failing. The Facebook-GitHub-Bot ultimately squashes all commits to a single one before merging your PR. #### Copyright Notice for files @@ -69,7 +70,7 @@ We are using GitHub Issues for our public bugs. We keep a close eye on this and ### Reporting New Issues -The best way to get your bug fixed is to provide a reduced test case. Please provide either a public repository with a runnable example or a [React Native Playground](https://rnplay.org/) snippet. +The best way to get your bug fixed is to provide a reduced test case. Please provide either a public repository with a runnable example or a [Sketch](https://sketch.expo.io/). ### Security Bugs @@ -77,8 +78,8 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe ## How to Get in Touch -* [Facebook group](https://www.facebook.com/groups/react.native.community/) -* Reactiflux — [#react-native](http://join.reactiflux.com/) +* [Facebook](https://www.facebook.com/groups/react.native.community/) +* [Twitter](https://www.twitter.com/reactnative) ## Style Guide diff --git a/ContainerShip/Dockerfile.android b/ContainerShip/Dockerfile.android index 9feadf159c0d09..63bfd8a46e563f 100644 --- a/ContainerShip/Dockerfile.android +++ b/ContainerShip/Dockerfile.android @@ -33,7 +33,7 @@ ADD build.gradle /app/build.gradle ADD react.gradle /app/react.gradle # run gradle downloads -RUN ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog +RUN ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders # compile native libs with Gradle script, we need bridge for unit and integration tests RUN ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1 -Pcom.android.build.threadPoolSize=1 diff --git a/ContainerShip/Dockerfile.android-base b/ContainerShip/Dockerfile.android-base index 47475078b76014..b1897445f19fe0 100644 --- a/ContainerShip/Dockerfile.android-base +++ b/ContainerShip/Dockerfile.android-base @@ -2,7 +2,7 @@ FROM library/ubuntu:16.04 # set default build arguments ARG ANDROID_VERSION=25.2.3 -ARG BUCK_VERSION=2016.11.11.01 +ARG BUCK_VERSION=f3452a6a7ab15a60e94c962e686293acbe677473 ARG NDK_VERSION=10e ARG NODE_VERSION=6.2.0 ARG WATCHMAN_VERSION=4.7.0 @@ -30,7 +30,7 @@ RUN n $NODE_VERSION # download buck RUN git clone https://github.com/facebook/buck.git /opt/buck WORKDIR /opt/buck -RUN git checkout v$BUCK_VERSION +RUN git checkout $BUCK_VERSION # build buck RUN ant @@ -62,16 +62,33 @@ RUN unzip ndk.zip # cleanup NDK RUN rm ndk.zip -# add android SDK tools -# 2 - Android SDK Platform-tools, revision 25.0.3 -# 12 - Android SDK Build-tools, revision 23.0.1 -# 35 - SDK Platform Android 6.0, API 23, revision 3 -# 39 - SDK Platform Android 4.4.2, API 19, revision 4 -# 102 - ARM EABI v7a System Image, Android API 19, revision 5 -# 103 - Intel x86 Atom System Image, Android API 19, revision 5 -# 131 - Google APIs, Android API 23, revision 1 -# 166 - Android Support Repository, revision 41 -RUN echo "y" | android update sdk -u -a -t 2,12,35,39,102,103,131,166 +# Add android SDK tools + +# Android SDK Platform-tools, revision 25.0.4 +RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android SDK Platform-tools, revision 25.0.4" | awk '{ print $1 }' | sed 's/.$//') + +# Android SDK Build-tools, revision 23.0.1 +RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android SDK Build-tools, revision 23.0.1" | awk '{ print $1 }' | sed 's/.$//') + +# SDK Platform Android 6.0, API 23, revision 3 +RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "SDK Platform Android 6.0, API 23" | awk '{ print $1 }' | sed 's/.$//') + +# SDK Platform Android 4.4.2, API 19, revision 4 +RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "SDK Platform Android 4.4.2, API 19, revision 4" | awk '{ print $1 }' | sed 's/.$//') + +# ARM EABI v7a System Image, Android API 19, revision 5 +RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "ARM EABI v7a System Image, Android API 19, revision 5" | awk '{ print $1 }' | sed 's/.$//') + +# Intel x86 Atom System Image, Android API 19, revision 5 +RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Intel x86 Atom System Image, Android API 19, revision 5" | awk '{ print $1 }' | sed 's/.$//') + +# Google APIs, Android API 23, revision 1 +RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Google APIs, Android API 23, revision 1" | awk '{ print $1 }' | sed 's/.$//') + +# Android Support Repository, revision 45 +RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android Support Repository, revision 45" | awk '{ print $1 }' | sed 's/.$//') + +# Link adb executable RUN ln -s /opt/android/platform-tools/adb /usr/bin/adb # clean up unnecessary directories diff --git a/ContainerShip/scripts/run-android-ci-instrumentation-tests.js b/ContainerShip/scripts/run-android-ci-instrumentation-tests.js index fe29cbac1bec17..7017c021eb9049 100644 --- a/ContainerShip/scripts/run-android-ci-instrumentation-tests.js +++ b/ContainerShip/scripts/run-android-ci-instrumentation-tests.js @@ -28,6 +28,12 @@ const child_process = require('child_process'); const fs = require('fs'); const path = require('path'); +// Flaky tests ignored on Circle CI. They still run internally at fb. +const ignoredTests = [ + 'ReactScrollViewTestCase', + 'ReactHorizontalScrollViewTestCase' +]; + const colors = { GREEN: '\x1b[32m', RED: '\x1b[31m', @@ -53,6 +59,8 @@ let testClasses = fs.readdirSync(path.resolve(process.cwd(), test_opts.PATH)) return file.endsWith('.java'); }).map((clazz) => { return path.basename(clazz, '.java'); + }).filter(className => { + return ignoredTests.indexOf(className) === -1; }).map((clazz) => { return test_opts.PACKAGE + '.' + clazz; }).filter((clazz) => { @@ -112,7 +120,7 @@ return async.mapSeries(testClasses, (clazz, callback) => { print_test_suite_results(results); const failures = results.filter((test) => { - test.status === 'failure'; + return test.status === 'failure'; }); return failures.length === 0 ? process.exit(0) : process.exit(1); diff --git a/ContainerShip/scripts/run-android-docker-instrumentation-tests.sh b/ContainerShip/scripts/run-android-docker-instrumentation-tests.sh index 9d3f768f4f9721..c7806c7f055aae 100644 --- a/ContainerShip/scripts/run-android-docker-instrumentation-tests.sh +++ b/ContainerShip/scripts/run-android-docker-instrumentation-tests.sh @@ -28,7 +28,8 @@ watchman shutdown-server node local-cli/cli.js bundle --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js # build test APK -buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1 +source ./scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1 # run installed apk with tests node ./ContainerShip/scripts/run-android-ci-instrumentation-tests.js $* +exit $? diff --git a/ContainerShip/scripts/run-ci-e2e-tests.sh b/ContainerShip/scripts/run-ci-e2e-tests.sh index e6739dfe9028a8..9416012dbe1ce4 100755 --- a/ContainerShip/scripts/run-ci-e2e-tests.sh +++ b/ContainerShip/scripts/run-ci-e2e-tests.sh @@ -11,7 +11,7 @@ RUN_CLI_INSTALL=1 RUN_IOS=0 RUN_JS=0 -RETRY_COUNT=${RETRY_COUNT:-3} +RETRY_COUNT=${RETRY_COUNT:-1} AVD_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1) ANDROID_NPM_DEPS="appium@1.5.1 mocha@2.4.5 wd@0.3.11 colors@1.0.3 pretty-data2@0.40.1" diff --git a/Examples/.project b/Examples/.project new file mode 100644 index 00000000000000..94ffe4c71bb673 --- /dev/null +++ b/Examples/.project @@ -0,0 +1,12 @@ + + + Examples + Project Examples created by Buildship. + + + + + + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/Examples/.settings/org.eclipse.buildship.core.prefs b/Examples/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000000000..360af3b9be76ee --- /dev/null +++ b/Examples/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,3 @@ +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir=.. +eclipse.preferences.version=1 diff --git a/Examples/2048/2048.xcodeproj/project.pbxproj b/Examples/2048/2048.xcodeproj/project.pbxproj deleted file mode 100644 index e463eeae8f18ee..00000000000000 --- a/Examples/2048/2048.xcodeproj/project.pbxproj +++ /dev/null @@ -1,489 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 1461632D1AC3E23900C2F5AD /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1461632C1AC3E22900C2F5AD /* libReact.a */; }; - 5F82F1781B85785200FAE87E /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F82F1771B85784500FAE87E /* libRCTWebSocket.a */; }; - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; - E730D7501D4EE4E2000B7DA8 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E730D74C1D4EE4D0000B7DA8 /* libRCTNetwork.a */; }; - E730D7571D4EE538000B7DA8 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E730D7561D4EE534000B7DA8 /* libRCTImage.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 1461632B1AC3E22900C2F5AD /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146163271AC3E22900C2F5AD /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = React; - }; - 5F82F1761B85784500FAE87E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 5F82F1721B85784500FAE87E /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; - 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5119B1A9E6C1200147676; - remoteInfo = RCTText; - }; - E730D74B1D4EE4D0000B7DA8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = E730D7471D4EE4D0000B7DA8 /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTNetwork; - }; - E730D7551D4EE534000B7DA8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = E730D7511D4EE534000B7DA8 /* RCTImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 13B07F961A680F5B00A75B9A /* 2048.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = 2048.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = 2048/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = 2048/AppDelegate.m; sourceTree = ""; }; - 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = 2048/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = 2048/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = 2048/main.m; sourceTree = ""; }; - 146163271AC3E22900C2F5AD /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = ""; }; - 5F82F1721B85784500FAE87E /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = ""; }; - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; - E730D7471D4EE4D0000B7DA8 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; - E730D7511D4EE534000B7DA8 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - E730D7571D4EE538000B7DA8 /* libRCTImage.a in Frameworks */, - E730D7501D4EE4E2000B7DA8 /* libRCTNetwork.a in Frameworks */, - 5F82F1781B85785200FAE87E /* libRCTWebSocket.a in Frameworks */, - 1461632D1AC3E23900C2F5AD /* libReact.a in Frameworks */, - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 13B07FAE1A68108700A75B9A /* 2048 */ = { - isa = PBXGroup; - children = ( - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13B07FB71A68108700A75B9A /* main.m */, - ); - name = 2048; - sourceTree = ""; - }; - 146163281AC3E22900C2F5AD /* Products */ = { - isa = PBXGroup; - children = ( - 1461632C1AC3E22900C2F5AD /* libReact.a */, - ); - name = Products; - sourceTree = ""; - }; - 5F82F1731B85784500FAE87E /* Products */ = { - isa = PBXGroup; - children = ( - 5F82F1771B85784500FAE87E /* libRCTWebSocket.a */, - ); - name = Products; - sourceTree = ""; - }; - 832341AE1AAA6A7D00B99B32 /* Libraries */ = { - isa = PBXGroup; - children = ( - E730D7511D4EE534000B7DA8 /* RCTImage.xcodeproj */, - E730D7471D4EE4D0000B7DA8 /* RCTNetwork.xcodeproj */, - 5F82F1721B85784500FAE87E /* RCTWebSocket.xcodeproj */, - 146163271AC3E22900C2F5AD /* React.xcodeproj */, - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, - ); - name = Libraries; - sourceTree = ""; - }; - 832341B11AAA6A8300B99B32 /* Products */ = { - isa = PBXGroup; - children = ( - 832341B51AAA6A8300B99B32 /* libRCTText.a */, - ); - name = Products; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 13B07FAE1A68108700A75B9A /* 2048 */, - 832341AE1AAA6A7D00B99B32 /* Libraries */, - 83CBBA001A601CBA00E9B192 /* Products */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* 2048.app */, - ); - name = Products; - sourceTree = ""; - }; - E730D7481D4EE4D0000B7DA8 /* Products */ = { - isa = PBXGroup; - children = ( - E730D74C1D4EE4D0000B7DA8 /* libRCTNetwork.a */, - ); - name = Products; - sourceTree = ""; - }; - E730D7521D4EE534000B7DA8 /* Products */ = { - isa = PBXGroup; - children = ( - E730D7561D4EE534000B7DA8 /* libRCTImage.a */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 13B07F861A680F5B00A75B9A /* 2048 */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "2048" */; - buildPhases = ( - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 68ACCCD51D2BE2D2008E368A /* Run Script */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = 2048; - productName = "Hello World"; - productReference = 13B07F961A680F5B00A75B9A /* 2048.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0700; - ORGANIZATIONNAME = Facebook; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "2048" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = E730D7521D4EE534000B7DA8 /* Products */; - ProjectRef = E730D7511D4EE534000B7DA8 /* RCTImage.xcodeproj */; - }, - { - ProductGroup = E730D7481D4EE4D0000B7DA8 /* Products */; - ProjectRef = E730D7471D4EE4D0000B7DA8 /* RCTNetwork.xcodeproj */; - }, - { - ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; - ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - }, - { - ProductGroup = 5F82F1731B85784500FAE87E /* Products */; - ProjectRef = 5F82F1721B85784500FAE87E /* RCTWebSocket.xcodeproj */; - }, - { - ProductGroup = 146163281AC3E22900C2F5AD /* Products */; - ProjectRef = 146163271AC3E22900C2F5AD /* React.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* 2048 */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 1461632C1AC3E22900C2F5AD /* libReact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReact.a; - remoteRef = 1461632B1AC3E22900C2F5AD /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 5F82F1771B85784500FAE87E /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 5F82F1761B85784500FAE87E /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTText.a; - remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - E730D74C1D4EE4D0000B7DA8 /* libRCTNetwork.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTNetwork.a; - remoteRef = E730D74B1D4EE4D0000B7DA8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - E730D7561D4EE534000B7DA8 /* libRCTImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTImage.a; - remoteRef = E730D7551D4EE534000B7DA8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 68ACCCD51D2BE2D2008E368A /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n$SRCROOT/../../packager/react-native-xcode.sh Examples/2048/Game2048.js\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 13B07FB21A68108700A75B9A /* Base */, - ); - name = LaunchScreen.xib; - path = 2048; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - INFOPLIST_FILE = "$(SRCROOT)/2048/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(inherited)"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = 2048; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - INFOPLIST_FILE = "$(SRCROOT)/2048/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(inherited)"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = 2048; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "2048" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "2048" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/Examples/2048/2048/AppDelegate.h b/Examples/2048/2048/AppDelegate.h deleted file mode 100644 index 1a4317567ce256..00000000000000 --- a/Examples/2048/2048/AppDelegate.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -@interface AppDelegate : UIResponder - -@property (nonatomic, strong) UIWindow *window; - -@end diff --git a/Examples/2048/2048/AppDelegate.m b/Examples/2048/2048/AppDelegate.m deleted file mode 100644 index 9e3c01b8111ea0..00000000000000 --- a/Examples/2048/2048/AppDelegate.m +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import "AppDelegate.h" - -#import "RCTBundleURLProvider.h" -#import "RCTRootView.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"Examples/2048/Game2048" fallbackResource:nil]; - - RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"Game2048" - initialProperties:nil - launchOptions:launchOptions]; - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [UIViewController new]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - return YES; -} - -@end diff --git a/Examples/2048/2048/Base.lproj/LaunchScreen.xib b/Examples/2048/2048/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 351e21c59ccf27..00000000000000 --- a/Examples/2048/2048/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Examples/2048/2048/Images.xcassets/AppIcon.appiconset/Contents.json b/Examples/2048/2048/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 118c98f7461bf9..00000000000000 --- a/Examples/2048/2048/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/2048/2048/Info.plist b/Examples/2048/2048/Info.plist deleted file mode 100644 index 6105445463d64b..00000000000000 --- a/Examples/2048/2048/Info.plist +++ /dev/null @@ -1,43 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/Examples/2048/2048/main.m b/Examples/2048/2048/main.m deleted file mode 100644 index b2a6473023a8e2..00000000000000 --- a/Examples/2048/2048/main.m +++ /dev/null @@ -1,23 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/Examples/2048/Game2048.js b/Examples/2048/Game2048.js deleted file mode 100644 index c19e7cf2deb227..00000000000000 --- a/Examples/2048/Game2048.js +++ /dev/null @@ -1,325 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule Game2048 - * @flow - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - AppRegistry, - StyleSheet, - Text, - View, -} = ReactNative; - -var Animated = require('Animated'); -var GameBoard = require('GameBoard'); -var TouchableBounce = require('TouchableBounce'); - -var BOARD_PADDING = 3; -var CELL_MARGIN = 4; -var CELL_SIZE = 60; - -class Cell extends React.Component { - render() { - return ; - } -} - -class Board extends React.Component { - render() { - return ( - - - - - - {this.props.children} - - ); - } -} - -class Tile extends React.Component { - state: any; - - static _getPosition(index): number { - return BOARD_PADDING + (index * (CELL_SIZE + CELL_MARGIN * 2) + CELL_MARGIN); - } - - constructor(props: {}) { - super(props); - - var tile = this.props.tile; - - this.state = { - opacity: new Animated.Value(0), - top: new Animated.Value(Tile._getPosition(tile.toRow())), - left: new Animated.Value(Tile._getPosition(tile.toColumn())), - }; - } - - calculateOffset(): {top: number; left: number; opacity: number} { - var tile = this.props.tile; - - var offset = { - top: this.state.top, - left: this.state.left, - opacity: this.state.opacity, - }; - - if (tile.isNew()) { - Animated.timing(this.state.opacity, { - duration: 100, - toValue: 1, - }).start(); - } else { - Animated.parallel([ - Animated.timing(offset.top, { - duration: 100, - toValue: Tile._getPosition(tile.toRow()), - }), - Animated.timing(offset.left, { - duration: 100, - toValue: Tile._getPosition(tile.toColumn()), - }), - ]).start(); - } - return offset; - } - - render() { - var tile = this.props.tile; - - var tileStyles = [ - styles.tile, - styles['tile' + tile.value], - this.calculateOffset(), - ]; - - var textStyles = [ - styles.value, - tile.value > 4 && styles.whiteText, - tile.value > 100 && styles.threeDigits, - tile.value > 1000 && styles.fourDigits, - ]; - - return ( - - {tile.value} - - ); - } -} - -class GameEndOverlay extends React.Component { - render() { - var board = this.props.board; - - if (!board.hasWon() && !board.hasLost()) { - return ; - } - - var message = board.hasWon() ? - 'Good Job!' : 'Game Over'; - - return ( - - {message} - - Try Again? - - - ); - } -} - -class Game2048 extends React.Component { - startX: number; - startY: number; - state: any; - - constructor(props: {}) { - super(props); - this.state = { - board: new GameBoard(), - }; - this.startX = 0; - this.startY = 0; - } - - restartGame() { - this.setState({board: new GameBoard()}); - } - - handleTouchStart(event: Object) { - if (this.state.board.hasWon()) { - return; - } - - this.startX = event.nativeEvent.pageX; - this.startY = event.nativeEvent.pageY; - } - - handleTouchEnd(event: Object) { - if (this.state.board.hasWon()) { - return; - } - - var deltaX = event.nativeEvent.pageX - this.startX; - var deltaY = event.nativeEvent.pageY - this.startY; - - var direction = -1; - if (Math.abs(deltaX) > 3 * Math.abs(deltaY) && Math.abs(deltaX) > 30) { - direction = deltaX > 0 ? 2 : 0; - } else if (Math.abs(deltaY) > 3 * Math.abs(deltaX) && Math.abs(deltaY) > 30) { - direction = deltaY > 0 ? 3 : 1; - } - - if (direction !== -1) { - this.setState({board: this.state.board.move(direction)}); - } - } - - render() { - var tiles = this.state.board.tiles - .filter((tile) => tile.value) - .map((tile) => ); - - return ( - this.handleTouchStart(event)} - onTouchEnd={(event) => this.handleTouchEnd(event)}> - - {tiles} - - this.restartGame()} /> - - ); - } -} - -var styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - board: { - padding: BOARD_PADDING, - backgroundColor: '#bbaaaa', - borderRadius: 5, - }, - overlay: { - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - backgroundColor: 'rgba(221, 221, 221, 0.5)', - flex: 1, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - }, - overlayMessage: { - fontSize: 40, - marginBottom: 20, - }, - tryAgain: { - backgroundColor: '#887761', - padding: 20, - borderRadius: 5, - }, - tryAgainText: { - color: '#ffffff', - fontSize: 20, - fontWeight: '500', - }, - cell: { - width: CELL_SIZE, - height: CELL_SIZE, - borderRadius: 5, - backgroundColor: '#ddccbb', - margin: CELL_MARGIN, - }, - row: { - flexDirection: 'row', - }, - tile: { - position: 'absolute', - width: CELL_SIZE, - height: CELL_SIZE, - backgroundColor: '#ddccbb', - borderRadius: 5, - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - value: { - fontSize: 24, - color: '#776666', - fontFamily: 'Verdana', - fontWeight: '500', - }, - tile2: { - backgroundColor: '#eeeeee', - }, - tile4: { - backgroundColor: '#eeeecc', - }, - tile8: { - backgroundColor: '#ffbb87', - }, - tile16: { - backgroundColor: '#ff9966', - }, - tile32: { - backgroundColor: '#ff7755', - }, - tile64: { - backgroundColor: '#ff5533', - }, - tile128: { - backgroundColor: '#eecc77', - }, - tile256: { - backgroundColor: '#eecc66', - }, - tile512: { - backgroundColor: '#eecc55', - }, - tile1024: { - backgroundColor: '#eecc33', - }, - tile2048: { - backgroundColor: '#eecc22', - }, - whiteText: { - color: '#ffffff', - }, - threeDigits: { - fontSize: 20, - }, - fourDigits: { - fontSize: 18, - }, -}); - -AppRegistry.registerComponent('Game2048', () => Game2048); - -module.exports = Game2048; diff --git a/Examples/2048/GameBoard.js b/Examples/2048/GameBoard.js deleted file mode 100644 index 6ed92e6b578ddf..00000000000000 --- a/Examples/2048/GameBoard.js +++ /dev/null @@ -1,201 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule GameBoard - * @flow - */ -'use strict'; - -// NB: Taken straight from: https://github.com/IvanVergiliev/2048-react/blob/master/src/board.js -// with no modification except to format it for CommonJS and fix lint/flow errors - -var rotateLeft = function (matrix) { - var rows = matrix.length; - var columns = matrix[0].length; - var res = []; - for (var row = 0; row < rows; ++row) { - res.push([]); - for (var column = 0; column < columns; ++column) { - res[row][column] = matrix[column][columns - row - 1]; - } - } - return res; -}; - -var Tile = function (value?: number, row?: number, column?: number) { - this.value = value || 0; - this.row = row || -1; - - this.column = column || -1; - this.oldRow = -1; - this.oldColumn = -1; - this.markForDeletion = false; - this.mergedInto = null; - this.id = Tile.id++; -}; - -Tile.id = 0; - -Tile.prototype.moveTo = function (row, column) { - this.oldRow = this.row; - this.oldColumn = this.column; - this.row = row; - this.column = column; -}; - -Tile.prototype.isNew = function () { - return this.oldRow === -1 && !this.mergedInto; -}; - -Tile.prototype.hasMoved = function () { - return (this.fromRow() !== -1 && (this.fromRow() !== this.toRow() || this.fromColumn() !== this.toColumn())) || - this.mergedInto; -}; - -Tile.prototype.fromRow = function () { - return this.mergedInto ? this.row : this.oldRow; -}; - -Tile.prototype.fromColumn = function () { - return this.mergedInto ? this.column : this.oldColumn; -}; - -Tile.prototype.toRow = function () { - return this.mergedInto ? this.mergedInto.row : this.row; -}; - -Tile.prototype.toColumn = function () { - return this.mergedInto ? this.mergedInto.column : this.column; -}; - -var Board = function () { - this.tiles = []; - this.cells = []; - for (var i = 0; i < Board.size; ++i) { - this.cells[i] = [this.addTile(), this.addTile(), this.addTile(), this.addTile()]; - } - this.addRandomTile(); - this.setPositions(); - this.won = false; -}; - -Board.prototype.addTile = function () { - var res = new Tile(); - Tile.apply(res, arguments); - this.tiles.push(res); - return res; -}; - -Board.size = 4; - -Board.prototype.moveLeft = function () { - var hasChanged = false; - for (var row = 0; row < Board.size; ++row) { - var currentRow = this.cells[row].filter(function (tile) { return tile.value !== 0; }); - var resultRow = []; - for (var target = 0; target < Board.size; ++target) { - var targetTile = currentRow.length ? currentRow.shift() : this.addTile(); - if (currentRow.length > 0 && currentRow[0].value === targetTile.value) { - var tile1 = targetTile; - targetTile = this.addTile(targetTile.value); - tile1.mergedInto = targetTile; - var tile2 = currentRow.shift(); - tile2.mergedInto = targetTile; - targetTile.value += tile2.value; - } - resultRow[target] = targetTile; - this.won = this.won || (targetTile.value === 2048); - hasChanged = hasChanged || (targetTile.value !== this.cells[row][target].value); - } - this.cells[row] = resultRow; - } - return hasChanged; -}; - -Board.prototype.setPositions = function () { - this.cells.forEach(function (row, rowIndex) { - row.forEach(function (tile, columnIndex) { - tile.oldRow = tile.row; - tile.oldColumn = tile.column; - tile.row = rowIndex; - tile.column = columnIndex; - tile.markForDeletion = false; - }); - }); -}; - -Board.fourProbability = 0.1; - -Board.prototype.addRandomTile = function () { - var emptyCells = []; - for (var r = 0; r < Board.size; ++r) { - for (var c = 0; c < Board.size; ++c) { - if (this.cells[r][c].value === 0) { - emptyCells.push({r: r, c: c}); - } - } - } - var index = Math.floor(Math.random() * emptyCells.length); - var cell = emptyCells[index]; - var newValue = Math.random() < Board.fourProbability ? 4 : 2; - this.cells[cell.r][cell.c] = this.addTile(newValue); -}; - -Board.prototype.move = function (direction) { - // 0 -> left, 1 -> up, 2 -> right, 3 -> down - this.clearOldTiles(); - for (var i = 0; i < direction; ++i) { - this.cells = rotateLeft(this.cells); - } - var hasChanged = this.moveLeft(); - for (var i = direction; i < 4; ++i) { - this.cells = rotateLeft(this.cells); - } - if (hasChanged) { - this.addRandomTile(); - } - this.setPositions(); - return this; -}; - -Board.prototype.clearOldTiles = function () { - this.tiles = this.tiles.filter(function (tile) { return tile.markForDeletion === false; }); - this.tiles.forEach(function (tile) { tile.markForDeletion = true; }); -}; - -Board.prototype.hasWon = function () { - return this.won; -}; - -Board.deltaX = [-1, 0, 1, 0]; -Board.deltaY = [0, -1, 0, 1]; - -Board.prototype.hasLost = function () { - var canMove = false; - for (var row = 0; row < Board.size; ++row) { - for (var column = 0; column < Board.size; ++column) { - canMove = canMove || (this.cells[row][column].value === 0); - for (var dir = 0; dir < 4; ++dir) { - var newRow = row + Board.deltaX[dir]; - var newColumn = column + Board.deltaY[dir]; - if (newRow < 0 || newRow >= Board.size || newColumn < 0 || newColumn >= Board.size) { - continue; - } - canMove = canMove || (this.cells[row][column].value === this.cells[newRow][newColumn].value); - } - } - } - return !canMove; -}; - -module.exports = Board; diff --git a/Examples/Movies/MovieCell.js b/Examples/Movies/MovieCell.js deleted file mode 100644 index d67c777eb56856..00000000000000 --- a/Examples/Movies/MovieCell.js +++ /dev/null @@ -1,105 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @flow - * @providesModule MovieCell - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - Image, - Platform, - StyleSheet, - Text, - TouchableHighlight, - TouchableNativeFeedback, - View -} = ReactNative; - -var getStyleFromScore = require('./getStyleFromScore'); -var getImageSource = require('./getImageSource'); -var getTextFromScore = require('./getTextFromScore'); - -class MovieCell extends React.Component { - render() { - var criticsScore = this.props.movie.ratings.critics_score; - var TouchableElement = TouchableHighlight; - if (Platform.OS === 'android') { - TouchableElement = TouchableNativeFeedback; - } - return ( - - - - - - - {this.props.movie.title} - - - {this.props.movie.year} - {' '}•{' '} - - Critics {getTextFromScore(criticsScore)} - - - - - - - ); - } -} - -var styles = StyleSheet.create({ - textContainer: { - flex: 1, - }, - movieTitle: { - flex: 1, - fontSize: 16, - fontWeight: '500', - marginBottom: 2, - }, - movieYear: { - color: '#999999', - fontSize: 12, - }, - row: { - alignItems: 'center', - backgroundColor: 'white', - flexDirection: 'row', - padding: 5, - }, - cellImage: { - backgroundColor: '#dddddd', - height: 93, - marginRight: 10, - width: 60, - }, - cellBorder: { - backgroundColor: 'rgba(0, 0, 0, 0.1)', - height: StyleSheet.hairlineWidth, - marginLeft: 4, - }, -}); - -module.exports = MovieCell; diff --git a/Examples/Movies/MovieScreen.js b/Examples/Movies/MovieScreen.js deleted file mode 100644 index 2f013f08ed8db5..00000000000000 --- a/Examples/Movies/MovieScreen.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @flow - * @providesModule MovieScreen - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - Image, - ScrollView, - StyleSheet, - Text, - View, -} = ReactNative; - -var getImageSource = require('./getImageSource'); -var getStyleFromScore = require('./getStyleFromScore'); -var getTextFromScore = require('./getTextFromScore'); - -class MovieScreen extends React.Component { - render() { - return ( - - - - - {this.props.movie.title} - {this.props.movie.year} - - - {this.props.movie.mpaa_rating} - - - - - - - - {this.props.movie.synopsis} - - - - - ); - } -} - -class Ratings extends React.Component { - render() { - var criticsScore = this.props.ratings.critics_score; - var audienceScore = this.props.ratings.audience_score; - - return ( - - - Critics: - - {getTextFromScore(criticsScore)} - - - - Audience: - - {getTextFromScore(audienceScore)} - - - - ); - } -} - -class Cast extends React.Component { - render() { - if (!this.props.actors) { - return null; - } - - return ( - - Actors - {this.props.actors.map(actor => - - • {actor.name} - - )} - - ); - } -} - -var styles = StyleSheet.create({ - contentContainer: { - padding: 10, - }, - rightPane: { - justifyContent: 'space-between', - flex: 1, - }, - movieTitle: { - flex: 1, - fontSize: 16, - fontWeight: '500', - }, - rating: { - marginTop: 10, - }, - ratingTitle: { - fontSize: 14, - }, - ratingValue: { - fontSize: 28, - fontWeight: '500', - }, - mpaaWrapper: { - alignSelf: 'flex-start', - borderColor: 'black', - borderWidth: 1, - paddingHorizontal: 3, - marginVertical: 5, - }, - mpaaText: { - fontFamily: 'Palatino', - fontSize: 13, - fontWeight: '500', - }, - mainSection: { - flexDirection: 'row', - }, - detailsImage: { - width: 134, - height: 200, - backgroundColor: '#eaeaea', - marginRight: 10, - }, - separator: { - backgroundColor: 'rgba(0, 0, 0, 0.1)', - height: StyleSheet.hairlineWidth, - marginVertical: 10, - }, - castTitle: { - fontWeight: '500', - marginBottom: 3, - }, - castActor: { - marginLeft: 2, - }, -}); - -module.exports = MovieScreen; diff --git a/Examples/Movies/Movies.xcodeproj/project.pbxproj b/Examples/Movies/Movies.xcodeproj/project.pbxproj deleted file mode 100644 index 9087fa7ac65910..00000000000000 --- a/Examples/Movies/Movies.xcodeproj/project.pbxproj +++ /dev/null @@ -1,520 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1341801E1AA91750003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341801B1AA91740003F314A /* libRCTNetwork.a */; }; - 13442C061AA90EA00037E5B0 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13442C051AA90E7D0037E5B0 /* libRCTImage.a */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 140D9B661AC36C42004F25EE /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14312D241AC3654D00CDC950 /* libRCTLinking.a */; }; - 14A2D4421AC3E43800CC738A /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14A2D4411AC3E41A00CC738A /* libReact.a */; }; - 58C5726B1AA6239E00CDF9C8 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C5725B1AA6236500CDF9C8 /* libRCTText.a */; }; - 67C95F201B0E64A30040BCE2 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 67C95F1E1B0E647A0040BCE2 /* libRCTWebSocket.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 1341801A1AA91740003F314A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 134180151AA91740003F314A /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTNetwork; - }; - 13442C041AA90E7D0037E5B0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; - 14312D231AC3654D00CDC950 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTLinking; - }; - 14A2D4401AC3E41A00CC738A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 14A2D43C1AC3E41A00CC738A /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = React; - }; - 58C5725A1AA6236500CDF9C8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5119B1A9E6C1200147676; - remoteInfo = RCTText; - }; - 67C95F1D1B0E647A0040BCE2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 67C95F151B0E647A0040BCE2 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 134180151AA91740003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; - 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; - 13B07F961A680F5B00A75B9A /* Movies.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Movies.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Movies/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Movies/AppDelegate.m; sourceTree = ""; }; - 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Movies/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Movies/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Movies/main.m; sourceTree = ""; }; - 14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; - 14A2D43C1AC3E41A00CC738A /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = ""; }; - 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = SOURCE_ROOT; }; - 67C95F151B0E647A0040BCE2 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 67C95F201B0E64A30040BCE2 /* libRCTWebSocket.a in Frameworks */, - 14A2D4421AC3E43800CC738A /* libReact.a in Frameworks */, - 140D9B661AC36C42004F25EE /* libRCTLinking.a in Frameworks */, - 1341801E1AA91750003F314A /* libRCTNetwork.a in Frameworks */, - 13442C061AA90EA00037E5B0 /* libRCTImage.a in Frameworks */, - 58C5726B1AA6239E00CDF9C8 /* libRCTText.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 134180161AA91740003F314A /* Products */ = { - isa = PBXGroup; - children = ( - 1341801B1AA91740003F314A /* libRCTNetwork.a */, - ); - name = Products; - sourceTree = ""; - }; - 13442C011AA90E7D0037E5B0 /* Products */ = { - isa = PBXGroup; - children = ( - 13442C051AA90E7D0037E5B0 /* libRCTImage.a */, - ); - name = Products; - sourceTree = ""; - }; - 13B07FAE1A68108700A75B9A /* Movies */ = { - isa = PBXGroup; - children = ( - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13B07FB71A68108700A75B9A /* main.m */, - ); - name = Movies; - sourceTree = ""; - }; - 14312D1F1AC3654D00CDC950 /* Products */ = { - isa = PBXGroup; - children = ( - 14312D241AC3654D00CDC950 /* libRCTLinking.a */, - ); - name = Products; - sourceTree = ""; - }; - 14A2D43D1AC3E41A00CC738A /* Products */ = { - isa = PBXGroup; - children = ( - 14A2D4411AC3E41A00CC738A /* libReact.a */, - ); - name = Products; - sourceTree = ""; - }; - 58C571FC1AA6124500CDF9C8 /* Libraries */ = { - isa = PBXGroup; - children = ( - 67C95F151B0E647A0040BCE2 /* RCTWebSocket.xcodeproj */, - 14A2D43C1AC3E41A00CC738A /* React.xcodeproj */, - 14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */, - 134180151AA91740003F314A /* RCTNetwork.xcodeproj */, - 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */, - 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */, - ); - name = Libraries; - sourceTree = ""; - }; - 58C572571AA6236500CDF9C8 /* Products */ = { - isa = PBXGroup; - children = ( - 58C5725B1AA6236500CDF9C8 /* libRCTText.a */, - ); - name = Products; - sourceTree = ""; - }; - 67C95F161B0E647A0040BCE2 /* Products */ = { - isa = PBXGroup; - children = ( - 67C95F1E1B0E647A0040BCE2 /* libRCTWebSocket.a */, - ); - name = Products; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 13B07FAE1A68108700A75B9A /* Movies */, - 58C571FC1AA6124500CDF9C8 /* Libraries */, - 83CBBA001A601CBA00E9B192 /* Products */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* Movies.app */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 13B07F861A680F5B00A75B9A /* Movies */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Movies" */; - buildPhases = ( - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 68ACCCEE1D2BE57F008E368A /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Movies; - productName = "Hello World"; - productReference = 13B07F961A680F5B00A75B9A /* Movies.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0700; - ORGANIZATIONNAME = Facebook; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Movies" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 13442C011AA90E7D0037E5B0 /* Products */; - ProjectRef = 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */; - }, - { - ProductGroup = 14312D1F1AC3654D00CDC950 /* Products */; - ProjectRef = 14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */; - }, - { - ProductGroup = 134180161AA91740003F314A /* Products */; - ProjectRef = 134180151AA91740003F314A /* RCTNetwork.xcodeproj */; - }, - { - ProductGroup = 58C572571AA6236500CDF9C8 /* Products */; - ProjectRef = 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */; - }, - { - ProductGroup = 67C95F161B0E647A0040BCE2 /* Products */; - ProjectRef = 67C95F151B0E647A0040BCE2 /* RCTWebSocket.xcodeproj */; - }, - { - ProductGroup = 14A2D43D1AC3E41A00CC738A /* Products */; - ProjectRef = 14A2D43C1AC3E41A00CC738A /* React.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* Movies */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 1341801B1AA91740003F314A /* libRCTNetwork.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTNetwork.a; - remoteRef = 1341801A1AA91740003F314A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 13442C051AA90E7D0037E5B0 /* libRCTImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTImage.a; - remoteRef = 13442C041AA90E7D0037E5B0 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 14312D241AC3654D00CDC950 /* libRCTLinking.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTLinking.a; - remoteRef = 14312D231AC3654D00CDC950 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 14A2D4411AC3E41A00CC738A /* libReact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReact.a; - remoteRef = 14A2D4401AC3E41A00CC738A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 58C5725B1AA6236500CDF9C8 /* libRCTText.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTText.a; - remoteRef = 58C5725A1AA6236500CDF9C8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 67C95F1E1B0E647A0040BCE2 /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 67C95F1D1B0E647A0040BCE2 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 68ACCCEE1D2BE57F008E368A /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n$SRCROOT/../../packager/react-native-xcode.sh Examples/Movies/MoviesApp.ios.js\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 13B07FB21A68108700A75B9A /* Base */, - ); - name = LaunchScreen.xib; - path = Movies; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - INFOPLIST_FILE = "$(SRCROOT)/Movies/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(inherited)"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = Movies; - USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**"; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - INFOPLIST_FILE = "$(SRCROOT)/Movies/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(inherited)"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = Movies; - USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**"; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Movies" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Movies" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/Examples/Movies/Movies/AppDelegate.h b/Examples/Movies/Movies/AppDelegate.h deleted file mode 100644 index 55c38cc0bb6b2d..00000000000000 --- a/Examples/Movies/Movies/AppDelegate.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -@interface AppDelegate : UIResponder - -@property (nonatomic, strong) UIWindow *window; - -@end - diff --git a/Examples/Movies/Movies/AppDelegate.m b/Examples/Movies/Movies/AppDelegate.m deleted file mode 100644 index 94f5d2ae94411f..00000000000000 --- a/Examples/Movies/Movies/AppDelegate.m +++ /dev/null @@ -1,51 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import "AppDelegate.h" - -#import "RCTBundleURLProvider.h" -#import "RCTLinkingManager.h" -#import "RCTRootView.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"Examples/Movies/MoviesApp.ios" fallbackResource:nil]; - - RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"MoviesApp" - initialProperties:nil - launchOptions:launchOptions]; - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [UIViewController new]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - return YES; -} - -- (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation -{ - return [RCTLinkingManager application:application - openURL:url - sourceApplication:sourceApplication - annotation:annotation]; -} - -@end diff --git a/Examples/Movies/Movies/Base.lproj/LaunchScreen.xib b/Examples/Movies/Movies/Base.lproj/LaunchScreen.xib deleted file mode 100644 index d3f0b52a88aaa2..00000000000000 --- a/Examples/Movies/Movies/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Examples/Movies/Movies/Images.xcassets/AppIcon.appiconset/Contents.json b/Examples/Movies/Movies/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 118c98f7461bf9..00000000000000 --- a/Examples/Movies/Movies/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/Movies/Movies/Info.plist b/Examples/Movies/Movies/Info.plist deleted file mode 100644 index 4ecf97f70446d6..00000000000000 --- a/Examples/Movies/Movies/Info.plist +++ /dev/null @@ -1,56 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - movies - - - - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/Examples/Movies/Movies/main.m b/Examples/Movies/Movies/main.m deleted file mode 100644 index 9c58a39a483c81..00000000000000 --- a/Examples/Movies/Movies/main.m +++ /dev/null @@ -1,22 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/Examples/Movies/MoviesApp.android.js b/Examples/Movies/MoviesApp.android.js deleted file mode 100644 index c6f78a3d1fc58e..00000000000000 --- a/Examples/Movies/MoviesApp.android.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule MoviesApp - * @flow - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); - -var nativeImageSource = require('nativeImageSource'); -var { - AppRegistry, - BackAndroid, - Navigator, - StyleSheet, - ToolbarAndroid, - View, -} = ReactNative; - -var MovieScreen = require('./MovieScreen'); -var SearchScreen = require('./SearchScreen'); - -var _navigator; -BackAndroid.addEventListener('hardwareBackPress', () => { - if (_navigator && _navigator.getCurrentRoutes().length > 1) { - _navigator.pop(); - return true; - } - return false; -}); - -var RouteMapper = function(route, navigationOperations, onComponentRef) { - _navigator = navigationOperations; - if (route.name === 'search') { - return ( - - ); - } else if (route.name === 'movie') { - return ( - - - - - ); - } -}; - -class MoviesApp extends React.Component { - render() { - var initialRoute = {name: 'search'}; - return ( - Navigator.SceneConfigs.FadeAndroid} - renderScene={RouteMapper} - /> - ); - } -} - -var styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: 'white', - }, - toolbar: { - backgroundColor: '#a9a9a9', - height: 56, - }, -}); - -AppRegistry.registerComponent('MoviesApp', () => MoviesApp); - -module.exports = MoviesApp; diff --git a/Examples/Movies/MoviesApp.ios.js b/Examples/Movies/MoviesApp.ios.js deleted file mode 100644 index 99ba1d3783b932..00000000000000 --- a/Examples/Movies/MoviesApp.ios.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule MoviesApp - * @flow - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - AppRegistry, - NavigatorIOS, - StyleSheet, -} = ReactNative; - -var SearchScreen = require('./SearchScreen'); - -class MoviesApp extends React.Component { - render() { - return ( - - ); - } -} - -var styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: 'white', - }, -}); - -AppRegistry.registerComponent('MoviesApp', () => MoviesApp); - -module.exports = MoviesApp; diff --git a/Examples/Movies/README.md b/Examples/Movies/README.md deleted file mode 100644 index f2ad9befac5129..00000000000000 --- a/Examples/Movies/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Movies app - -The Movies app is a demonstration of basic concepts, such as fetching data, rendering a list of data including images, and navigating between different screens. - -## Running this app - -Before running the app, make sure you ran: - - git clone https://github.com/facebook/react-native.git - cd react-native - npm install - -### Running on iOS - -Mac OS and Xcode are required. - -- Open `Examples/Movies/Movies.xcodeproj` in Xcode -- Hit the Run button - -See [Running on device](https://facebook.github.io/react-native/docs/running-on-device.html) if you want to use a physical device. - -### Running on Android - -You'll need to have all the [prerequisites](https://github.com/facebook/react-native/tree/master/ReactAndroid#prerequisites) (SDK, NDK) for Building React Native installed. - -Start an Android emulator ([Genymotion](https://www.genymotion.com) is recommended). - - cd react-native - ./gradlew :Examples:Movies:android:app:installDebug - ./packager/packager.sh - -_Note: Building for the first time can take a while._ - -Open the Movies app in your emulator. - -See [Running on Device](https://facebook.github.io/react-native/docs/running-on-device.html) in case you want to use a physical device. - -### Running with Buck - -Follow the same setup as running with gradle. - -Install Buck from [here](https://buckbuild.com/setup/install.html). - -Run the following commands from the react-native folder: - - ./gradlew :ReactAndroid:packageReactNdkLibsForBuck - buck fetch movies - buck install -r movies - ./packager/packager.sh - -_Note: The native libs are still built using gradle. Full build with buck is coming soon(tm)._ - -## Built from source - -Building the app on both iOS and Android means building the React Native framework from source. This way you're running the latest native and JS code the way you see it in your clone of the github repo. - -This is different from apps created using `react-native init` which have a dependency on a specific version of React Native JS and native code, declared in a `package.json` file (and `build.gradle` for Android apps). diff --git a/Examples/Movies/SearchBar.android.js b/Examples/Movies/SearchBar.android.js deleted file mode 100644 index 1a889d7fbebfa2..00000000000000 --- a/Examples/Movies/SearchBar.android.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule SearchBar - * @flow - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); - -var nativeImageSource = require('nativeImageSource'); -var { - Image, - Platform, - ActivityIndicator, - TextInput, - StyleSheet, - TouchableNativeFeedback, - View, -} = ReactNative; - -var IS_RIPPLE_EFFECT_SUPPORTED = Platform.Version >= 21; - -class SearchBar extends React.Component { - render() { - var background = IS_RIPPLE_EFFECT_SUPPORTED ? - TouchableNativeFeedback.SelectableBackgroundBorderless() : - TouchableNativeFeedback.SelectableBackground(); - return ( - - this.refs.input && this.refs.input.focus()}> - - - - - - - - ); - } -} - -var styles = StyleSheet.create({ - searchBar: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: '#a9a9a9', - height: 56, - }, - searchBarInput: { - flex: 1, - fontSize: 20, - fontWeight: 'bold', - color: 'white', - height: 50, - padding: 0, - backgroundColor: 'transparent' - }, - spinner: { - width: 30, - height: 30, - marginRight: 16, - }, - icon: { - width: 24, - height: 24, - marginHorizontal: 8, - }, -}); - -module.exports = SearchBar; diff --git a/Examples/Movies/SearchBar.ios.js b/Examples/Movies/SearchBar.ios.js deleted file mode 100644 index c168d40184df05..00000000000000 --- a/Examples/Movies/SearchBar.ios.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule SearchBar - * @flow - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - ActivityIndicator, - TextInput, - StyleSheet, - View, -} = ReactNative; - -class SearchBar extends React.Component { - render() { - return ( - - - - - ); - } -} - -var styles = StyleSheet.create({ - searchBar: { - marginTop: 64, - padding: 3, - paddingLeft: 8, - flexDirection: 'row', - alignItems: 'center', - }, - searchBarInput: { - fontSize: 15, - flex: 1, - height: 30, - }, - spinner: { - width: 30, - }, -}); - -module.exports = SearchBar; diff --git a/Examples/Movies/SearchScreen.js b/Examples/Movies/SearchScreen.js deleted file mode 100644 index 57fa82885f7950..00000000000000 --- a/Examples/Movies/SearchScreen.js +++ /dev/null @@ -1,370 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @flow - * @providesModule SearchScreen - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - ActivityIndicator, - ListView, - Platform, - StyleSheet, - Text, - View, -} = ReactNative; -var TimerMixin = require('react-timer-mixin'); - -var invariant = require('fbjs/lib/invariant'); -var dismissKeyboard = require('dismissKeyboard'); - -var MovieCell = require('./MovieCell'); -var MovieScreen = require('./MovieScreen'); -var SearchBar = require('SearchBar'); - -/** - * This is for demo purposes only, and rate limited. - * In case you want to use the Rotten Tomatoes' API on a real app you should - * create an account at http://developer.rottentomatoes.com/ - */ -var API_URL = 'http://api.rottentomatoes.com/api/public/v1.0/'; -var API_KEYS = [ - '7waqfqbprs7pajbz28mqf6vz', - // 'y4vwv8m33hed9ety83jmv52f', Fallback api_key -]; - -// Results should be cached keyed by the query -// with values of null meaning "being fetched" -// and anything besides null and undefined -// as the result of a valid query -var resultsCache = { - dataForQuery: {}, - nextPageNumberForQuery: {}, - totalForQuery: {}, -}; - -var LOADING = {}; - -var SearchScreen = React.createClass({ - mixins: [TimerMixin], - - timeoutID: (null: any), - - getInitialState: function() { - return { - isLoading: false, - isLoadingTail: false, - dataSource: new ListView.DataSource({ - rowHasChanged: (row1, row2) => row1 !== row2, - }), - filter: '', - queryNumber: 0, - }; - }, - - componentDidMount: function() { - this.searchMovies(''); - }, - - _urlForQueryAndPage: function(query: string, pageNumber: number): string { - var apiKey = API_KEYS[this.state.queryNumber % API_KEYS.length]; - if (query) { - return ( - API_URL + 'movies.json?apikey=' + apiKey + '&q=' + - encodeURIComponent(query) + '&page_limit=20&page=' + pageNumber - ); - } else { - // With no query, load latest movies - return ( - API_URL + 'lists/movies/in_theaters.json?apikey=' + apiKey + - '&page_limit=20&page=' + pageNumber - ); - } - }, - - searchMovies: function(query: string) { - this.timeoutID = null; - - this.setState({filter: query}); - - var cachedResultsForQuery = resultsCache.dataForQuery[query]; - if (cachedResultsForQuery) { - if (!LOADING[query]) { - this.setState({ - dataSource: this.getDataSource(cachedResultsForQuery), - isLoading: false - }); - } else { - this.setState({isLoading: true}); - } - return; - } - - LOADING[query] = true; - resultsCache.dataForQuery[query] = null; - this.setState({ - isLoading: true, - queryNumber: this.state.queryNumber + 1, - isLoadingTail: false, - }); - - fetch(this._urlForQueryAndPage(query, 1)) - .then((response) => response.json()) - .then((responseData) => { - LOADING[query] = false; - resultsCache.totalForQuery[query] = responseData.total; - resultsCache.dataForQuery[query] = responseData.movies; - resultsCache.nextPageNumberForQuery[query] = 2; - - if (this.state.filter !== query) { - // do not update state if the query is stale - return; - } - - this.setState({ - isLoading: false, - dataSource: this.getDataSource(responseData.movies), - }); - }) - .catch((error) => { - LOADING[query] = false; - resultsCache.dataForQuery[query] = undefined; - - this.setState({ - dataSource: this.getDataSource([]), - isLoading: false, - }); - }) - .done(); - }, - - hasMore: function(): boolean { - var query = this.state.filter; - if (!resultsCache.dataForQuery[query]) { - return true; - } - return ( - resultsCache.totalForQuery[query] !== - resultsCache.dataForQuery[query].length - ); - }, - - onEndReached: function() { - var query = this.state.filter; - if (!this.hasMore() || this.state.isLoadingTail) { - // We're already fetching or have all the elements so noop - return; - } - - if (LOADING[query]) { - return; - } - - LOADING[query] = true; - this.setState({ - queryNumber: this.state.queryNumber + 1, - isLoadingTail: true, - }); - - var page = resultsCache.nextPageNumberForQuery[query]; - invariant(page != null, 'Next page number for "%s" is missing', query); - fetch(this._urlForQueryAndPage(query, page)) - .then((response) => response.json()) - .catch((error) => { - console.error(error); - LOADING[query] = false; - this.setState({ - isLoadingTail: false, - }); - }) - .then((responseData) => { - var moviesForQuery = resultsCache.dataForQuery[query].slice(); - - LOADING[query] = false; - // We reached the end of the list before the expected number of results - if (!responseData.movies) { - resultsCache.totalForQuery[query] = moviesForQuery.length; - } else { - for (var i in responseData.movies) { - moviesForQuery.push(responseData.movies[i]); - } - resultsCache.dataForQuery[query] = moviesForQuery; - resultsCache.nextPageNumberForQuery[query] += 1; - } - - if (this.state.filter !== query) { - // do not update state if the query is stale - return; - } - - this.setState({ - isLoadingTail: false, - dataSource: this.getDataSource(resultsCache.dataForQuery[query]), - }); - }) - .done(); - }, - - getDataSource: function(movies: Array): ListView.DataSource { - return this.state.dataSource.cloneWithRows(movies); - }, - - selectMovie: function(movie: Object) { - if (Platform.OS === 'ios') { - this.props.navigator.push({ - title: movie.title, - component: MovieScreen, - passProps: {movie}, - }); - } else { - dismissKeyboard(); - this.props.navigator.push({ - title: movie.title, - name: 'movie', - movie: movie, - }); - } - }, - - onSearchChange: function(event: Object) { - var filter = event.nativeEvent.text.toLowerCase(); - - this.clearTimeout(this.timeoutID); - this.timeoutID = this.setTimeout(() => this.searchMovies(filter), 100); - }, - - renderFooter: function() { - if (!this.hasMore() || !this.state.isLoadingTail) { - return ; - } - - return ; - }, - - renderSeparator: function( - sectionID: number | string, - rowID: number | string, - adjacentRowHighlighted: boolean - ) { - var style = styles.rowSeparator; - if (adjacentRowHighlighted) { - style = [style, styles.rowSeparatorHide]; - } - return ( - - ); - }, - - renderRow: function( - movie: Object, - sectionID: number | string, - rowID: number | string, - highlightRowFunc: (sectionID: ?number | string, rowID: ?number | string) => void, - ) { - return ( - this.selectMovie(movie)} - onHighlight={() => highlightRowFunc(sectionID, rowID)} - onUnhighlight={() => highlightRowFunc(null, null)} - movie={movie} - /> - ); - }, - - render: function() { - var content = this.state.dataSource.getRowCount() === 0 ? - : - ; - - return ( - - - this.refs.listview && this.refs.listview.getScrollResponder().scrollTo({ x: 0, y: 0 })} - /> - - {content} - - ); - }, -}); - -class NoMovies extends React.Component { - render() { - var text = ''; - if (this.props.filter) { - text = `No results for "${this.props.filter}"`; - } else if (!this.props.isLoading) { - // If we're looking at the latest movies, aren't currently loading, and - // still have no results, show a message - text = 'No movies found'; - } - - return ( - - {text} - - ); - } -} - -var styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: 'white', - }, - centerText: { - alignItems: 'center', - }, - noMoviesText: { - marginTop: 80, - color: '#888888', - }, - separator: { - height: 1, - backgroundColor: '#eeeeee', - }, - scrollSpinner: { - marginVertical: 20, - }, - rowSeparator: { - backgroundColor: 'rgba(0, 0, 0, 0.1)', - height: 1, - marginLeft: 4, - }, - rowSeparatorHide: { - opacity: 0.0, - }, -}); - -module.exports = SearchScreen; diff --git a/Examples/Movies/__tests__/getImageSource-test.js b/Examples/Movies/__tests__/getImageSource-test.js deleted file mode 100644 index 2061312f020de2..00000000000000 --- a/Examples/Movies/__tests__/getImageSource-test.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2004-present Facebook. All Rights Reserved. - */ -'use strict'; - -jest.unmock('../getImageSource'); - -var getImageSource = require('../getImageSource'); - -describe('getImageSource', () => { - it('returns null for invalid input', () => { - expect(getImageSource().uri).toBe(null); - }); - - it('returns a movie thumbnail', () => { - var uri = 'https://facebook.com'; - var source = { - posters: { - thumbnail: uri, - }, - }; - expect(getImageSource(source).uri).toBe(uri); - }); - - it('returns a movie thumbnail with kind', () => { - var uri = 'https://facebook.com?tmb'; - var source = { - posters: { - thumbnail: uri, - }, - }; - expect(getImageSource(source, 'kind').uri).toBe( - 'https://facebook.com?kind' - ); - }); -}); diff --git a/Examples/Movies/android/app/BUCK b/Examples/Movies/android/app/BUCK deleted file mode 100644 index bed62bb7a7ed01..00000000000000 --- a/Examples/Movies/android/app/BUCK +++ /dev/null @@ -1,31 +0,0 @@ -include_defs("//ReactAndroid/DEFS") - -android_binary( - name = "app", - keystore = "//keystores:debug", - manifest = "src/main/AndroidManifest.xml", - deps = [ - ":movies-lib", - ], -) - -android_library( - name = "movies-lib", - srcs = glob(["src/main/java/**/*.java"]), - deps = [ - ":res", - react_native_dep("third-party/java/jsr-305:jsr-305"), - react_native_target("java/com/facebook/react:react"), - react_native_target("java/com/facebook/react/modules/core:core"), - react_native_target("java/com/facebook/react/shell:shell"), - react_native_target("jni/prebuilt:android-jsc"), - # .so files are prebuilt by Gradle with `./gradlew :ReactAndroid:packageReactNdkLibsForBuck` - react_native_target("jni/prebuilt:reactnative-libs"), - ], -) - -android_resource( - name = "res", - package = "com.facebook.react.movies", - res = "src/main/res", -) diff --git a/Examples/Movies/android/app/build.gradle b/Examples/Movies/android/app/build.gradle deleted file mode 100644 index 6bc71ffda74360..00000000000000 --- a/Examples/Movies/android/app/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 23 - buildToolsVersion "23.0.1" - - defaultConfig { - applicationId "com.facebook.react.movies" - minSdkVersion 16 - targetSdkVersion 22 - versionCode 1 - versionName "1.0" - ndk { - abiFilters "armeabi-v7a", "x86" - } - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.0.1' - - // Build React Native from source - compile project(':ReactAndroid') -} diff --git a/Examples/Movies/android/app/gradle.properties b/Examples/Movies/android/app/gradle.properties deleted file mode 100644 index dfbe478748b403..00000000000000 --- a/Examples/Movies/android/app/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -android.useDeprecatedNdk=true diff --git a/Examples/Movies/android/app/proguard-rules.pro b/Examples/Movies/android/app/proguard-rules.pro deleted file mode 100644 index a92fa177ee49f0..00000000000000 --- a/Examples/Movies/android/app/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/Examples/Movies/android/app/src/main/AndroidManifest.xml b/Examples/Movies/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 34f5b0bc47ee60..00000000000000 --- a/Examples/Movies/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesActivity.java b/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesActivity.java deleted file mode 100644 index da1d7fc5b8fba5..00000000000000 --- a/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesActivity.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.facebook.react.movies; - -import com.facebook.react.ReactActivity; - -public class MoviesActivity extends ReactActivity { - @Override - protected String getMainComponentName() { - return "MoviesApp"; - } -} diff --git a/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesApplication.java b/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesApplication.java deleted file mode 100644 index ee57b9b595fc58..00000000000000 --- a/Examples/Movies/android/app/src/main/java/com/facebook/react/movies/MoviesApplication.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.facebook.react.movies; - -import android.app.Application; - -import com.facebook.react.ReactApplication; -import com.facebook.react.ReactNativeHost; -import com.facebook.react.ReactPackage; -import com.facebook.react.shell.MainReactPackage; - -import java.util.Arrays; -import java.util.List; - -import javax.annotation.Nullable; - -public class MoviesApplication extends Application implements ReactApplication { - private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { - @Override - public String getJSMainModuleName() { - return "Examples/Movies/MoviesApp.android"; - } - - @Override - public @Nullable String getBundleAssetName() { - return "MoviesApp.android.bundle"; - } - - @Override - public boolean getUseDeveloperSupport() { - return true; - } - - @Override - protected List getPackages() { - return Arrays.asList( - new MainReactPackage() - ); - } - }; - - @Override - public ReactNativeHost getReactNativeHost() { - return mReactNativeHost; - } -} diff --git a/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_back_white.png b/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_back_white.png deleted file mode 100644 index a34f0dbb8fd520..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_back_white.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_search_white.png b/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_search_white.png deleted file mode 100755 index 861b40db0d4ddb..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable-hdpi/android_search_white.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_back_white.png b/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_back_white.png deleted file mode 100644 index 63111599a3b177..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_back_white.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_search_white.png b/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_search_white.png deleted file mode 100755 index fedd0c623f5698..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable-mdpi/android_search_white.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_back_white.png b/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_back_white.png deleted file mode 100644 index 500892e105e98c..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_back_white.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_search_white.png b/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_search_white.png deleted file mode 100755 index 5713a86f6329d9..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_search_white.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_back_white.png b/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_back_white.png deleted file mode 100644 index 03620979ea75d9..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_back_white.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_search_white.png b/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_search_white.png deleted file mode 100755 index d9f75bc794e76f..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_search_white.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/drawable/rotten_tomatoes_icon.png b/Examples/Movies/android/app/src/main/res/drawable/rotten_tomatoes_icon.png deleted file mode 100644 index 395d7043130e5e..00000000000000 Binary files a/Examples/Movies/android/app/src/main/res/drawable/rotten_tomatoes_icon.png and /dev/null differ diff --git a/Examples/Movies/android/app/src/main/res/layout/activity_main.xml b/Examples/Movies/android/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index d42c3b844e6301..00000000000000 --- a/Examples/Movies/android/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/Examples/Movies/android/app/src/main/res/values/strings.xml b/Examples/Movies/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 7c4632e0d0e8be..00000000000000 --- a/Examples/Movies/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - MoviesApp - diff --git a/Examples/Movies/android/app/src/main/res/values/styles.xml b/Examples/Movies/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 319eb0ca100b5a..00000000000000 --- a/Examples/Movies/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/Examples/Movies/getImageSource.js b/Examples/Movies/getImageSource.js deleted file mode 100644 index d2332625e6b63f..00000000000000 --- a/Examples/Movies/getImageSource.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @flow - * @providesModule getImageSource - */ -'use strict'; - -function getImageSource(movie: Object, kind: ?string): {uri: ?string} { - var uri = movie && movie.posters ? movie.posters.thumbnail : null; - if (uri && kind) { - uri = uri.replace('tmb', kind); - } - return { uri }; -} - -module.exports = getImageSource; diff --git a/Examples/Movies/getStyleFromScore.js b/Examples/Movies/getStyleFromScore.js deleted file mode 100644 index 553c0002b12565..00000000000000 --- a/Examples/Movies/getStyleFromScore.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @flow - * @providesModule getStyleFromScore - */ -'use strict'; - -var ReactNative = require('react-native'); -var { - StyleSheet, -} = ReactNative; - -var MAX_VALUE = 200; - -import type { StyleObj } from 'StyleSheetTypes'; - -function getStyleFromScore(score: number): StyleObj { - if (score < 0) { - return styles.noScore; - } - - var normalizedScore = Math.round((score / 100) * MAX_VALUE); - return { - color: 'rgb(' + - (MAX_VALUE - normalizedScore) + ', ' + - normalizedScore + ', ' + - 0 + - ')' - }; -} - -var styles = StyleSheet.create({ - noScore: { - color: '#999999', - }, -}); - -module.exports = getStyleFromScore; diff --git a/Examples/Movies/getTextFromScore.js b/Examples/Movies/getTextFromScore.js deleted file mode 100644 index f36c22e32832ec..00000000000000 --- a/Examples/Movies/getTextFromScore.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @flow - * @providesModule getTextFromScore - */ -'use strict'; - -function getTextFromScore(score: number): string { - return score > 0 ? score + '%' : 'N/A'; -} - -module.exports = getTextFromScore; diff --git a/Examples/TicTacToe/TicTacToe.xcodeproj/project.pbxproj b/Examples/TicTacToe/TicTacToe.xcodeproj/project.pbxproj deleted file mode 100644 index f1f1514d32dc86..00000000000000 --- a/Examples/TicTacToe/TicTacToe.xcodeproj/project.pbxproj +++ /dev/null @@ -1,518 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1341803E1AA91802003F314A /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341803D1AA917ED003F314A /* libRCTImage.a */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 144C5F691AC3E5E300B004E7 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 144C5F681AC3E5D800B004E7 /* libReact.a */; }; - 58C572511AA6229D00CDF9C8 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C5724D1AA6224400CDF9C8 /* libRCTText.a */; }; - 5FF8942E1B85571A007731BE /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5FF8942D1B8556F8007731BE /* libRCTWebSocket.a */; }; - 832044981B492C2500E297FC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832044951B492C1E00E297FC /* libRCTSettings.a */; }; - E730D7731D4EE604000B7DA8 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E730D76E1D4EE5FC000B7DA8 /* libRCTNetwork.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 1341803C1AA917ED003F314A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 134180381AA917ED003F314A /* RCTImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; - 144C5F671AC3E5D800B004E7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 144C5F631AC3E5D800B004E7 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = React; - }; - 58C5724C1AA6224400CDF9C8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 587650DA1A9EB0DB008B8F17 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5119B1A9E6C1200147676; - remoteInfo = RCTText; - }; - 5FF8942C1B8556F8007731BE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 5FF894281B8556F8007731BE /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; - 832044941B492C1E00E297FC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 8320448F1B492C1E00E297FC /* RCTSettings.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTSettings; - }; - E730D76D1D4EE5FC000B7DA8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = E730D7681D4EE5FC000B7DA8 /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTNetwork; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 134180381AA917ED003F314A /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; - 13B07F961A680F5B00A75B9A /* TicTacToe.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TicTacToe.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = TicTacToe/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = TicTacToe/AppDelegate.m; sourceTree = ""; }; - 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = TicTacToe/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = TicTacToe/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = TicTacToe/main.m; sourceTree = ""; }; - 144C5F631AC3E5D800B004E7 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = ""; }; - 587650DA1A9EB0DB008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; - 5FF894281B8556F8007731BE /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = ""; }; - 8320448F1B492C1E00E297FC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; - E730D7681D4EE5FC000B7DA8 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - E730D7731D4EE604000B7DA8 /* libRCTNetwork.a in Frameworks */, - 5FF8942E1B85571A007731BE /* libRCTWebSocket.a in Frameworks */, - 144C5F691AC3E5E300B004E7 /* libReact.a in Frameworks */, - 1341803E1AA91802003F314A /* libRCTImage.a in Frameworks */, - 58C572511AA6229D00CDF9C8 /* libRCTText.a in Frameworks */, - 832044981B492C2500E297FC /* libRCTSettings.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 134180391AA917ED003F314A /* Products */ = { - isa = PBXGroup; - children = ( - 1341803D1AA917ED003F314A /* libRCTImage.a */, - ); - name = Products; - sourceTree = ""; - }; - 13B07FAE1A68108700A75B9A /* TicTacToe */ = { - isa = PBXGroup; - children = ( - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13B07FB71A68108700A75B9A /* main.m */, - ); - name = TicTacToe; - sourceTree = ""; - }; - 144C5F641AC3E5D800B004E7 /* Products */ = { - isa = PBXGroup; - children = ( - 144C5F681AC3E5D800B004E7 /* libReact.a */, - ); - name = Products; - sourceTree = ""; - }; - 58C572071AA6126D00CDF9C8 /* Libraries */ = { - isa = PBXGroup; - children = ( - E730D7681D4EE5FC000B7DA8 /* RCTNetwork.xcodeproj */, - 5FF894281B8556F8007731BE /* RCTWebSocket.xcodeproj */, - 144C5F631AC3E5D800B004E7 /* React.xcodeproj */, - 134180381AA917ED003F314A /* RCTImage.xcodeproj */, - 8320448F1B492C1E00E297FC /* RCTSettings.xcodeproj */, - 587650DA1A9EB0DB008B8F17 /* RCTText.xcodeproj */, - ); - name = Libraries; - sourceTree = ""; - }; - 58C572481AA6224300CDF9C8 /* Products */ = { - isa = PBXGroup; - children = ( - 58C5724D1AA6224400CDF9C8 /* libRCTText.a */, - ); - name = Products; - sourceTree = ""; - }; - 5FF894291B8556F8007731BE /* Products */ = { - isa = PBXGroup; - children = ( - 5FF8942D1B8556F8007731BE /* libRCTWebSocket.a */, - ); - name = Products; - sourceTree = ""; - }; - 832044901B492C1E00E297FC /* Products */ = { - isa = PBXGroup; - children = ( - 832044951B492C1E00E297FC /* libRCTSettings.a */, - ); - name = Products; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 13B07FAE1A68108700A75B9A /* TicTacToe */, - 58C572071AA6126D00CDF9C8 /* Libraries */, - 83CBBA001A601CBA00E9B192 /* Products */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* TicTacToe.app */, - ); - name = Products; - sourceTree = ""; - }; - E730D7691D4EE5FC000B7DA8 /* Products */ = { - isa = PBXGroup; - children = ( - E730D76E1D4EE5FC000B7DA8 /* libRCTNetwork.a */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 13B07F861A680F5B00A75B9A /* TicTacToe */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "TicTacToe" */; - buildPhases = ( - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 681C70ED1D2BE73C00E71791 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = TicTacToe; - productName = "Hello World"; - productReference = 13B07F961A680F5B00A75B9A /* TicTacToe.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0700; - ORGANIZATIONNAME = Facebook; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "TicTacToe" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 134180391AA917ED003F314A /* Products */; - ProjectRef = 134180381AA917ED003F314A /* RCTImage.xcodeproj */; - }, - { - ProductGroup = E730D7691D4EE5FC000B7DA8 /* Products */; - ProjectRef = E730D7681D4EE5FC000B7DA8 /* RCTNetwork.xcodeproj */; - }, - { - ProductGroup = 832044901B492C1E00E297FC /* Products */; - ProjectRef = 8320448F1B492C1E00E297FC /* RCTSettings.xcodeproj */; - }, - { - ProductGroup = 58C572481AA6224300CDF9C8 /* Products */; - ProjectRef = 587650DA1A9EB0DB008B8F17 /* RCTText.xcodeproj */; - }, - { - ProductGroup = 5FF894291B8556F8007731BE /* Products */; - ProjectRef = 5FF894281B8556F8007731BE /* RCTWebSocket.xcodeproj */; - }, - { - ProductGroup = 144C5F641AC3E5D800B004E7 /* Products */; - ProjectRef = 144C5F631AC3E5D800B004E7 /* React.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* TicTacToe */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 1341803D1AA917ED003F314A /* libRCTImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTImage.a; - remoteRef = 1341803C1AA917ED003F314A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 144C5F681AC3E5D800B004E7 /* libReact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReact.a; - remoteRef = 144C5F671AC3E5D800B004E7 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 58C5724D1AA6224400CDF9C8 /* libRCTText.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTText.a; - remoteRef = 58C5724C1AA6224400CDF9C8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 5FF8942D1B8556F8007731BE /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 5FF8942C1B8556F8007731BE /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 832044951B492C1E00E297FC /* libRCTSettings.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTSettings.a; - remoteRef = 832044941B492C1E00E297FC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - E730D76E1D4EE5FC000B7DA8 /* libRCTNetwork.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTNetwork.a; - remoteRef = E730D76D1D4EE5FC000B7DA8 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 681C70ED1D2BE73C00E71791 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n$SRCROOT/../../packager/react-native-xcode.sh Examples/TicTacToe/TicTacToeApp.js\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 13B07FB21A68108700A75B9A /* Base */, - ); - name = LaunchScreen.xib; - path = TicTacToe; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - INFOPLIST_FILE = "$(SRCROOT)/TicTacToe/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(inherited)"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = TicTacToe; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - INFOPLIST_FILE = "$(SRCROOT)/TicTacToe/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(inherited)"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = TicTacToe; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "TicTacToe" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "TicTacToe" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/Examples/TicTacToe/TicTacToe.xcodeproj/xcshareddata/xcschemes/TicTacToe.xcscheme b/Examples/TicTacToe/TicTacToe.xcodeproj/xcshareddata/xcschemes/TicTacToe.xcscheme deleted file mode 100644 index 635a53803474a1..00000000000000 --- a/Examples/TicTacToe/TicTacToe.xcodeproj/xcshareddata/xcschemes/TicTacToe.xcscheme +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Examples/TicTacToe/TicTacToe/AppDelegate.h b/Examples/TicTacToe/TicTacToe/AppDelegate.h deleted file mode 100644 index 1a4317567ce256..00000000000000 --- a/Examples/TicTacToe/TicTacToe/AppDelegate.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import - -@interface AppDelegate : UIResponder - -@property (nonatomic, strong) UIWindow *window; - -@end diff --git a/Examples/TicTacToe/TicTacToe/AppDelegate.m b/Examples/TicTacToe/TicTacToe/AppDelegate.m deleted file mode 100644 index 3e7399917b5530..00000000000000 --- a/Examples/TicTacToe/TicTacToe/AppDelegate.m +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import "AppDelegate.h" - -#import "RCTBundleURLProvider.h" -#import "RCTRootView.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"Examples/TicTacToe/TicTacToeApp" fallbackResource:nil]; - - RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"TicTacToeApp" - initialProperties:nil - launchOptions:launchOptions]; - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [UIViewController new]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - return YES; -} - -@end diff --git a/Examples/TicTacToe/TicTacToe/Base.lproj/LaunchScreen.xib b/Examples/TicTacToe/TicTacToe/Base.lproj/LaunchScreen.xib deleted file mode 100644 index c2d4e9079046d7..00000000000000 --- a/Examples/TicTacToe/TicTacToe/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Examples/TicTacToe/TicTacToe/Images.xcassets/AppIcon.appiconset/Contents.json b/Examples/TicTacToe/TicTacToe/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 118c98f7461bf9..00000000000000 --- a/Examples/TicTacToe/TicTacToe/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/TicTacToe/TicTacToe/Info.plist b/Examples/TicTacToe/TicTacToe/Info.plist deleted file mode 100644 index 25742279c9dce4..00000000000000 --- a/Examples/TicTacToe/TicTacToe/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - NSAppTransportSecurity - - - NSAllowsArbitraryLoads - - - - diff --git a/Examples/TicTacToe/TicTacToe/main.m b/Examples/TicTacToe/TicTacToe/main.m deleted file mode 100644 index 9c58a39a483c81..00000000000000 --- a/Examples/TicTacToe/TicTacToe/main.m +++ /dev/null @@ -1,22 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/Examples/TicTacToe/TicTacToeApp.js b/Examples/TicTacToe/TicTacToeApp.js deleted file mode 100755 index 17d41a7e675870..00000000000000 --- a/Examples/TicTacToe/TicTacToeApp.js +++ /dev/null @@ -1,327 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule TicTacToeApp - * @flow - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - AppRegistry, - StyleSheet, - Text, - TouchableHighlight, - View, -} = ReactNative; - -class Board { - grid: Array>; - turn: number; - - constructor() { - var size = 3; - var grid = Array(size); - for (var i = 0; i < size; i++) { - var row = Array(size); - for (var j = 0; j < size; j++) { - row[j] = 0; - } - grid[i] = row; - } - this.grid = grid; - - this.turn = 1; - } - - mark(row: number, col: number, player: number): Board { - this.grid[row][col] = player; - return this; - } - - hasMark(row: number, col: number): boolean { - return this.grid[row][col] !== 0; - } - - winner(): ?number { - for (var i = 0; i < 3; i++) { - if (this.grid[i][0] !== 0 && this.grid[i][0] === this.grid[i][1] && - this.grid[i][0] === this.grid[i][2]) { - return this.grid[i][0]; - } - } - - for (var i = 0; i < 3; i++) { - if (this.grid[0][i] !== 0 && this.grid[0][i] === this.grid[1][i] && - this.grid[0][i] === this.grid[2][i]) { - return this.grid[0][i]; - } - } - - if (this.grid[0][0] !== 0 && this.grid[0][0] === this.grid[1][1] && - this.grid[0][0] === this.grid[2][2]) { - return this.grid[0][0]; - } - - if (this.grid[0][2] !== 0 && this.grid[0][2] === this.grid[1][1] && - this.grid[0][2] === this.grid[2][0]) { - return this.grid[0][2]; - } - - return null; - } - - tie(): boolean { - for (var i = 0; i < 3; i++) { - for (var j = 0; j < 3; j++) { - if (this.grid[i][j] === 0) { - return false; - } - } - } - return this.winner() === null; - } -} - -class Cell extends React.Component { - cellStyle() { - switch (this.props.player) { - case 1: - return styles.cellX; - case 2: - return styles.cellO; - default: - return null; - } - } - - textStyle() { - switch (this.props.player) { - case 1: - return styles.cellTextX; - case 2: - return styles.cellTextO; - default: - return {}; - } - } - - textContents() { - switch (this.props.player) { - case 1: - return 'X'; - case 2: - return 'O'; - default: - return ''; - } - } - - render() { - return ( - - - - {this.textContents()} - - - - ); - } -} - -class GameEndOverlay extends React.Component { - render() { - var board = this.props.board; - - var tie = board.tie(); - var winner = board.winner(); - if (!winner && !tie) { - return null; - } - - var message; - if (tie) { - message = 'It\'s a tie!'; - } else { - message = (winner === 1 ? 'X' : 'O') + ' wins!'; - } - - return ( - - {message} - - - New Game - - - - ); - } -} - -var TicTacToeApp = React.createClass({ - getInitialState() { - return { board: new Board(), player: 1 }; - }, - - restartGame() { - this.setState(this.getInitialState()); - }, - - nextPlayer(): number { - return this.state.player === 1 ? 2 : 1; - }, - - handleCellPress(row: number, col: number) { - if (this.state.board.hasMark(row, col)) { - return; - } - - this.setState({ - board: this.state.board.mark(row, col, this.state.player), - player: this.nextPlayer(), - }); - }, - - render() { - var rows = this.state.board.grid.map((cells, row) => - - {cells.map((player, col) => - - )} - - ); - - return ( - - EXTREME T3 - - {rows} - - - - ); - } -}); - -var styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'white' - }, - title: { - fontFamily: 'Chalkduster', - fontSize: 39, - marginBottom: 20, - }, - board: { - padding: 5, - backgroundColor: '#47525d', - borderRadius: 10, - }, - row: { - flexDirection: 'row', - }, - - // CELL - - cell: { - width: 80, - height: 80, - borderRadius: 5, - backgroundColor: '#7b8994', - margin: 5, - justifyContent: 'center', - alignItems: 'center', - }, - cellX: { - backgroundColor: '#72d0eb', - }, - cellO: { - backgroundColor: '#7ebd26', - }, - - // CELL TEXT - - cellText: { - fontSize: 50, - fontFamily: 'AvenirNext-Bold', - }, - cellTextX: { - color: '#19a9e5', - }, - cellTextO: { - color: '#b9dc2f', - }, - - // GAME OVER - - overlay: { - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - backgroundColor: 'rgba(221, 221, 221, 0.5)', - flex: 1, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - }, - overlayMessage: { - fontSize: 40, - marginBottom: 20, - marginLeft: 20, - marginRight: 20, - fontFamily: 'AvenirNext-DemiBold', - textAlign: 'center', - }, - newGame: { - backgroundColor: '#887765', - padding: 20, - borderRadius: 5, - }, - newGameText: { - color: 'white', - fontSize: 20, - fontFamily: 'AvenirNext-DemiBold', - }, -}); - -AppRegistry.registerComponent('TicTacToeApp', () => TicTacToeApp); - -module.exports = TicTacToeApp; diff --git a/Examples/TicTacToe/android/app/BUCK b/Examples/TicTacToe/android/app/BUCK deleted file mode 100644 index 3dae6c388d4c01..00000000000000 --- a/Examples/TicTacToe/android/app/BUCK +++ /dev/null @@ -1,31 +0,0 @@ -include_defs("//ReactAndroid/DEFS") - -android_binary( - name = "app", - keystore = "//keystores:debug", - manifest = "src/main/AndroidManifest.xml", - deps = [ - ":tictactoe-lib", - ], -) - -android_library( - name = "tictactoe-lib", - srcs = glob(["src/main/java/**/*.java"]), - deps = [ - ":res", - react_native_dep("third-party/java/jsr-305:jsr-305"), - react_native_target("java/com/facebook/react:react"), - react_native_target("java/com/facebook/react/modules/core:core"), - react_native_target("java/com/facebook/react/shell:shell"), - react_native_target("jni/prebuilt:android-jsc"), - # .so files are prebuilt by Gradle with `./gradlew :ReactAndroid:packageReactNdkLibsForBuck` - react_native_target("jni/prebuilt:reactnative-libs"), - ], -) - -android_resource( - name = "res", - package = "com.facebook.react.tictactoe", - res = "src/main/res", -) diff --git a/Examples/TicTacToe/android/app/build.gradle b/Examples/TicTacToe/android/app/build.gradle deleted file mode 100644 index 94509eca564bdb..00000000000000 --- a/Examples/TicTacToe/android/app/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 23 - buildToolsVersion "23.0.1" - - defaultConfig { - applicationId "com.facebook.react.tictactoe" - minSdkVersion 16 - targetSdkVersion 22 - versionCode 1 - versionName "1.0" - ndk { - abiFilters "armeabi-v7a", "x86" - } - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.0.1' - - // Build React Native from source - compile project(':ReactAndroid') -} diff --git a/Examples/TicTacToe/android/app/gradle.properties b/Examples/TicTacToe/android/app/gradle.properties deleted file mode 100644 index dfbe478748b403..00000000000000 --- a/Examples/TicTacToe/android/app/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -android.useDeprecatedNdk=true diff --git a/Examples/TicTacToe/android/app/proguard-rules.pro b/Examples/TicTacToe/android/app/proguard-rules.pro deleted file mode 100644 index a92fa177ee49f0..00000000000000 --- a/Examples/TicTacToe/android/app/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/Examples/TicTacToe/android/app/src/main/AndroidManifest.xml b/Examples/TicTacToe/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index a6dbe21557ad43..00000000000000 --- a/Examples/TicTacToe/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Examples/TicTacToe/android/app/src/main/java/com/facebook/react/tictactoe/TicTacToeActivity.java b/Examples/TicTacToe/android/app/src/main/java/com/facebook/react/tictactoe/TicTacToeActivity.java deleted file mode 100644 index 3589ea4e5a6fe1..00000000000000 --- a/Examples/TicTacToe/android/app/src/main/java/com/facebook/react/tictactoe/TicTacToeActivity.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.facebook.react.tictactoe; - -import com.facebook.react.ReactActivity; - -public class TicTacToeActivity extends ReactActivity { - @Override - protected String getMainComponentName() { - return "TicTacToeApp"; - } -} diff --git a/Examples/TicTacToe/android/app/src/main/java/com/facebook/react/tictactoe/TicTacToeApplication.java b/Examples/TicTacToe/android/app/src/main/java/com/facebook/react/tictactoe/TicTacToeApplication.java deleted file mode 100644 index c765a847cf39cd..00000000000000 --- a/Examples/TicTacToe/android/app/src/main/java/com/facebook/react/tictactoe/TicTacToeApplication.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.facebook.react.tictactoe; - -import android.app.Application; - -import com.facebook.react.ReactApplication; -import com.facebook.react.ReactNativeHost; -import com.facebook.react.ReactPackage; -import com.facebook.react.shell.MainReactPackage; - -import java.util.Arrays; -import java.util.List; - -import javax.annotation.Nullable; - -public class TicTacToeApplication extends Application implements ReactApplication { - private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { - @Override - public String getJSMainModuleName() { - return "Examples/TicTacToe/TicTacToeApp"; - } - - @Override - public @Nullable String getBundleAssetName() { - return "TicTacToeApp.bundle"; - } - - @Override - public boolean getUseDeveloperSupport() { - return true; - } - - @Override - protected List getPackages() { - return Arrays.asList( - new MainReactPackage() - ); - } - }; - - @Override - public ReactNativeHost getReactNativeHost() { - return mReactNativeHost; - } -} diff --git a/Examples/TicTacToe/android/app/src/main/res/drawable/launcher_icon.png b/Examples/TicTacToe/android/app/src/main/res/drawable/launcher_icon.png deleted file mode 100644 index a4aa3bd6d9acdc..00000000000000 Binary files a/Examples/TicTacToe/android/app/src/main/res/drawable/launcher_icon.png and /dev/null differ diff --git a/Examples/TicTacToe/android/app/src/main/res/layout/activity_main.xml b/Examples/TicTacToe/android/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 7d338d621d0232..00000000000000 --- a/Examples/TicTacToe/android/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/Examples/TicTacToe/android/app/src/main/res/values/strings.xml b/Examples/TicTacToe/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 26cdf63c68463c..00000000000000 --- a/Examples/TicTacToe/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - TicTacToeApp - diff --git a/Examples/TicTacToe/android/app/src/main/res/values/styles.xml b/Examples/TicTacToe/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 319eb0ca100b5a..00000000000000 --- a/Examples/TicTacToe/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/Examples/UIExplorer/.project b/Examples/UIExplorer/.project new file mode 100644 index 00000000000000..bbe67d10e935b6 --- /dev/null +++ b/Examples/UIExplorer/.project @@ -0,0 +1,12 @@ + + + UIExplorer + Project UIExplorer created by Buildship. + + + + + + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/Examples/UIExplorer/.settings/org.eclipse.buildship.core.prefs b/Examples/UIExplorer/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000000000..abb1f3437834e4 --- /dev/null +++ b/Examples/UIExplorer/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,3 @@ +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir=../.. +eclipse.preferences.version=1 diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index 228917af5f2e43..484503d464fbe7 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -55,6 +55,10 @@ 14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; }; 14D6D7291B2222EF001FB087 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; }; 14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; }; + 192F69B81E82409A008692C7 /* RCTAnimationUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B51E82409A008692C7 /* RCTAnimationUtilsTests.m */; }; + 192F69B91E82409A008692C7 /* RCTConvert_YGValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B61E82409A008692C7 /* RCTConvert_YGValueTests.m */; }; + 192F69BA1E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B71E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m */; }; + 192F69DA1E8240E2008692C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13E501A31D07A502005F35D8 /* libRCTAnimation.a */; }; 272E6B3F1BEA849E001FCF37 /* UpdatePropertiesExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.m */; }; 27B885561BED29AF00008352 /* RCTRootViewIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */; }; 27F441EC1BEBE5030039B79C /* FlexibleSizeExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.m */; }; @@ -422,6 +426,9 @@ 14D6D7101B220EB3001FB087 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = ""; }; 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = ../../Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj; sourceTree = ""; }; 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; + 192F69B51E82409A008692C7 /* RCTAnimationUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationUtilsTests.m; sourceTree = ""; }; + 192F69B61E82409A008692C7 /* RCTConvert_YGValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_YGValueTests.m; sourceTree = ""; }; + 192F69B71E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNativeAnimatedNodesManagerTests.m; sourceTree = ""; }; 272E6B3B1BEA849E001FCF37 /* UpdatePropertiesExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UpdatePropertiesExampleView.h; path = UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.h; sourceTree = ""; }; 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UpdatePropertiesExampleView.m; path = UIExplorer/NativeExampleViews/UpdatePropertiesExampleView.m; sourceTree = ""; }; 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootViewIntegrationTests.m; sourceTree = ""; }; @@ -456,6 +463,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 192F69DA1E8240E2008692C7 /* libRCTAnimation.a in Frameworks */, 14D6D71E1B2222EF001FB087 /* libRCTActionSheet.a in Frameworks */, 14D6D71F1B2222EF001FB087 /* libRCTAdSupport.a in Frameworks */, 14D6D7201B2222EF001FB087 /* libRCTGeolocation.a in Frameworks */, @@ -660,6 +668,9 @@ 143BC57C1B21E18100462512 /* UIExplorerUnitTests */ = { isa = PBXGroup; children = ( + 192F69B51E82409A008692C7 /* RCTAnimationUtilsTests.m */, + 192F69B61E82409A008692C7 /* RCTConvert_YGValueTests.m */, + 192F69B71E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m */, 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */, 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */, 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */, @@ -727,11 +738,11 @@ 14AADF041AC3DB95002390C9 /* libReact.a */, 2DD323D91DA2DD8B000FE1B8 /* libReact.a */, 3D3C08811DE3424E00C268FA /* libyoga.a */, - 3D3C08831DE3424E00C268FA /* libyoga-tvOS.a */, + 3D3C08831DE3424E00C268FA /* libyoga.a */, 3D05748C1DE6008900184BB4 /* libcxxreact.a */, - 3D05748E1DE6008900184BB4 /* libcxxreact-tvOS.a */, + 3D05748E1DE6008900184BB4 /* libcxxreact.a */, 3D0574901DE6008900184BB4 /* libjschelpers.a */, - 3D0574921DE6008900184BB4 /* libjschelpers-tvOS.a */, + 3D0574921DE6008900184BB4 /* libjschelpers.a */, ); name = Products; sourceTree = ""; @@ -997,7 +1008,7 @@ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0820; ORGANIZATIONNAME = Facebook; TargetAttributes = { 004D289D1AAF61C70097A701 = { @@ -1269,7 +1280,7 @@ remoteRef = 3D05748B1DE6008900184BB4 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 3D05748E1DE6008900184BB4 /* libcxxreact-tvOS.a */ = { + 3D05748E1DE6008900184BB4 /* libcxxreact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libcxxreact.a; @@ -1283,7 +1294,7 @@ remoteRef = 3D05748F1DE6008900184BB4 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 3D0574921DE6008900184BB4 /* libjschelpers-tvOS.a */ = { + 3D0574921DE6008900184BB4 /* libjschelpers.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libjschelpers.a; @@ -1297,7 +1308,7 @@ remoteRef = 3D3C08801DE3424E00C268FA /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 3D3C08831DE3424E00C268FA /* libyoga-tvOS.a */ = { + 3D3C08831DE3424E00C268FA /* libyoga.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libyoga.a; @@ -1433,8 +1444,10 @@ 1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */, 1497CFAE1B21F5E400C1F8F2 /* RCTJSCExecutorTests.m in Sources */, 13129DD41C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m in Sources */, + 192F69B81E82409A008692C7 /* RCTAnimationUtilsTests.m in Sources */, 1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */, 134CB92A1C85A38800265FA6 /* RCTModuleInitTests.m in Sources */, + 192F69BA1E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m in Sources */, 1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */, 1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */, 13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */, @@ -1445,6 +1458,7 @@ 39AA31A41DC1DFDC000F7EBB /* RCTUnicodeDecodeTests.m in Sources */, 13B6C1A31C34225900D3FAF5 /* RCTURLUtilsTests.m in Sources */, 8385CF041B87479200C6273E /* RCTImageLoaderHelpers.m in Sources */, + 192F69B91E82409A008692C7 /* RCTConvert_YGValueTests.m in Sources */, BC9C03401DC9F1D600B1C635 /* RCTDevMenuTests.m in Sources */, 68FF44381CF6111500720EFD /* RCTBundleURLProviderTests.m in Sources */, 8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */, @@ -1613,9 +1627,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/UIExplorer/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp; PRODUCT_NAME = UIExplorer; @@ -1629,7 +1641,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = V9WTTPBFK9; INFOPLIST_FILE = "$(SRCROOT)/UIExplorer/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp; PRODUCT_NAME = UIExplorer; @@ -1861,9 +1872,12 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -1872,6 +1886,7 @@ ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -1904,9 +1919,7 @@ WARNING_CFLAGS = ( "-Wextra", "-Wall", - "-Wincompatible-pointer-types", - "-Wincompatible-pointer-types-discards-qualifiers", - "-Wshadow", + "-Wno-semicolon-before-method-body", ); }; name = Debug; @@ -1927,9 +1940,12 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -1937,6 +1953,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; @@ -1963,9 +1980,7 @@ WARNING_CFLAGS = ( "-Wextra", "-Wall", - "-Wincompatible-pointer-types", - "-Wincompatible-pointer-types-discards-qualifiers", - "-Wshadow", + "-Wno-semicolon-before-method-body", ); }; name = Release; diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer-tvOS.xcscheme b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer-tvOS.xcscheme index 190472449b9d2b..5cb85ce16ba19a 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer-tvOS.xcscheme +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer-tvOS.xcscheme @@ -1,6 +1,6 @@ - -#import - -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - [RCTCxxBridge enable]; - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/Examples/UIExplorer/UIExplorerCxx.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/project.pbxproj index ad832206603f87..561367c549fb25 100644 --- a/Examples/UIExplorer/UIExplorerCxx.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; }; 134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; }; 134CB92A1C85A38800265FA6 /* RCTModuleInitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */; }; + 1380DCD41E70C44800E7C47D /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1380DC991E70C0DD00E7C47D /* libReact.a */; }; 138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; }; 138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */; }; 1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; }; @@ -23,10 +24,9 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 13B6C1A31C34225900D3FAF5 /* RCTURLUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */; }; 13BCE84F1C9C209600DD7AAD /* RCTComponentPropsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BCE84E1C9C209600DD7AAD /* RCTComponentPropsTests.m */; }; - 13CF8FD11E2966FF0005310D /* maincxx.m in Sources */ = {isa = PBXBuildFile; fileRef = 13CF8FD01E2966FF0005310D /* maincxx.m */; }; - 13CF8FF31E2967D10005310D /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13CF8FE01E2967C40005310D /* libReact.a */; }; 13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */; }; 13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; }; 13E501F11D07A84A005F35D8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13E501A31D07A502005F35D8 /* libRCTAnimation.a */; }; @@ -90,6 +90,7 @@ 2DD323DD1DA2DDBF000FE1B8 /* UpdatePropertiesExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.m */; }; 2DD323DE1DA2DDBF000FE1B8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 2DD323DF1DA2DDBF000FE1B8 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 2DD323E01DA2DDBF000FE1B8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 2DD323E11DA2DDBF000FE1B8 /* legacy_image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D2AFAF41D646CF80089D1A3 /* legacy_image@2x.png */; }; 2DD323E21DA2DDBF000FE1B8 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB61A68108700A75B9A /* Info.plist */; }; 2DD323E31DA2DE3F000FE1B8 /* libRCTAnimation-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323B51DA2DD8B000FE1B8 /* libRCTAnimation-tvOS.a */; }; @@ -154,89 +155,89 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTGeolocation; }; - 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; - 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; - 13CF8FDF1E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DC981E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = ReactCxx; + remoteInfo = React; }; - 13CF8FE11E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DC9A1E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2D2A28131D9B038B00D4039D; - remoteInfo = "ReactCxx-tvOS"; + remoteInfo = "React-tvOS"; }; - 13CF8FE31E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DC9C1E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3C059A1DE3340900C268FA; remoteInfo = yoga; }; - 13CF8FE51E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DC9E1E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3C06751DE3340C00C268FA; remoteInfo = "yoga-tvOS"; }; - 13CF8FE71E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA01E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; remoteInfo = cxxreact; }; - 13CF8FE91E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA21E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; remoteInfo = "cxxreact-tvOS"; }; - 13CF8FEB1E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA41E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; remoteInfo = jschelpers; }; - 13CF8FED1E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA61E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; remoteInfo = "jschelpers-tvOS"; }; - 13CF8FEF1E2967C40005310D /* PBXContainerItemProxy */ = { + 1380DCA81E70C0DD00E7C47D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; + remoteInfo = "third-party"; + }; + 1380DCAA1E70C0DD00E7C47D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; proxyType = 2; remoteGlobalIDString = 139D7E881E25C6D100323FB7; remoteInfo = "double-conversion"; }; - 13CF8FF11E2967C40005310D /* PBXContainerItemProxy */ = { + 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + containerPortal = 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; - remoteInfo = "third-party"; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; + 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3C86DF461ADF2C930047B81A; + remoteInfo = RCTWebSocket; }; 13E501A21D07A502005F35D8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -391,6 +392,7 @@ 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = ""; }; 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = ""; }; 134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleInitTests.m; sourceTree = ""; }; + 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactCxx.xcodeproj; path = ../../React/ReactCxx.xcodeproj; sourceTree = ""; }; 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = ""; }; 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = ../../Libraries/CameraRoll/RCTCameraRoll.xcodeproj; sourceTree = ""; }; 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = ""; }; @@ -401,11 +403,10 @@ 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = UIExplorer/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = ""; }; 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTURLUtilsTests.m; sourceTree = ""; }; 13BCE84E1C9C209600DD7AAD /* RCTComponentPropsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTComponentPropsTests.m; sourceTree = ""; }; 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; - 13CF8FD01E2966FF0005310D /* maincxx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = maincxx.m; path = UIExplorer/maincxx.m; sourceTree = ""; }; - 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactCxx.xcodeproj; path = ../../React/ReactCxx.xcodeproj; sourceTree = ""; }; 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSONTests.m; sourceTree = ""; }; 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMethodArgumentTests.m; sourceTree = ""; }; 13E5019C1D07A502005F35D8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = ../../Libraries/NativeAnimation/RCTAnimation.xcodeproj; sourceTree = ""; }; @@ -486,7 +487,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 13CF8FF31E2967D10005310D /* libReact.a in Frameworks */, + 1380DCD41E70C44800E7C47D /* libReact.a in Frameworks */, 147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */, 134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */, 13E501F11D07A84A005F35D8 /* libRCTAnimation.a in Frameworks */, @@ -555,7 +556,7 @@ 1316A21D1AA397F400C0188E /* Libraries */ = { isa = PBXGroup; children = ( - 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */, + 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */, 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */, 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */, 13E5019C1D07A502005F35D8 /* RCTAnimation.xcodeproj */, @@ -626,6 +627,23 @@ name = Products; sourceTree = ""; }; + 1380DC8C1E70C0DC00E7C47D /* Products */ = { + isa = PBXGroup; + children = ( + 1380DC991E70C0DD00E7C47D /* libReact.a */, + 1380DC9B1E70C0DD00E7C47D /* libReact.a */, + 1380DC9D1E70C0DD00E7C47D /* libyoga.a */, + 1380DC9F1E70C0DD00E7C47D /* libyoga.a */, + 1380DCA11E70C0DD00E7C47D /* libcxxreact.a */, + 1380DCA31E70C0DD00E7C47D /* libcxxreact.a */, + 1380DCA51E70C0DD00E7C47D /* libjschelpers.a */, + 1380DCA71E70C0DD00E7C47D /* libjschelpers.a */, + 1380DCA91E70C0DD00E7C47D /* libthird-party.a */, + 1380DCAB1E70C0DD00E7C47D /* libdouble-conversion.a */, + ); + name = Products; + sourceTree = ""; + }; 138DEE031B9EDDDB007F4EA5 /* Products */ = { isa = PBXGroup; children = ( @@ -651,29 +669,12 @@ 13B07FB01A68108700A75B9A /* AppDelegate.m */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13CF8FD01E2966FF0005310D /* maincxx.m */, + 13B07FB71A68108700A75B9A /* main.m */, 1323F18D1C04ABAC0091BED0 /* Supporting Files */, ); name = UIExplorer; sourceTree = ""; }; - 13CF8FD31E2967C40005310D /* Products */ = { - isa = PBXGroup; - children = ( - 13CF8FE01E2967C40005310D /* libReact.a */, - 13CF8FE21E2967C40005310D /* libReact.a */, - 13CF8FE41E2967C40005310D /* libyoga.a */, - 13CF8FE61E2967C40005310D /* libyoga.a */, - 13CF8FE81E2967C40005310D /* libcxxreact.a */, - 13CF8FEA1E2967C40005310D /* libcxxreact.a */, - 13CF8FEC1E2967C40005310D /* libjschelpers.a */, - 13CF8FEE1E2967C40005310D /* libjschelpers.a */, - 13CF8FF21E2967C40005310D /* libthird-party.a */, - 13CF8FF01E2967C40005310D /* libdouble-conversion.a */, - ); - name = Products; - sourceTree = ""; - }; 13E5019D1D07A502005F35D8 /* Products */ = { isa = PBXGroup; children = ( @@ -1008,7 +1009,7 @@ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0820; ORGANIZATIONNAME = Facebook; TargetAttributes = { 004D289D1AAF61C70097A701 = { @@ -1101,8 +1102,8 @@ ProjectRef = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; }, { - ProductGroup = 13CF8FD31E2967C40005310D /* Products */; - ProjectRef = 13CF8FD21E2967C40005310D /* ReactCxx.xcodeproj */; + ProductGroup = 1380DC8C1E70C0DC00E7C47D /* Products */; + ProjectRef = 1380DC8B1E70C0DC00E7C47D /* ReactCxx.xcodeproj */; }, ); projectRoot = ""; @@ -1154,88 +1155,88 @@ remoteRef = 134A8A241AACED6A00945AAE /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTCameraRoll.a; - remoteRef = 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 13CF8FE01E2967C40005310D /* libReact.a */ = { + 1380DC991E70C0DD00E7C47D /* libReact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libReact.a; - remoteRef = 13CF8FDF1E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DC981E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FE21E2967C40005310D /* libReact.a */ = { + 1380DC9B1E70C0DD00E7C47D /* libReact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libReact.a; - remoteRef = 13CF8FE11E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DC9A1E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FE41E2967C40005310D /* libyoga.a */ = { + 1380DC9D1E70C0DD00E7C47D /* libyoga.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libyoga.a; - remoteRef = 13CF8FE31E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DC9C1E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FE61E2967C40005310D /* libyoga.a */ = { + 1380DC9F1E70C0DD00E7C47D /* libyoga.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libyoga.a; - remoteRef = 13CF8FE51E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DC9E1E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FE81E2967C40005310D /* libcxxreact.a */ = { + 1380DCA11E70C0DD00E7C47D /* libcxxreact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libcxxreact.a; - remoteRef = 13CF8FE71E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCA01E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FEA1E2967C40005310D /* libcxxreact.a */ = { + 1380DCA31E70C0DD00E7C47D /* libcxxreact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libcxxreact.a; - remoteRef = 13CF8FE91E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCA21E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FEC1E2967C40005310D /* libjschelpers.a */ = { + 1380DCA51E70C0DD00E7C47D /* libjschelpers.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libjschelpers.a; - remoteRef = 13CF8FEB1E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCA41E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FEE1E2967C40005310D /* libjschelpers.a */ = { + 1380DCA71E70C0DD00E7C47D /* libjschelpers.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libjschelpers.a; - remoteRef = 13CF8FED1E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCA61E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FF01E2967C40005310D /* libdouble-conversion.a */ = { + 1380DCA91E70C0DD00E7C47D /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = 1380DCA81E70C0DD00E7C47D /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 1380DCAB1E70C0DD00E7C47D /* libdouble-conversion.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libdouble-conversion.a"; - remoteRef = 13CF8FEF1E2967C40005310D /* PBXContainerItemProxy */; + remoteRef = 1380DCAA1E70C0DD00E7C47D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 13CF8FF21E2967C40005310D /* libthird-party.a */ = { + 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libthird-party.a"; - remoteRef = 13CF8FF11E2967C40005310D /* PBXContainerItemProxy */; + path = libRCTCameraRoll.a; + remoteRef = 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWebSocket.a; + remoteRef = 139FDED81B0651EA00C62182 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 13E501A31D07A502005F35D8 /* libRCTAnimation.a */ = { @@ -1480,10 +1481,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 13CF8FD11E2966FF0005310D /* maincxx.m in Sources */, 272E6B3F1BEA849E001FCF37 /* UpdatePropertiesExampleView.m in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 27F441EC1BEBE5030039B79C /* FlexibleSizeExampleView.m in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1517,6 +1518,7 @@ files = ( 2DD323DC1DA2DDBF000FE1B8 /* FlexibleSizeExampleView.m in Sources */, 2DD323DD1DA2DDBF000FE1B8 /* UpdatePropertiesExampleView.m in Sources */, + 2DD323E01DA2DDBF000FE1B8 /* main.m in Sources */, 2DD323DE1DA2DDBF000FE1B8 /* AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1885,9 +1887,11 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -1896,6 +1900,7 @@ ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -1951,9 +1956,11 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -1961,6 +1968,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; diff --git a/Examples/Movies/Movies.xcodeproj/xcshareddata/xcschemes/Movies.xcscheme b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer-tvOS.xcscheme similarity index 52% rename from Examples/Movies/Movies.xcodeproj/xcshareddata/xcschemes/Movies.xcscheme rename to Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer-tvOS.xcscheme index f1006908055e93..cd6ee8cea9e99a 100644 --- a/Examples/Movies/Movies.xcodeproj/xcshareddata/xcschemes/Movies.xcscheme +++ b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer-tvOS.xcscheme @@ -1,6 +1,6 @@ + BlueprintName = "React-tvOS" + ReferencedContainer = "container:../../React/ReactCxx.xcodeproj"> + BlueprintIdentifier = "3D13F83D1D6F6AE000E69E0E" + BuildableName = "UIExplorerBundle.bundle" + BlueprintName = "UIExplorerBundle" + ReferencedContainer = "container:UIExplorerCxx.xcodeproj"> + + + + @@ -42,14 +56,34 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + + BlueprintIdentifier = "2DD3238F1DA2DD8A000FE1B8" + BuildableName = "UIExplorer-tvOS.app" + BlueprintName = "UIExplorer-tvOS" + ReferencedContainer = "container:UIExplorerCxx.xcodeproj"> @@ -69,10 +103,10 @@ runnableDebuggingMode = "0"> + BlueprintIdentifier = "2DD3238F1DA2DD8A000FE1B8" + BuildableName = "UIExplorer-tvOS.app" + BlueprintName = "UIExplorer-tvOS" + ReferencedContainer = "container:UIExplorerCxx.xcodeproj"> @@ -88,10 +122,10 @@ runnableDebuggingMode = "0"> + BlueprintIdentifier = "2DD3238F1DA2DD8A000FE1B8" + BuildableName = "UIExplorer-tvOS.app" + BlueprintName = "UIExplorer-tvOS" + ReferencedContainer = "container:UIExplorerCxx.xcodeproj"> diff --git a/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme index aa4516c991545d..4410a73359c712 100644 --- a/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme +++ b/Examples/UIExplorer/UIExplorerCxx.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m index 1ff6f6555f2af1..ca31db98812fec 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m @@ -32,7 +32,7 @@ #import #define RCT_TEST_DATA_CONFIGURATION_BLOCK(appName, testType, input, block) \ -- (void)test##appName##_##testType##_##input \ +- (void)DISABLED_test##appName##_##testType##_##input \ { \ [_runner runTest:_cmd \ module:@#appName \ @@ -41,7 +41,7 @@ - (void)test##appName##_##testType##_##input \ } #define RCT_TEST_CONFIGURATION_BLOCK(appName, block) \ -- (void)test##appName \ +- (void)DISABLED_test##appName \ { \ [_runner runTest:_cmd \ module:@#appName \ @@ -121,13 +121,6 @@ static ControlBlock reactContentSizeUpdateBlock(RCTRootViewSizeFlexibility sizeF }; } -static ControlBlock propertiesUpdateBlock() -{ - return ^(RCTRootView *rootView){ - rootView.appProperties = @{@"markTestPassed":@YES}; - }; -} - @interface RCTRootViewIntegrationTests : XCTestCase @end @@ -162,6 +155,8 @@ - (void)setUp RCT_TEST_CONFIGURATION_BLOCK(ReactContentSizeUpdateTest, reactContentSizeUpdateBlock(RCTBoth)) // Test if setting 'appProperties' property updates the RN app -RCT_TEST_CONFIGURATION_BLOCK(PropertiesUpdateTest, propertiesUpdateBlock()) +RCT_TEST_CONFIGURATION_BLOCK(PropertiesUpdateTest, ^(RCTRootView *rootView) { + rootView.appProperties = @{@"markTestPassed":@YES}; +}) @end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTAnimationUtilsTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTAnimationUtilsTests.m new file mode 100644 index 00000000000000..53f176f1232f79 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTAnimationUtilsTests.m @@ -0,0 +1,122 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#import + +@interface RCTAnimationUtilsTests : XCTestCase + +@end + +static CGFloat RCTSimpleInterpolation(CGFloat value, NSArray *inputRange, NSArray *outputRange) { + return RCTInterpolateValueInRange(value, + inputRange, + outputRange, + EXTRAPOLATE_TYPE_EXTEND, + EXTRAPOLATE_TYPE_EXTEND); +} + +@implementation RCTAnimationUtilsTests + +// RCTInterpolateValueInRange + +- (void)testSimpleOneToOneMapping +{ + NSArray *input = @[@0, @1]; + NSArray *output = @[@0, @1]; + XCTAssertEqual(RCTSimpleInterpolation(0, input, output), 0); + XCTAssertEqual(RCTSimpleInterpolation(0.5, input, output), 0.5); + XCTAssertEqual(RCTSimpleInterpolation(0.8, input, output), 0.8); + XCTAssertEqual(RCTSimpleInterpolation(1, input, output), 1); +} + +- (void)testWiderOutputRange +{ + NSArray *input = @[@0, @1]; + NSArray *output = @[@100, @200]; + XCTAssertEqual(RCTSimpleInterpolation(0, input, output), 100); + XCTAssertEqual(RCTSimpleInterpolation(0.5, input, output), 150); + XCTAssertEqual(RCTSimpleInterpolation(0.8, input, output), 180); + XCTAssertEqual(RCTSimpleInterpolation(1, input, output), 200); +} + +- (void)testWiderInputRange +{ + NSArray *input = @[@2000, @3000]; + NSArray *output = @[@1, @2]; + XCTAssertEqual(RCTSimpleInterpolation(2000, input, output), 1); + XCTAssertEqual(RCTSimpleInterpolation(2250, input, output), 1.25); + XCTAssertEqual(RCTSimpleInterpolation(2800, input, output), 1.8); + XCTAssertEqual(RCTSimpleInterpolation(3000, input, output), 2); +} + +- (void)testManySegments +{ + NSArray *input = @[@-1, @1, @5]; + NSArray *output = @[@0, @10, @20]; + XCTAssertEqual(RCTSimpleInterpolation(-1, input, output), 0); + XCTAssertEqual(RCTSimpleInterpolation(0, input, output), 5); + XCTAssertEqual(RCTSimpleInterpolation(1, input, output), 10); + XCTAssertEqual(RCTSimpleInterpolation(2, input, output), 12.5); + XCTAssertEqual(RCTSimpleInterpolation(5, input, output), 20); +} + +- (void)testExtendExtrapolate +{ + NSArray *input = @[@10, @20]; + NSArray *output = @[@0, @1]; + XCTAssertEqual(RCTSimpleInterpolation(30, input, output), 2); + XCTAssertEqual(RCTSimpleInterpolation(5, input, output), -0.5); +} + +- (void)testClampExtrapolate +{ + NSArray *input = @[@10, @20]; + NSArray *output = @[@0, @1]; + CGFloat value; + value = RCTInterpolateValueInRange(30, + input, + output, + EXTRAPOLATE_TYPE_CLAMP, + EXTRAPOLATE_TYPE_CLAMP); + XCTAssertEqual(value, 1); + value = RCTInterpolateValueInRange(5, + input, + output, + EXTRAPOLATE_TYPE_CLAMP, + EXTRAPOLATE_TYPE_CLAMP); + XCTAssertEqual(value, 0); +} + +- (void)testIdentityExtrapolate +{ + NSArray *input = @[@10, @20]; + NSArray *output = @[@0, @1]; + CGFloat value; + value = RCTInterpolateValueInRange(30, + input, + output, + EXTRAPOLATE_TYPE_IDENTITY, + EXTRAPOLATE_TYPE_IDENTITY); + XCTAssertEqual(value, 30); + value = RCTInterpolateValueInRange(5, + input, + output, + EXTRAPOLATE_TYPE_IDENTITY, + EXTRAPOLATE_TYPE_IDENTITY); + XCTAssertEqual(value, 5); +} + +@end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTNativeAnimatedNodesManagerTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTNativeAnimatedNodesManagerTests.m new file mode 100644 index 00000000000000..5b7016dfd8c8ed --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTNativeAnimatedNodesManagerTests.m @@ -0,0 +1,666 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#import + +#import +#import +#import + +static const NSTimeInterval FRAME_LENGTH = 1.0 / 60.0; + +@interface RCTFakeDisplayLink : CADisplayLink + +@end + +@implementation RCTFakeDisplayLink +{ + NSTimeInterval _timestamp; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _timestamp = 1124.1234143251; // Random + } + return self; +} + +- (NSTimeInterval)timestamp +{ + _timestamp += FRAME_LENGTH; + return _timestamp; +} + +@end + +@interface RCTFakeValueObserver : NSObject + +@property (nonatomic, strong) NSMutableArray *calls; + +@end + +@implementation RCTFakeValueObserver + +- (instancetype)init +{ + self = [super init]; + if (self) { + _calls = [NSMutableArray new]; + } + return self; +} + +- (void)animatedNode:(__unused RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value +{ + [_calls addObject:@(value)]; +} + +@end + +@interface RCTFakeEvent : NSObject + +@end + +@implementation RCTFakeEvent +{ + NSArray *_arguments; +} + +@synthesize eventName = _eventName; +@synthesize viewTag = _viewTag; +@synthesize coalescingKey = _coalescingKey; + +- (instancetype)initWithName:(NSString *)name viewTag:(NSNumber *)viewTag arguments:(NSArray *)arguments +{ + self = [super init]; + if (self) { + _eventName = name; + _viewTag = viewTag; + _arguments = arguments; + } + return self; +} + +- (NSArray *)arguments +{ + return _arguments; +} + +RCT_NOT_IMPLEMENTED(+ (NSString *)moduleDotMethod); +RCT_NOT_IMPLEMENTED(- (BOOL)canCoalesce); +RCT_NOT_IMPLEMENTED(- (id)coalesceWithEvent:(id)newEvent); + +@end + +static id RCTPropChecker(NSString *prop, NSNumber *value) +{ + return [OCMArg checkWithBlock:^BOOL(NSDictionary *props) { + BOOL match = fabs(props[prop].doubleValue - value.doubleValue) < FLT_EPSILON; + if (!match) { + NSLog(@"Props `%@` with value `%@` is not close to `%@`", prop, props[prop], value); + } + return match; + }]; +} + +@interface RCTNativeAnimatedNodesManagerTests : XCTestCase + +@end + +@implementation RCTNativeAnimatedNodesManagerTests +{ + id _uiManager; + RCTNativeAnimatedNodesManager *_nodesManager; + RCTFakeDisplayLink *_displayLink; +} + +- (void)setUp +{ + [super setUp]; + + _uiManager = [OCMockObject niceMockForClass:[RCTUIManager class]]; + _nodesManager = [[RCTNativeAnimatedNodesManager alloc] initWithUIManager:_uiManager]; + _displayLink = [RCTFakeDisplayLink new]; +} + +/** + * Generates a simple animated nodes graph and attaches the props node to a given viewTag + * Parameter opacity is used as a initial value for the "opacity" attribute. + * + * Nodes are connected as follows (nodes IDs in parens): + * ValueNode(1) -> StyleNode(2) -> PropNode(3) + */ +- (void)createSimpleAnimatedView:(NSNumber *)viewTag withOpacity:(CGFloat)opacity +{ + [_nodesManager createAnimatedNode:@1 + config:@{@"type": @"value", @"value": @(opacity), @"offset": @0}]; + [_nodesManager createAnimatedNode:@2 + config:@{@"type": @"style", @"style": @{@"opacity": @1}}]; + [_nodesManager createAnimatedNode:@3 + config:@{@"type": @"props", @"props": @{@"style": @2}}]; + + [_nodesManager connectAnimatedNodes:@1 childTag:@2]; + [_nodesManager connectAnimatedNodes:@2 childTag:@3]; + [_nodesManager connectAnimatedNodeToView:@3 viewTag:viewTag viewName:@"UIView"]; +} + +- (void)testFramesAnimation +{ + [self createSimpleAnimatedView:@1000 withOpacity:0]; + NSArray *frames = @[@0, @0.2, @0.4, @0.6, @0.8, @1]; + [_nodesManager startAnimatingNode:@1 + nodeTag:@1 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @1} + endCallback:nil]; + + for (NSNumber *frame in frames) { + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:@1000 + viewName:@"UIView" + props:RCTPropChecker(@"opacity", frame)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + } + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:@1000 + viewName:@"UIView" + props:RCTPropChecker(@"opacity", @1)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; +} + +- (void)testNodeValueListenerIfNotListening +{ + NSNumber *nodeId = @1; + [self createSimpleAnimatedView:@1000 withOpacity:0]; + NSArray *frames = @[@0, @0.2, @0.4, @0.6, @0.8, @1]; + + RCTFakeValueObserver *observer = [RCTFakeValueObserver new]; + [_nodesManager startListeningToAnimatedNodeValue:nodeId valueObserver:observer]; + + [_nodesManager startAnimatingNode:@1 + nodeTag:nodeId + config:@{@"type": @"frames", @"frames": frames, @"toValue": @1} + endCallback:nil]; + + [_nodesManager stepAnimations:_displayLink]; + XCTAssertEqual(observer.calls.count, 1UL); + XCTAssertEqualObjects(observer.calls[0], @0); + + [_nodesManager stopListeningToAnimatedNodeValue:nodeId]; + + [_nodesManager stepAnimations:_displayLink]; + XCTAssertEqual(observer.calls.count, 1UL); +} + +- (void)testNodeValueListenerIfListening +{ + NSNumber *nodeId = @1; + [self createSimpleAnimatedView:@1000 withOpacity:0]; + NSArray *frames = @[@0, @0.2, @0.4, @0.6, @0.8, @1]; + + RCTFakeValueObserver *observer = [RCTFakeValueObserver new]; + [_nodesManager startListeningToAnimatedNodeValue:nodeId valueObserver:observer]; + + [_nodesManager startAnimatingNode:@1 + nodeTag:nodeId + config:@{@"type": @"frames", @"frames": frames, @"toValue": @1} + endCallback:nil]; + + for (NSUInteger i = 0; i < frames.count; i++) { + [_nodesManager stepAnimations:_displayLink]; + XCTAssertEqual(observer.calls.count, i + 1); + XCTAssertEqualWithAccuracy(observer.calls[i].doubleValue, frames[i].doubleValue, FLT_EPSILON); + } + + [_nodesManager stepAnimations:_displayLink]; + XCTAssertEqual(observer.calls.count, 7UL); + XCTAssertEqualObjects(observer.calls[6], @1); + + [_nodesManager stepAnimations:_displayLink]; + XCTAssertEqual(observer.calls.count, 7UL); +} + +- (void)testSpringAnimation +{ + [self createSimpleAnimatedView:@1000 withOpacity:0]; + [_nodesManager startAnimatingNode:@1 + nodeTag:@1 + config:@{@"type": @"spring", + @"friction": @7, + @"tension": @40, + @"initialVelocity": @0, + @"toValue": @1, + @"restSpeedThreshold": @0.001, + @"restDisplacementThreshold": @0.001, + @"overshootClamping": @NO} + endCallback:nil]; + + BOOL wasGreaterThanOne = NO; + CGFloat previousValue = 0; + __block CGFloat currentValue; + [[[_uiManager stub] andDo:^(NSInvocation *invocation) { + __unsafe_unretained NSDictionary *props; + [invocation getArgument:&props atIndex:4]; + currentValue = props[@"opacity"].doubleValue; + }] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + + // Run for 3 seconds. + for (NSUInteger i = 0; i < 3 * 60; i++) { + [_nodesManager stepAnimations:_displayLink]; + + if (currentValue > 1) { + wasGreaterThanOne = YES; + } + + // Verify that animation step is relatively small. + XCTAssertLessThan(fabs(currentValue - previousValue), 0.1); + + previousValue = currentValue; + } + + // Verify that we've reach the final value at the end of animation. + XCTAssertEqual(previousValue, 1.0); + + // Verify that value has reached some maximum value that is greater than the final value (bounce). + XCTAssertTrue(wasGreaterThanOne); + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; +} + +- (void)testAnimationCallbackFinish +{ + [self createSimpleAnimatedView:@1000 withOpacity:0]; + NSArray *frames = @[@0, @1]; + + __block NSInteger endCallbackCalls = 0; + + RCTResponseSenderBlock endCallback = ^(NSArray *response) { + endCallbackCalls++; + XCTAssertEqualObjects(response, @[@{@"finished": @YES}]); + }; + + [_nodesManager startAnimatingNode:@1 + nodeTag:@1 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @1} + endCallback:endCallback]; + + [_nodesManager stepAnimations:_displayLink]; + [_nodesManager stepAnimations:_displayLink]; + XCTAssertEqual(endCallbackCalls, 0); + [_nodesManager stepAnimations:_displayLink]; + XCTAssertEqual(endCallbackCalls, 1); + [_nodesManager stepAnimations:_displayLink]; + XCTAssertEqual(endCallbackCalls, 1); +} + +/** + * Creates a following graph of nodes: + * Value(1, firstValue) ----> Add(3) ---> Style(4) ---> Props(5) ---> View(viewTag) + * | + * Value(2, secondValue) --+ + * + * Add(3) node maps to a "translateX" attribute of the Style(4) node. + */ +- (void)createAnimatedGraphWithAdditionNode:(NSNumber *)viewTag + firstValue:(CGFloat)firstValue + secondValue:(CGFloat)secondValue +{ + [_nodesManager createAnimatedNode:@1 + config:@{@"type": @"value", @"value": @(firstValue), @"offset": @0}]; + [_nodesManager createAnimatedNode:@2 + config:@{@"type": @"value", @"value": @(secondValue), @"offset": @0}]; + [_nodesManager createAnimatedNode:@3 + config:@{@"type": @"addition", @"input": @[@1, @2]}]; + [_nodesManager createAnimatedNode:@4 + config:@{@"type": @"style", @"style": @{@"translateX": @3}}]; + [_nodesManager createAnimatedNode:@5 + config:@{@"type": @"props", @"props": @{@"style": @4}}]; + + [_nodesManager connectAnimatedNodes:@1 childTag:@3]; + [_nodesManager connectAnimatedNodes:@2 childTag:@3]; + [_nodesManager connectAnimatedNodes:@3 childTag:@4]; + [_nodesManager connectAnimatedNodes:@4 childTag:@5]; + [_nodesManager connectAnimatedNodeToView:@5 viewTag:viewTag viewName:@"UIView"]; +} + +- (void)testAdditionNode +{ + NSNumber *viewTag = @50; + [self createAnimatedGraphWithAdditionNode:viewTag firstValue:100 secondValue:1000]; + + NSArray *frames = @[@0, @1]; + [_nodesManager startAnimatingNode:@1 + nodeTag:@1 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @101} + endCallback:nil]; + [_nodesManager startAnimatingNode:@2 + nodeTag:@2 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @1010} + endCallback:nil]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @1100)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @1111)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @1111)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; +} + +/** + * Verifies that views are updated properly when one of the addition input nodes has started animating + * while the other one has not. + * + * We expect that the output of the addition node will take the starting value of the second input + * node even though the node hasn't been connected to an active animation driver. + */ +- (void)testViewReceiveUpdatesIfOneOfAnimationHasntStarted +{ + NSNumber *viewTag = @50; + [self createAnimatedGraphWithAdditionNode:viewTag firstValue:100 secondValue:1000]; + + NSArray *frames = @[@0, @1]; + [_nodesManager startAnimatingNode:@1 + nodeTag:@1 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @101} + endCallback:nil]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @1100)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @1101)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @1101)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; +} + +/** + * Verifies that views are updated properly when one of the addition input nodes animation finishes + * before the other. + * + * We expect that the output of the addition node after one of the animation has finished will + * take the last value of the animated node and the view will receive updates up until the second + * animation is over. + */ +- (void)testViewReceiveUpdatesWhenOneOfAnimationHasFinished +{ + NSNumber *viewTag = @50; + [self createAnimatedGraphWithAdditionNode:viewTag firstValue:100 secondValue:1000]; + + NSArray *firstFrames = @[@0, @1]; + [_nodesManager startAnimatingNode:@1 + nodeTag:@1 + config:@{@"type": @"frames", @"frames": firstFrames, @"toValue": @200} + endCallback:nil]; + NSArray *secondFrames = @[@0, @0.2, @0.4, @0.6, @0.8, @1]; + [_nodesManager startAnimatingNode:@2 + nodeTag:@2 + config:@{@"type": @"frames", @"frames": secondFrames, @"toValue": @1010} + endCallback:nil]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @1100)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + for (NSUInteger i = 1; i < secondFrames.count; i++) { + CGFloat expected = 1200.0 + secondFrames[i].doubleValue * 10.0; + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @(expected))]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + } + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @1210)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; +} + +- (void)testMultiplicationNode +{ + NSNumber *viewTag = @50; + [_nodesManager createAnimatedNode:@1 + config:@{@"type": @"value", @"value": @1, @"offset": @0}]; + [_nodesManager createAnimatedNode:@2 + config:@{@"type": @"value", @"value": @5, @"offset": @0}]; + [_nodesManager createAnimatedNode:@3 + config:@{@"type": @"multiplication", @"input": @[@1, @2]}]; + [_nodesManager createAnimatedNode:@4 + config:@{@"type": @"style", @"style": @{@"translateX": @3}}]; + [_nodesManager createAnimatedNode:@5 + config:@{@"type": @"props", @"props": @{@"style": @4}}]; + + [_nodesManager connectAnimatedNodes:@1 childTag:@3]; + [_nodesManager connectAnimatedNodes:@2 childTag:@3]; + [_nodesManager connectAnimatedNodes:@3 childTag:@4]; + [_nodesManager connectAnimatedNodes:@4 childTag:@5]; + [_nodesManager connectAnimatedNodeToView:@5 viewTag:viewTag viewName:@"UIView"]; + + NSArray *frames = @[@0, @1]; + [_nodesManager startAnimatingNode:@1 + nodeTag:@1 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @2} + endCallback:nil]; + [_nodesManager startAnimatingNode:@2 + nodeTag:@2 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @10} + endCallback:nil]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @5)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @20)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"translateX", @20)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; +} + +- (void)testHandleStoppingAnimation +{ + [self createSimpleAnimatedView:@1000 withOpacity:0]; + NSArray *frames = @[@0, @0.2, @0.4, @0.6, @0.8, @1]; + + __block BOOL endCallbackCalled = NO; + + RCTResponseSenderBlock endCallback = ^(NSArray *response) { + endCallbackCalled = YES; + XCTAssertEqualObjects(response, @[@{@"finished": @NO}]); + }; + + [_nodesManager startAnimatingNode:@404 + nodeTag:@1 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @1} + endCallback:endCallback]; + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + [_nodesManager stopAnimation:@404]; + XCTAssertEqual(endCallbackCalled, YES); + + // Run "update" loop a few more times -> we expect no further updates nor callback calls to be + // triggered + for (NSUInteger i = 0; i < 5; i++) { + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + } +} + +- (void)testInterpolationNode +{ + NSNumber *viewTag = @50; + [_nodesManager createAnimatedNode:@1 + config:@{@"type": @"value", @"value": @10, @"offset": @0}]; + [_nodesManager createAnimatedNode:@2 + config:@{@"type": @"interpolation", + @"inputRange": @[@10, @20], + @"outputRange": @[@0, @1], + @"extrapolateLeft": @"extend", + @"extrapolateRight": @"extend"}]; + [_nodesManager createAnimatedNode:@3 + config:@{@"type": @"style", @"style": @{@"opacity": @2}}]; + [_nodesManager createAnimatedNode:@4 + config:@{@"type": @"props", @"props": @{@"style": @3}}]; + + [_nodesManager connectAnimatedNodes:@1 childTag:@2]; + [_nodesManager connectAnimatedNodes:@2 childTag:@3]; + [_nodesManager connectAnimatedNodes:@3 childTag:@4]; + [_nodesManager connectAnimatedNodeToView:@4 viewTag:viewTag viewName:@"UIView"]; + + NSArray *frames = @[@0, @0.2, @0.4, @0.6, @0.8, @1]; + [_nodesManager startAnimatingNode:@1 + nodeTag:@1 + config:@{@"type": @"frames", @"frames": frames, @"toValue": @20} + endCallback:nil]; + + for (NSNumber *frame in frames) { + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"opacity", frame)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + } + + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"opacity", @1)]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; + + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; +} + +- (id)createScrollEventWithTag:(NSNumber *)viewTag value:(CGFloat)value +{ + // The event value is the 3rd argument. + NSArray *arguments = @[@1, @1, @{@"contentOffset": @{@"y": @(value)}}]; + return [[RCTFakeEvent alloc] initWithName:@"topScroll" + viewTag:viewTag + arguments:arguments]; +} + +- (void)testNativeAnimatedEventDoUpdate +{ + NSNumber *viewTag = @1000; + [self createSimpleAnimatedView:viewTag withOpacity:0]; + + [_nodesManager addAnimatedEventToView:viewTag + eventName:@"topScroll" + eventMapping:@{@"animatedValueTag": @1, + @"nativeEventPath": @[@"contentOffset", @"y"]}]; + + + + // Make sure that the update actually happened synchronously in `handleAnimatedEvent` and does + // not wait for the next animation loop. + [[_uiManager expect] synchronouslyUpdateViewOnUIThread:viewTag + viewName:@"UIView" + props:RCTPropChecker(@"opacity", @10)]; + [_nodesManager handleAnimatedEvent:[self createScrollEventWithTag:viewTag value:10]]; + [_uiManager verify]; + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager stepAnimations:_displayLink]; + [_uiManager verify]; +} + +- (void)testNativeAnimatedEventDoNotUpdate +{ + NSNumber *viewTag = @1000; + [self createSimpleAnimatedView:viewTag withOpacity:0]; + + [_nodesManager addAnimatedEventToView:viewTag + eventName:@"otherEvent" + eventMapping:@{@"animatedValueTag": @1, + @"nativeEventPath": @[@"contentOffset", @"y"]}]; + + [_nodesManager addAnimatedEventToView:@999 + eventName:@"topScroll" + eventMapping:@{@"animatedValueTag": @1, + @"nativeEventPath": @[@"contentOffset", @"y"]}]; + + [[_uiManager reject] synchronouslyUpdateViewOnUIThread:OCMOCK_ANY viewName:OCMOCK_ANY props:OCMOCK_ANY]; + [_nodesManager handleAnimatedEvent:[self createScrollEventWithTag:viewTag value:10]]; + [_uiManager verify]; +} + +@end diff --git a/Examples/UIExplorer/android/.project b/Examples/UIExplorer/android/.project new file mode 100644 index 00000000000000..d05f956f686e53 --- /dev/null +++ b/Examples/UIExplorer/android/.project @@ -0,0 +1,12 @@ + + + UIExplorer-android + Project UIExplorer-android created by Buildship. + + + + + + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/Examples/UIExplorer/android/.settings/org.eclipse.buildship.core.prefs b/Examples/UIExplorer/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000000000..0d766b1131fe55 --- /dev/null +++ b/Examples/UIExplorer/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,3 @@ +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir=../../.. +eclipse.preferences.version=1 diff --git a/Examples/UIExplorer/android/app/.classpath b/Examples/UIExplorer/android/app/.classpath new file mode 100644 index 00000000000000..c93a0ddb76802f --- /dev/null +++ b/Examples/UIExplorer/android/app/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/Examples/UIExplorer/android/app/.project b/Examples/UIExplorer/android/app/.project new file mode 100644 index 00000000000000..6f97afb0453540 --- /dev/null +++ b/Examples/UIExplorer/android/app/.project @@ -0,0 +1,18 @@ + + + UIExplorer-android-app + Project UIExplorer-android-app created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/Examples/UIExplorer/android/app/.settings/org.eclipse.buildship.core.prefs b/Examples/UIExplorer/android/app/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000000000..5d0546335d60e2 --- /dev/null +++ b/Examples/UIExplorer/android/app/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,3 @@ +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir=../../../.. +eclipse.preferences.version=1 diff --git a/Examples/UIExplorer/android/app/src/main/AndroidManifest.xml b/Examples/UIExplorer/android/app/src/main/AndroidManifest.xml index 308604da5383f0..ddad36d21910bf 100644 --- a/Examples/UIExplorer/android/app/src/main/AndroidManifest.xml +++ b/Examples/UIExplorer/android/app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ + diff --git a/Examples/UIExplorer/js/ActionSheetIOSExample.js b/Examples/UIExplorer/js/ActionSheetIOSExample.js index 43a921e99c0247..fe6084acdb4faa 100644 --- a/Examples/UIExplorer/js/ActionSheetIOSExample.js +++ b/Examples/UIExplorer/js/ActionSheetIOSExample.js @@ -28,8 +28,8 @@ var ReactNative = require('react-native'); var { ActionSheetIOS, StyleSheet, + takeSnapshot, Text, - UIManager, View, } = ReactNative; @@ -164,7 +164,7 @@ class ShareScreenshotExample extends React.Component { showShareActionSheet = () => { // Take the snapshot (returns a temp file uri) - UIManager.takeSnapshot('window').then((uri) => { + takeSnapshot('window').then((uri) => { // Share image data ActionSheetIOS.showShareActionSheetWithOptions({ url: uri, diff --git a/Examples/UIExplorer/js/CameraRollView.js b/Examples/UIExplorer/js/CameraRollView.js index ca5f1dc5e8e26a..e3a9572e290427 100644 --- a/Examples/UIExplorer/js/CameraRollView.js +++ b/Examples/UIExplorer/js/CameraRollView.js @@ -27,9 +27,11 @@ var React = require('react'); var ReactNative = require('react-native'); var { ActivityIndicator, + Alert, CameraRoll, Image, ListView, + PermissionsAndroid, Platform, StyleSheet, View, @@ -139,13 +141,27 @@ var CameraRollView = React.createClass({ } }, - _fetch: function(clear?: boolean) { + _fetch: async function(clear?: boolean) { if (clear) { this.setState(this.getInitialState(), this.fetch); return; } - var fetchParams: Object = { + if (Platform.OS === 'android') { + const result = await PermissionsAndroid.request( + PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE, + { + title: 'Permission Explanation', + message: 'UIExplorer would like to access your pictures.', + }, + ); + if (result !== 'granted') { + Alert.alert('Access to pictures was denied.'); + return; + } + } + + const fetchParams: Object = { first: this.props.batchSize, groupTypes: this.props.groupTypes, assetType: this.props.assetType, @@ -158,8 +174,12 @@ var CameraRollView = React.createClass({ fetchParams.after = this.state.lastCursor; } - CameraRoll.getPhotos(fetchParams) - .then((data) => this._appendAssets(data), (e) => logError(e)); + try { + const data = await CameraRoll.getPhotos(fetchParams); + this._appendAssets(data); + } catch (e) { + logError(e); + } }, /** @@ -180,6 +200,7 @@ var CameraRollView = React.createClass({ onEndReached={this._onEndReached} style={styles.container} dataSource={this.state.dataSource} + enableEmptySections /> ); }, diff --git a/Examples/UIExplorer/js/FlatListExample.js b/Examples/UIExplorer/js/FlatListExample.js index 90263d70aaed44..83e754a5633373 100644 --- a/Examples/UIExplorer/js/FlatListExample.js +++ b/Examples/UIExplorer/js/FlatListExample.js @@ -42,6 +42,7 @@ const { ItemComponent, PlainInput, SeparatorComponent, + Spindicator, genItemData, getItemLayout, pressItem, @@ -61,7 +62,7 @@ class FlatListExample extends React.PureComponent { static description = 'Performant, scrollable list of data.'; state = { - data: genItemData(1000), + data: genItemData(100), debug: false, horizontal: false, filterText: '', @@ -120,22 +121,14 @@ class FlatListExample extends React.PureComponent { {renderSmallSwitchOption(this, 'fixedHeight')} {renderSmallSwitchOption(this, 'logViewable')} {renderSmallSwitchOption(this, 'debug')} - + } + ListFooterComponent={FooterComponent} data={filteredData} debug={this.state.debug} disableVirtualization={!this.state.virtualized} @@ -149,13 +142,13 @@ class FlatListExample extends React.PureComponent { } legacyImplementation={false} numColumns={1} + onEndReached={this._onEndReached} onRefresh={this._onRefresh} onScroll={this.state.horizontal ? this._scrollSinkX : this._scrollSinkY} onViewableItemsChanged={this._onViewableItemsChanged} ref={this._captureRef} refreshing={false} renderItem={this._renderItemComponent} - shouldItemUpdate={this._shouldItemUpdate} viewabilityConfig={VIEWABILITY_CONFIG} /> @@ -165,6 +158,11 @@ class FlatListExample extends React.PureComponent { _getItemLayout = (data: any, index: number) => { return getItemLayout(data, index, this.state.horizontal); }; + _onEndReached = () => { + this.setState((state) => ({ + data: state.data.concat(genItemData(100, state.data.length)), + })); + }; _onRefresh = () => alert('onRefresh: nothing to refresh :P'); _renderItemComponent = ({item}) => { return ( @@ -176,15 +174,6 @@ class FlatListExample extends React.PureComponent { /> ); }; - _shouldItemUpdate(prev, next) { - /** - * Note that this does not check state.horizontal or state.fixedheight - * because we blow away the whole list by changing the key in those cases. - * Make sure that you do the same in your code, or incorporate all relevant - * data into the item data, or skip this optimization entirely. - */ - return prev.item !== next.item; - } // This is called when items change viewability by scrolling into or out of // the viewable area. _onViewableItemsChanged = (info: { @@ -209,7 +198,7 @@ class FlatListExample extends React.PureComponent { this._listRef.getNode().recordInteraction(); pressItem(this, key); }; - _listRef: FlatList<*>; + _listRef: AnimatedFlatList; } @@ -222,12 +211,6 @@ const styles = StyleSheet.create({ searchRow: { paddingHorizontal: 10, }, - spindicator: { - marginLeft: 'auto', - width: 2, - height: 16, - backgroundColor: 'darkgray', - }, }); module.exports = FlatListExample; diff --git a/Examples/UIExplorer/js/ListExampleShared.js b/Examples/UIExplorer/js/ListExampleShared.js index d4a870cdd87055..6eae3fcb8ce6c0 100644 --- a/Examples/UIExplorer/js/ListExampleShared.js +++ b/Examples/UIExplorer/js/ListExampleShared.js @@ -26,6 +26,7 @@ const React = require('react'); const ReactNative = require('react-native'); const { + Animated, Image, Platform, TouchableHighlight, @@ -38,9 +39,9 @@ const { type Item = {title: string, text: string, key: number, pressed: boolean, noImage?: ?boolean}; -function genItemData(count: number): Array { +function genItemData(count: number, start: number = 0): Array { const dataBlob = []; - for (let ii = 0; ii < count; ii++) { + for (let ii = start; ii < count + start; ii++) { const itemHash = Math.abs(hashCode('Item ' + ii)); dataBlob.push({ title: 'Item ' + ii, @@ -129,6 +130,22 @@ class SeparatorComponent extends React.PureComponent { } } +class Spindicator extends React.PureComponent { + render() { + return ( + + ); + } +} + const THUMB_URLS = [ require('./Thumbnails/like.png'), require('./Thumbnails/dislike.png'), @@ -266,6 +283,13 @@ const styles = StyleSheet.create({ width: 64, height: 64, }, + spindicator: { + marginLeft: 'auto', + marginTop: 8, + width: 2, + height: 16, + backgroundColor: 'darkgray', + }, stackedText: { padding: 4, fontSize: 18, @@ -281,6 +305,7 @@ module.exports = { ItemComponent, PlainInput, SeparatorComponent, + Spindicator, genItemData, getItemLayout, pressItem, diff --git a/Examples/UIExplorer/js/ListViewPagingExample.js b/Examples/UIExplorer/js/ListViewPagingExample.js index 58d7359e5b7f77..f140f29948e765 100644 --- a/Examples/UIExplorer/js/ListViewPagingExample.js +++ b/Examples/UIExplorer/js/ListViewPagingExample.js @@ -195,6 +195,7 @@ class ListViewPagingExample extends React.Component { initialListSize={10} pageSize={4} scrollRenderAheadDistance={500} + stickySectionHeadersEnabled /> ); } diff --git a/Examples/UIExplorer/js/MultiColumnExample.js b/Examples/UIExplorer/js/MultiColumnExample.js index 806b6a7313d8ce..3427c8aeba20cc 100644 --- a/Examples/UIExplorer/js/MultiColumnExample.js +++ b/Examples/UIExplorer/js/MultiColumnExample.js @@ -97,9 +97,9 @@ class MultiColumnExample extends React.PureComponent { alert('onRefresh: nothing to refresh :P')} refreshing={false} renderItem={this._renderItemComponent} - shouldItemUpdate={this._shouldItemUpdate} disableVirtualization={!this.state.virtualized} onViewableItemsChanged={this._onViewableItemsChanged} legacyImplementation={false} @@ -127,11 +126,6 @@ class MultiColumnExample extends React.PureComponent { /> ); }; - _shouldItemUpdate(prev, next) { - // Note that this does not check state.fixedHeight because we blow away the whole list by - // changing the key anyway. - return prev.item !== next.item; - } // This is called when items change viewability by scrolling into or out of the viewable area. _onViewableItemsChanged = (info: { changed: Array<{ diff --git a/Examples/UIExplorer/js/NativeAnimationsExample.js b/Examples/UIExplorer/js/NativeAnimationsExample.js index a45986ee9a84e8..d9550530ed85f1 100644 --- a/Examples/UIExplorer/js/NativeAnimationsExample.js +++ b/Examples/UIExplorer/js/NativeAnimationsExample.js @@ -49,7 +49,7 @@ class Tester extends React.Component { this.current && this.props.reverseConfig ? this.props.reverseConfig : this.props.config ); this.current = this.current ? 0 : 1; - const config = { + const config: Object = { ...animConfig, toValue: this.current, }; @@ -352,12 +352,12 @@ exports.examples = [ }, }, { - title: 'Opacity without interpolation', + title: 'Opacity with delay', render: function() { return ( + config={{ duration: 1000, delay: 1000 }}> {anim => ( - {this.props.text} - - ); - } -} - -class BreadcrumbNavSample extends React.Component { - componentWillMount() { - this._navBarRouteMapper = { - rightContentForRoute: function(route, navigator) { - return null; - }, - titleContentForRoute: function(route, navigator) { - return ( - navigator.push(_getRandomRoute())}> - {route.title} - - ); - }, - iconForRoute: function(route, navigator) { - return ( - { navigator.popToRoute(route); }} - style={styles.crumbIconPlaceholder} - /> - ); - }, - separatorForRoute: function(route, navigator) { - return ( - - ); - } - }; - } - - _renderScene = (route, navigator) => { - return ( - - { navigator.push(_getRandomRoute()); }} - text="Push" - /> - { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]); }} - text="Reset w/ 2 scenes" - /> - { navigator.popToTop(); }} - text="Pop to top" - /> - { navigator.replace(_getRandomRoute()); }} - text="Replace" - /> - { this.props.navigator.pop(); }} - text="Close breadcrumb example" - /> - - ); - }; - - render() { - return ( - - } - /> - ); - } -} - -var styles = StyleSheet.create({ - scene: { - paddingTop: 50, - flex: 1, - }, - button: { - backgroundColor: 'white', - padding: 15, - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: '#CDCDCD', - }, - buttonText: { - fontSize: 17, - fontWeight: '500', - }, - container: { - overflow: 'hidden', - backgroundColor: '#dddddd', - flex: 1, - }, - titleText: { - fontSize: 18, - color: '#666666', - textAlign: 'center', - fontWeight: 'bold', - lineHeight: 32, - }, - crumbIconPlaceholder: { - flex: 1, - backgroundColor: '#666666', - }, - crumbSeparatorPlaceholder: { - flex: 1, - backgroundColor: '#aaaaaa', - }, -}); - -module.exports = BreadcrumbNavSample; diff --git a/Examples/UIExplorer/js/Navigator/JumpingNavSample.js b/Examples/UIExplorer/js/Navigator/JumpingNavSample.js deleted file mode 100644 index c4e1a502772cff..00000000000000 --- a/Examples/UIExplorer/js/Navigator/JumpingNavSample.js +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * @providesModule JumpingNavSample - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); - -var nativeImageSource = require('nativeImageSource'); -var { - Navigator, - StyleSheet, - ScrollView, - TabBarIOS, - Text, - TouchableHighlight, - View, -} = ReactNative; - -var _getRandomRoute = function() { - return { - randNumber: Math.ceil(Math.random() * 1000), - }; -}; - -class NavButton extends React.Component { - render() { - return ( - - {this.props.text} - - ); - } -} - -var ROUTE_STACK = [ - _getRandomRoute(), - _getRandomRoute(), - _getRandomRoute(), -]; -var INIT_ROUTE_INDEX = 1; - -class JumpingNavBar extends React.Component { - constructor(props) { - super(props); - this.state = { - tabIndex: props.initTabIndex, - }; - } - handleWillFocus(route) { - var tabIndex = ROUTE_STACK.indexOf(route); - this.setState({ tabIndex, }); - } - render() { - return ( - - - { - this.props.onTabIndex(0); - this.setState({ tabIndex: 0, }); - }}> - - - { - this.props.onTabIndex(1); - this.setState({ tabIndex: 1, }); - }}> - - - { - this.props.onTabIndex(2); - this.setState({ tabIndex: 2, }); - }}> - - - - - ); - } -} - -class JumpingNavSample extends React.Component { - render() { - return ( - { - this._navigator = navigator; - }} - initialRoute={ROUTE_STACK[INIT_ROUTE_INDEX]} - initialRouteStack={ROUTE_STACK} - renderScene={this.renderScene} - configureScene={() => ({ - ...Navigator.SceneConfigs.HorizontalSwipeJump, - })} - navigationBar={ - { this.navBar = navBar; }} - initTabIndex={INIT_ROUTE_INDEX} - routeStack={ROUTE_STACK} - onTabIndex={(index) => { - this._navigator.jumpTo(ROUTE_STACK[index]); - }} - /> - } - /> - ); - } - - renderScene = (route, navigator) => { - var backBtn; - var forwardBtn; - if (ROUTE_STACK.indexOf(route) !== 0) { - backBtn = ( - { - navigator.jumpBack(); - }} - text="jumpBack" - /> - ); - } - if (ROUTE_STACK.indexOf(route) !== ROUTE_STACK.length - 1) { - forwardBtn = ( - { - navigator.jumpForward(); - }} - text="jumpForward" - /> - ); - } - return ( - - #{route.randNumber} - {backBtn} - {forwardBtn} - { - navigator.jumpTo(ROUTE_STACK[1]); - }} - text="jumpTo middle route" - /> - { - this.props.navigator.pop(); - }} - text="Exit Navigation Example" - /> - { - this.props.navigator.push({ - message: 'Came from jumping example', - }); - }} - text="Nav Menu" - /> - - ); - }; -} - -var styles = StyleSheet.create({ - button: { - backgroundColor: 'white', - padding: 15, - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: '#CDCDCD', - }, - buttonText: { - fontSize: 17, - fontWeight: '500', - }, - appContainer: { - overflow: 'hidden', - backgroundColor: '#dddddd', - flex: 1, - }, - messageText: { - fontSize: 17, - fontWeight: '500', - padding: 15, - marginTop: 50, - marginLeft: 15, - }, - scene: { - flex: 1, - paddingTop: 20, - backgroundColor: '#EAEAEA', - }, - tabs: { - height: 50, - } -}); - -module.exports = JumpingNavSample; diff --git a/Examples/UIExplorer/js/Navigator/NavigationBarSample.js b/Examples/UIExplorer/js/Navigator/NavigationBarSample.js deleted file mode 100644 index 5d767c7c793c73..00000000000000 --- a/Examples/UIExplorer/js/Navigator/NavigationBarSample.js +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * @providesModule NavigationBarSample - */ -'use strict'; - - -var React = require('react'); -var ReactNative = require('react-native'); -var { - Navigator, - ScrollView, - StyleSheet, - Text, - TouchableHighlight, - TouchableOpacity, -} = ReactNative; - -class NavButton extends React.Component { - render() { - return ( - - {this.props.text} - - ); - } -} - -var NavigationBarRouteMapper = { - - LeftButton: function(route, navigator, index, navState) { - if (index === 0) { - return null; - } - - var previousRoute = navState.routeStack[index - 1]; - return ( - navigator.pop()} - style={styles.navBarLeftButton}> - - {previousRoute.title} - - - ); - }, - - RightButton: function(route, navigator, index, navState) { - return ( - navigator.push(newRandomRoute())} - style={styles.navBarRightButton}> - - Next - - - ); - }, - - Title: function(route, navigator, index, navState) { - return ( - - {route.title} [{index}] - - ); - }, - -}; - -function newRandomRoute() { - return { - title: '#' + Math.ceil(Math.random() * 1000), - }; -} - -class NavigationBarSample extends React.Component { - componentWillMount() { - var navigator = this.props.navigator; - - var callback = (event) => { - console.log( - `NavigationBarSample : event ${event.type}`, - { - route: JSON.stringify(event.data.route), - target: event.target, - type: event.type, - } - ); - }; - - // Observe focus change events from this component. - this._listeners = [ - navigator.navigationContext.addListener('willfocus', callback), - navigator.navigationContext.addListener('didfocus', callback), - ]; - } - - componentWillUnmount() { - this._listeners && this._listeners.forEach(listener => listener.remove()); - } - - render() { - return ( - ( - - {route.content} - { - navigator.immediatelyResetRouteStack([ - newRandomRoute(), - newRandomRoute(), - newRandomRoute(), - ]); - }} - text="Reset w/ 3 scenes" - /> - { - this.props.navigator.pop(); - }} - text="Exit NavigationBar Example" - /> - - )} - navigationBar={ - - } - /> - ); - } -} - -var styles = StyleSheet.create({ - messageText: { - fontSize: 17, - fontWeight: '500', - padding: 15, - marginTop: 50, - marginLeft: 15, - }, - button: { - backgroundColor: 'white', - padding: 15, - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: '#CDCDCD', - }, - buttonText: { - fontSize: 17, - fontWeight: '500', - }, - navBar: { - backgroundColor: 'white', - }, - navBarText: { - fontSize: 16, - marginVertical: 10, - }, - navBarTitleText: { - color: '#373E4D', - fontWeight: '500', - marginVertical: 9, - }, - navBarLeftButton: { - paddingLeft: 10, - }, - navBarRightButton: { - paddingRight: 10, - }, - navBarButtonText: { - color: '#5890FF', - }, - scene: { - flex: 1, - paddingTop: 20, - backgroundColor: '#EAEAEA', - }, -}); - -module.exports = NavigationBarSample; diff --git a/Examples/UIExplorer/js/Navigator/NavigatorExample.js b/Examples/UIExplorer/js/Navigator/NavigatorExample.js deleted file mode 100644 index 9815ce06661809..00000000000000 --- a/Examples/UIExplorer/js/Navigator/NavigatorExample.js +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule NavigatorExample - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - Navigator, - ScrollView, - StyleSheet, - Text, - TouchableHighlight, -} = ReactNative; -var BreadcrumbNavSample = require('./BreadcrumbNavSample'); -var NavigationBarSample = require('./NavigationBarSample'); -var JumpingNavSample = require('./JumpingNavSample'); - -class NavButton extends React.Component { - render() { - return ( - - {this.props.text} - - ); - } -} - -class NavMenu extends React.Component { - render() { - return ( - - {this.props.message} - { - this.props.navigator.push({ - message: 'Swipe right to dismiss', - sceneConfig: Navigator.SceneConfigs.FloatFromRight, - }); - }} - text="Float in from right" - /> - { - this.props.navigator.push({ - message: 'Swipe down to dismiss', - sceneConfig: Navigator.SceneConfigs.FloatFromBottom, - }); - }} - text="Float in from bottom" - /> - { - this.props.navigator.pop(); - }} - text="Pop" - /> - { - this.props.navigator.popToTop(); - }} - text="Pop to top" - /> - { - this.props.navigator.push({ id: 'navbar' }); - }} - text="Navbar Example" - /> - { - this.props.navigator.push({ id: 'jumping' }); - }} - text="Jumping Example" - /> - { - this.props.navigator.push({ id: 'breadcrumbs' }); - }} - text="Breadcrumbs Example" - /> - { - this.props.onExampleExit(); - }} - text="Exit Example" - /> - - ); - } -} - -class TabBarExample extends React.Component { - static title = ''; - static description = 'JS-implemented navigation'; - - renderScene = (route, nav) => { - switch (route.id) { - case 'navbar': - return ; - case 'breadcrumbs': - return ; - case 'jumping': - return ; - default: - return ( - - ); - } - }; - - render() { - return ( - { - if (route.sceneConfig) { - return route.sceneConfig; - } - return Navigator.SceneConfigs.FloatFromBottom; - }} - /> - ); - } - - componentWillUnmount() { - this._listeners && this._listeners.forEach(listener => listener.remove()); - } - - _setNavigatorRef = (navigator) => { - if (navigator !== this._navigator) { - this._navigator = navigator; - - if (navigator) { - var callback = (event) => { - console.log( - `TabBarExample: event ${event.type}`, - { - route: JSON.stringify(event.data.route), - target: event.target, - type: event.type, - } - ); - }; - // Observe focus change events from the owner. - this._listeners = [ - navigator.navigationContext.addListener('willfocus', callback), - navigator.navigationContext.addListener('didfocus', callback), - ]; - } - } - }; -} - -var styles = StyleSheet.create({ - messageText: { - fontSize: 17, - fontWeight: '500', - padding: 15, - marginTop: 50, - marginLeft: 15, - }, - container: { - flex: 1, - }, - button: { - backgroundColor: 'white', - padding: 15, - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: '#CDCDCD', - }, - buttonText: { - fontSize: 17, - fontWeight: '500', - }, - scene: { - flex: 1, - paddingTop: 20, - backgroundColor: '#EAEAEA', - } -}); - -TabBarExample.external = true; - -module.exports = TabBarExample; diff --git a/Examples/UIExplorer/js/ScrollViewExample.js b/Examples/UIExplorer/js/ScrollViewExample.js index 37aa60f5c35473..1435c03192121a 100644 --- a/Examples/UIExplorer/js/ScrollViewExample.js +++ b/Examples/UIExplorer/js/ScrollViewExample.js @@ -71,7 +71,7 @@ exports.examples = [ description: 'You can display \'s child components horizontally rather than vertically', render: function() { - function renderScrollView(title: string, addtionalStyles: StyleSheet) { + function renderScrollView(title: string, addtionalStyles: typeof StyleSheet) { var _scrollView: ScrollView; return ( diff --git a/Examples/UIExplorer/js/SectionListExample.js b/Examples/UIExplorer/js/SectionListExample.js index 08ed9572d8976e..8b795c5bfc2244 100644 --- a/Examples/UIExplorer/js/SectionListExample.js +++ b/Examples/UIExplorer/js/SectionListExample.js @@ -26,6 +26,7 @@ const React = require('react'); const ReactNative = require('react-native'); const { + Animated, SectionList, StyleSheet, Text, @@ -42,12 +43,15 @@ const { ItemComponent, PlainInput, SeparatorComponent, + Spindicator, genItemData, pressItem, renderSmallSwitchOption, renderStackedItem, } = require('./ListExampleShared'); +const AnimatedSectionList = Animated.createAnimatedComponent(SectionList); + const VIEWABILITY_CONFIG = { minimumViewTime: 3000, viewAreaCoveragePercentThreshold: 100, @@ -55,7 +59,7 @@ const VIEWABILITY_CONFIG = { }; const renderSectionHeader = ({section}) => ( - + SECTION HEADER: {section.key} @@ -75,16 +79,34 @@ class SectionListExample extends React.PureComponent { state = { data: genItemData(1000), + debug: false, filterText: '', logViewable: false, virtualized: true, }; + + _scrollPos = new Animated.Value(0); + _scrollSinkY = Animated.event( + [{nativeEvent: { contentOffset: { y: this._scrollPos } }}], + {useNativeDriver: true}, + ); + render() { const filterRegex = new RegExp(String(this.state.filterText), 'i'); const filter = (item) => ( filterRegex.test(item.text) || filterRegex.test(item.title) ); const filteredData = this.state.data.filter(filter); + const filteredSectionData = []; + let startIndex = 0; + const endIndex = filteredData.length - 1; + for (let ii = 10; ii <= endIndex + 10; ii += 10) { + filteredSectionData.push({ + key: `${filteredData[startIndex].key} - ${filteredData[Math.min(ii - 1, endIndex)].key}`, + data: filteredData.slice(startIndex, ii), + }); + startIndex = ii; + } return ( {renderSmallSwitchOption(this, 'virtualized')} {renderSmallSwitchOption(this, 'logViewable')} + {renderSmallSwitchOption(this, 'debug')} + - @@ -112,12 +136,15 @@ class SectionListExample extends React.PureComponent { ItemSeparatorComponent={() => } + debug={this.state.debug} enableVirtualization={this.state.virtualized} onRefresh={() => alert('onRefresh: nothing to refresh :P')} + onScroll={this._scrollSinkY} onViewableItemsChanged={this._onViewableItemsChanged} refreshing={false} renderItem={this._renderItemComponent} renderSectionHeader={renderSectionHeader} + stickySectionHeadersEnabled sections={[ {renderItem: renderStackedItem, key: 's1', data: [ {title: 'Item In Header Section', text: 'Section s1', key: '0'}, @@ -126,7 +153,7 @@ class SectionListExample extends React.PureComponent { {noImage: true, title: '1st item', text: 'Section s2', key: '0'}, {noImage: true, title: '2nd item', text: 'Section s2', key: '1'}, ]}, - {key: 'Filtered Items', data: filteredData}, + ...filteredSectionData, ]} viewabilityConfig={VIEWABILITY_CONFIG} /> @@ -160,6 +187,9 @@ class SectionListExample extends React.PureComponent { } const styles = StyleSheet.create({ + header: { + backgroundColor: '#e9eaed', + }, headerText: { padding: 4, }, diff --git a/Examples/UIExplorer/js/TextInputExample.ios.js b/Examples/UIExplorer/js/TextInputExample.ios.js index f6499160448063..89f8ccaff2ec2b 100644 --- a/Examples/UIExplorer/js/TextInputExample.ios.js +++ b/Examples/UIExplorer/js/TextInputExample.ios.js @@ -103,34 +103,6 @@ class TextEventsExample extends React.Component { } } -class AutoExpandingTextInput extends React.Component { - state: any; - - constructor(props) { - super(props); - this.state = { - text: 'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about — learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native.', - height: 0, - }; - } - render() { - return ( - { - this.setState({text}); - }} - onContentSizeChange={(event) => { - this.setState({height: event.nativeEvent.contentSize.height}); - }} - style={[styles.default, {height: Math.max(35, this.state.height)}]} - value={this.state.text} - /> - ); - } -} - class RewriteExample extends React.Component { state: any; @@ -403,6 +375,10 @@ var styles = StyleSheet.create({ padding: 4, marginBottom: 4, }, + multilineExpandable: { + height: 'auto', + maxHeight: 100, + }, multilineWithFontStyles: { color: 'blue', fontWeight: 'bold', @@ -801,10 +777,13 @@ exports.examples = [ render: function() { return ( - ); diff --git a/Examples/UIExplorer/js/TouchableExample.js b/Examples/UIExplorer/js/TouchableExample.js index 9ae2f06e248206..ede3f1ccb7e63b 100644 --- a/Examples/UIExplorer/js/TouchableExample.js +++ b/Examples/UIExplorer/js/TouchableExample.js @@ -37,6 +37,11 @@ var { View, } = ReactNative; +const NativeModules = require('NativeModules'); + +const forceTouchAvailable = (NativeModules.PlatformConstants && + NativeModules.PlatformConstants.forceTouchAvailable) || false; + exports.displayName = (undefined: ?string); exports.description = 'Touchable and onPress examples.'; exports.title = ' and onPress'; @@ -263,7 +268,7 @@ class ForceTouchExample extends React.Component { }; _renderConsoleText = () => { - return View.forceTouchAvailable ? + return forceTouchAvailable ? 'Force: ' + this.state.force.toFixed(3) : '3D Touch is not available on this device'; }; diff --git a/Examples/UIExplorer/js/UIExplorerList.ios.js b/Examples/UIExplorer/js/UIExplorerList.ios.js index 9ac8aeeeb3fe2b..5a082acc659b34 100644 --- a/Examples/UIExplorer/js/UIExplorerList.ios.js +++ b/Examples/UIExplorer/js/UIExplorerList.ios.js @@ -90,11 +90,6 @@ const ComponentExamples: Array = [ module: require('./MultiColumnExample'), supportsTVOS: true, }, - { - key: 'NavigatorExample', - module: require('./Navigator/NavigatorExample'), - supportsTVOS: true, - }, { key: 'NavigatorIOSColorsExample', module: require('./NavigatorIOSColorsExample'), diff --git a/IntegrationTests/TimersTest.js b/IntegrationTests/TimersTest.js index 918576c9eee5c9..25056a892f59c1 100644 --- a/IntegrationTests/TimersTest.js +++ b/IntegrationTests/TimersTest.js @@ -82,17 +82,17 @@ var TimersTest = React.createClass({ }, testClearMulti() { - var fails = [this.setTimeout(() => this._fail('testClearMulti-1'), 20)]; + var fails = []; + fails.push(this.setTimeout(() => this._fail('testClearMulti-1'), 20)); fails.push(this.setTimeout(() => this._fail('testClearMulti-2'), 50)); var delayClear = this.setTimeout(() => this._fail('testClearMulti-3'), 50); fails.push(this.setTimeout(() => this._fail('testClearMulti-4'), 0)); - - this.setTimeout(this.testOrdering, 100); // Next test interleaved - fails.push(this.setTimeout(() => this._fail('testClearMulti-5'), 10)); fails.forEach((timeout) => this.clearTimeout(timeout)); this.setTimeout(() => this.clearTimeout(delayClear), 20); + + this.setTimeout(this.testOrdering, 50); }, testOrdering() { @@ -110,14 +110,14 @@ var TimersTest = React.createClass({ () => this._fail('testOrdering-Anim, setTimeout 0 should happen before ' + 'requestAnimationFrame') ); - var fail50; - this.setTimeout(() => this.clearTimeout(fail50), 20); - fail50 = this.setTimeout( - () => this._fail('testOrdering-t50, setTimeout 20 should happen before ' + - 'setTimeout 50'), - 50 + var fail25; + this.setTimeout(() => { this.clearTimeout(fail25); }, 20); + fail25 = this.setTimeout( + () => this._fail('testOrdering-t25, setTimeout 20 should happen before ' + + 'setTimeout 25'), + 25 ); - this.setTimeout(this.done, 75); + this.setTimeout(this.done, 50); }, done() { diff --git a/IntegrationTests/websocket_integration_test_server.js b/IntegrationTests/websocket_integration_test_server.js index b96ee79d660583..175c1db69fae7d 100755 --- a/IntegrationTests/websocket_integration_test_server.js +++ b/IntegrationTests/websocket_integration_test_server.js @@ -1,25 +1,13 @@ #!/usr/bin/env node /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * * @flow * @providesModule websocket_integration_test_server */ diff --git a/Jenkinsfile b/Jenkinsfile index 3563e6bbb3ddf2..3242aef058a843 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -99,14 +99,14 @@ def runStages() { buildInfo.scm.sha = githubInfo.sha buildInfo.scm.tag = githubInfo.tag buildInfo.scm.isPR = githubInfo.isPR - buildInfo.image.tag = "${buildInfo.scm.sha}-${env.BUILD_TAG.replace("/", "-").replace("%2F", "-")}" + buildInfo.image.tag = "${buildInfo.scm.sha}-${env.BUILD_TAG.replace(" ", "-").replace("/", "-").replace("%2F", "-")}" jsTag = "${buildInfo.image.tag}" androidTag = "${buildInfo.image.tag}" jsImageName = "${buildInfo.image.name}-js:${jsTag}" androidImageName = "${buildInfo.image.name}-android:${androidTag}" - parallelInstrumentationTests = getParallelInstrumentationTests('./ReactAndroid/src/androidTest/java/com/facebook/react/tests', 3, androidImageName) + parallelInstrumentationTests = getParallelInstrumentationTests('./ReactAndroid/src/androidTest/java/com/facebook/react/tests', 1, androidImageName) parallel( 'javascript build': { @@ -120,36 +120,53 @@ def runStages() { } stage('Tests JS') { - parallel( - 'javascript flow': { - runCmdOnDockerImage(jsImageName, 'yarn run flow -- check', '--rm') - }, - 'javascript tests': { - runCmdOnDockerImage(jsImageName, 'yarn test --maxWorkers=4', '--rm') - }, - 'documentation tests': { - runCmdOnDockerImage(jsImageName, 'cd website && yarn test', '--rm') - }, - 'documentation generation': { - runCmdOnDockerImage(jsImageName, 'cd website && node ./server/generate.js', '--rm') - } - ) + try { + parallel( + 'javascript flow': { + runCmdOnDockerImage(jsImageName, 'yarn run flow -- check', '--rm') + }, + 'javascript tests': { + runCmdOnDockerImage(jsImageName, 'yarn test --maxWorkers=4', '--rm') + }, + 'documentation tests': { + runCmdOnDockerImage(jsImageName, 'cd website && yarn test', '--rm') + }, + 'documentation generation': { + runCmdOnDockerImage(jsImageName, 'cd website && node ./server/generate.js', '--rm') + } + ) + } catch(e) { + currentBuild.result = "FAILED" + echo "Test JS Stage Error: ${e}" + } } stage('Tests Android') { - parallel( - 'android unit tests': { - runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-android-docker-unit-tests.sh', '--privileged --rm') - }, - 'android e2e tests': { - runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-ci-e2e-tests.sh --android --js', '--rm') - } - ) + try { + parallel( + 'android unit tests': { + runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-android-docker-unit-tests.sh', '--privileged --rm') + }, + 'android e2e tests': { + // temporarily disable e2e tests, they have a high transient failure rate + // runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-ci-e2e-tests.sh --android --js', '--rm') + echo "Android E2E tests have been temporarily disabled" + } + ) + } catch(e) { + currentBuild.result = "FAILED" + echo "Tests Android Stage Error: ${e}" + } } stage('Tests Android Instrumentation') { // run all tests in parallel - parallel(parallelInstrumentationTests) + try { + parallel(parallelInstrumentationTests) + } catch(e) { + currentBuild.result = "FAILED" + echo "Tests Android Instrumentation Stage Error: ${e}" + } } stage('Cleanup') { diff --git a/LICENSE-CustomComponents b/LICENSE-CustomComponents deleted file mode 100644 index 01f2fbc020bb37..00000000000000 --- a/LICENSE-CustomComponents +++ /dev/null @@ -1,9 +0,0 @@ -LICENSE AGREEMENT - -For React Native Custom Components software - -Copyright (c) 2015, Facebook, Inc. All rights reserved. - -Facebook, Inc. (“Facebook”) owns all right, title and interest, including all intellectual property and other proprietary rights, in and to the React Native Custom Components software (the “Software”). Subject to your compliance with these terms, you are hereby granted a non-exclusive, worldwide, royalty-free copyright license to (1) use and copy the Software; and (2) reproduce and distribute the Software as part of your own software (“Your Software”). Facebook reserves all rights not expressly granted to you in this license agreement. - -THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj b/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj index ec14be43777e8f..aefd0f1c38d5f0 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj +++ b/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj @@ -138,7 +138,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; WARNING_CFLAGS = ( - "-Wextra", + "-Werror", "-Wall", ); }; @@ -179,7 +179,7 @@ SKIP_INSTALL = YES; VALIDATE_PRODUCT = YES; WARNING_CFLAGS = ( - "-Wextra", + "-Werror", "-Wall", ); }; diff --git a/Libraries/AdSupport/RCTAdSupport.xcodeproj/project.pbxproj b/Libraries/AdSupport/RCTAdSupport.xcodeproj/project.pbxproj index 81bee2ab570dfb..127186d0ec1dea 100644 --- a/Libraries/AdSupport/RCTAdSupport.xcodeproj/project.pbxproj +++ b/Libraries/AdSupport/RCTAdSupport.xcodeproj/project.pbxproj @@ -135,6 +135,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -182,6 +183,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index e0860eced71cac..51420039ee065e 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -97,6 +97,7 @@ type AnimationConfig = { isInteraction?: bool, useNativeDriver?: bool, onComplete?: ?EndCallback, + iterations?: number, }; // Important note: start() and stop() will only be called at most once. @@ -107,6 +108,7 @@ class Animation { __isInteraction: bool; __nativeId: number; __onEnd: ?EndCallback; + __iterations: number; start( fromValue: number, onUpdate: (value: number) => void, @@ -228,7 +230,7 @@ function _flush(rootNode: AnimatedValue): void { animatedStyles.forEach(animatedStyle => animatedStyle.update()); } -type TimingAnimationConfig = AnimationConfig & { +type TimingAnimationConfig = AnimationConfig & { toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY, easing?: (value: number) => number, duration?: number, @@ -271,6 +273,7 @@ class TimingAnimation extends Animation { this._easing = config.easing !== undefined ? config.easing : easeInOut(); this._duration = config.duration !== undefined ? config.duration : 500; this._delay = config.delay !== undefined ? config.delay : 0; + this.__iterations = config.iterations !== undefined ? config.iterations : 1; this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; this._useNativeDriver = shouldUseNativeDriver(config); } @@ -286,7 +289,7 @@ class TimingAnimation extends Animation { type: 'frames', frames, toValue: this._toValue, - delay: this._delay + iterations: this.__iterations, }; } @@ -386,6 +389,7 @@ class DecayAnimation extends Animation { this._velocity = config.velocity; this._useNativeDriver = shouldUseNativeDriver(config); this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; + this.__iterations = config.iterations !== undefined ? config.iterations : 1; } __getNativeAnimationConfig() { @@ -393,6 +397,7 @@ class DecayAnimation extends Animation { type: 'decay', deceleration: this._deceleration, velocity: this._velocity, + iterations: this.__iterations, }; } @@ -505,6 +510,7 @@ class SpringAnimation extends Animation { this._toValue = config.toValue; this._useNativeDriver = shouldUseNativeDriver(config); this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true; + this.__iterations = config.iterations !== undefined ? config.iterations : 1; var springConfig; if (config.bounciness !== undefined || config.speed !== undefined) { @@ -536,6 +542,7 @@ class SpringAnimation extends Animation { friction: this._friction, initialVelocity: withDefault(this._initialVelocity, this._lastVelocity), toValue: this._toValue, + iterations: this.__iterations, }; } @@ -695,6 +702,7 @@ var _uniqueId = 1; */ class AnimatedValue extends AnimatedWithChildren { _value: number; + _startingValue: number; _offset: number; _animation: ?Animation; _tracking: ?Animated; @@ -703,7 +711,7 @@ class AnimatedValue extends AnimatedWithChildren { constructor(value: number) { super(); - this._value = value; + this._startingValue = this._value = value; this._offset = 0; this._animation = null; this._listeners = {}; @@ -846,6 +854,14 @@ class AnimatedValue extends AnimatedWithChildren { callback && callback(this.__getValue()); } + /** + * Stops any animation and resets the value to its original + */ + resetAnimation(callback?: ?(value: number) => void): void { + this.stopAnimation(callback); + this._value = this._startingValue; + } + /** * Interpolates the value before updating the property, e.g. mapping 0-1 to * 0-10. @@ -1013,6 +1029,12 @@ class AnimatedValueXY extends AnimatedWithChildren { }; } + resetAnimation(callback?: (value: {x: number, y: number}) => void): void { + this.x.resetAnimation(); + this.y.resetAnimation(); + callback && callback(this.__getValue()); + } + stopAnimation(callback?: (value: {x: number, y: number}) => void): void { this.x.stopAnimation(); this.y.stopAnimation(); @@ -1305,6 +1327,7 @@ class AnimatedModulo extends AnimatedWithChildren { __detach(): void { this._a.__removeChild(this); + super.__detach(); } __getNativeConfig(): any { @@ -1355,6 +1378,7 @@ class AnimatedDiffClamp extends AnimatedWithChildren { __detach(): void { this._a.__removeChild(this); + super.__detach(); } __getNativeConfig(): any { @@ -1438,6 +1462,7 @@ class AnimatedTransform extends AnimatedWithChildren { } } }); + super.__detach(); } __getNativeConfig(): any { @@ -1485,32 +1510,48 @@ class AnimatedStyle extends AnimatedWithChildren { this._style = style; } - __getValue(): Object { - var style = {}; - for (var key in this._style) { - var value = this._style[key]; + // Recursively get values for nested styles (like iOS's shadowOffset) + __walkStyleAndGetValues(style) { + const updatedStyle = {}; + for (const key in style) { + const value = style[key]; if (value instanceof Animated) { if (!value.__isNative) { // We cannot use value of natively driven nodes this way as the value we have access from // JS may not be up to date. - style[key] = value.__getValue(); + updatedStyle[key] = value.__getValue(); } + } else if (value && !Array.isArray(value) && typeof value === 'object') { + // Support animating nested values (for example: shadowOffset.height) + updatedStyle[key] = this.__walkStyleAndGetValues(value); } else { - style[key] = value; + updatedStyle[key] = value; } } - return style; + return updatedStyle; } - __getAnimatedValue(): Object { - var style = {}; - for (var key in this._style) { - var value = this._style[key]; + __getValue(): Object { + return this.__walkStyleAndGetValues(this._style); + } + + // Recursively get animated values for nested styles (like iOS's shadowOffset) + __walkStyleAndGetAnimatedValues(style) { + const updatedStyle = {}; + for (const key in style) { + const value = style[key]; if (value instanceof Animated) { - style[key] = value.__getAnimatedValue(); + updatedStyle[key] = value.__getAnimatedValue(); + } else if (value && !Array.isArray(value) && typeof value === 'object') { + // Support animating nested values (for example: shadowOffset.height) + updatedStyle[key] = this.__walkStyleAndGetAnimatedValues(value); } } - return style; + return updatedStyle; + } + + __getAnimatedValue(): Object { + return this.__walkStyleAndGetAnimatedValues(this._style); } __attach(): void { @@ -1529,6 +1570,7 @@ class AnimatedStyle extends AnimatedWithChildren { value.__removeChild(this); } } + super.__detach(); } __makeNative() { @@ -1651,7 +1693,9 @@ class AnimatedProps extends Animated { } setNativeView(animatedView: any): void { - invariant(this._animatedView === undefined, 'Animated view already set.'); + if (this._animatedView === animatedView) { + return; + } this._animatedView = animatedView; if (this.__isNative) { this.__connectAnimatedView(); @@ -1690,7 +1734,9 @@ class AnimatedProps extends Animated { function createAnimatedComponent(Component: any): any { class AnimatedComponent extends React.Component { _component: any; + _prevComponent: any; _propsAnimated: AnimatedProps; + _eventDetachers: Array = []; _setComponentRef: Function; constructor(props: Object) { @@ -1700,7 +1746,7 @@ function createAnimatedComponent(Component: any): any { componentWillUnmount() { this._propsAnimated && this._propsAnimated.__detach(); - this._detachNativeEvents(this.props); + this._detachNativeEvents(); } setNativeProps(props) { @@ -1713,42 +1759,28 @@ function createAnimatedComponent(Component: any): any { componentDidMount() { this._propsAnimated.setNativeView(this._component); - - this._attachNativeEvents(this.props); + this._attachNativeEvents(); } - _attachNativeEvents(newProps) { - if (newProps !== this.props) { - this._detachNativeEvents(this.props); - } - + _attachNativeEvents() { // Make sure to get the scrollable node for components that implement // `ScrollResponder.Mixin`. - const ref = this._component.getScrollableNode ? + const scrollableNode = this._component.getScrollableNode ? this._component.getScrollableNode() : this._component; - for (const key in newProps) { - const prop = newProps[key]; + for (const key in this.props) { + const prop = this.props[key]; if (prop instanceof AnimatedEvent && prop.__isNative) { - prop.__attach(ref, key); + prop.__attach(scrollableNode, key); + this._eventDetachers.push(() => prop.__detach(scrollableNode, key)); } } } - _detachNativeEvents(props) { - // Make sure to get the scrollable node for components that implement - // `ScrollResponder.Mixin`. - const ref = this._component.getScrollableNode ? - this._component.getScrollableNode() : - this._component; - - for (const key in props) { - const prop = props[key]; - if (prop instanceof AnimatedEvent && prop.__isNative) { - prop.__detach(ref, key); - } - } + _detachNativeEvents() { + this._eventDetachers.forEach(remove => remove()); + this._eventDetachers = []; } _attachProps(nextProps) { @@ -1781,10 +1813,6 @@ function createAnimatedComponent(Component: any): any { callback, ); - if (this._component) { - this._propsAnimated.setNativeView(this._component); - } - // When you call detach, it removes the element from the parent list // of children. If it goes to 0, then the parent also detaches itself // and so on. @@ -1796,9 +1824,18 @@ function createAnimatedComponent(Component: any): any { oldPropsAnimated && oldPropsAnimated.__detach(); } - componentWillReceiveProps(nextProps) { - this._attachProps(nextProps); - this._attachNativeEvents(nextProps); + componentWillReceiveProps(newProps) { + this._attachProps(newProps); + } + + componentDidUpdate(prevProps) { + if (this._component !== this._prevComponent) { + this._propsAnimated.setNativeView(this._component); + } + if (this._component !== this._prevComponent || prevProps !== this.props) { + this._detachNativeEvents(); + this._attachNativeEvents(); + } } render() { @@ -1811,6 +1848,7 @@ function createAnimatedComponent(Component: any): any { } _setComponentRef(c) { + this._prevComponent = this._component; this._component = c; } @@ -1820,14 +1858,24 @@ function createAnimatedComponent(Component: any): any { return this._component; } } + + // ReactNative `View.propTypes` have been deprecated in favor of + // `ViewPropTypes`. In their place a temporary getter has been added with a + // deprecated warning message. Avoid triggering that warning here by using + // temporary workaround, __propTypesSecretDontUseThesePlease. + // TODO (bvaughn) Revert this particular change any time after April 1 + var propTypes = + Component.__propTypesSecretDontUseThesePlease || + Component.propTypes; + AnimatedComponent.propTypes = { style: function(props, propName, componentName) { - if (!Component.propTypes) { + if (!propTypes) { return; } for (var key in ViewStylePropTypes) { - if (!Component.propTypes[key] && props[key] !== undefined) { + if (!propTypes[key] && props[key] !== undefined) { console.warn( 'You are setting the style `{ ' + key + ': ... }` as a prop. You ' + 'should nest it in a style object. ' + @@ -1888,6 +1936,9 @@ class AnimatedTracking extends Animated { type CompositeAnimation = { start: (callback?: ?EndCallback) => void, stop: () => void, + reset: () => void, + _startNativeLoop: (iterations?: number) => void, + _isUsingNativeDriver: () => boolean, }; var add = function( @@ -1965,16 +2016,18 @@ var spring = function( value: AnimatedValue | AnimatedValueXY, config: SpringAnimationConfig, ): CompositeAnimation { - return maybeVectorAnim(value, config, spring) || { - start: function(callback?: ?EndCallback): void { - callback = _combineCallbacks(callback, config); - var singleValue: any = value; - var singleConfig: any = config; + var start = function( + animatedValue: AnimatedValue | AnimatedValueXY, + configuration: SpringAnimationConfig, + callback?: ?EndCallback): void { + callback = _combineCallbacks(callback, configuration); + var singleValue: any = animatedValue; + var singleConfig: any = configuration; singleValue.stopTracking(); - if (config.toValue instanceof Animated) { + if (configuration.toValue instanceof Animated) { singleValue.track(new AnimatedTracking( singleValue, - config.toValue, + configuration.toValue, SpringAnimation, singleConfig, callback @@ -1982,11 +2035,28 @@ var spring = function( } else { singleValue.animate(new SpringAnimation(singleConfig), callback); } + }; + return maybeVectorAnim(value, config, spring) || { + start: function(callback?: ?EndCallback): void { + start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, + + reset: function(): void { + value.resetAnimation(); + }, + + _startNativeLoop: function(iterations?: number): void { + var singleConfig = { ...config, iterations }; + start(value, singleConfig); + }, + + _isUsingNativeDriver: function(): boolean { + return config.useNativeDriver || false; + } }; }; @@ -1994,16 +2064,18 @@ var timing = function( value: AnimatedValue | AnimatedValueXY, config: TimingAnimationConfig, ): CompositeAnimation { - return maybeVectorAnim(value, config, timing) || { - start: function(callback?: ?EndCallback): void { - callback = _combineCallbacks(callback, config); - var singleValue: any = value; - var singleConfig: any = config; + var start = function( + animatedValue: AnimatedValue | AnimatedValueXY, + configuration: TimingAnimationConfig, + callback?: ?EndCallback): void { + callback = _combineCallbacks(callback, configuration); + var singleValue: any = animatedValue; + var singleConfig: any = configuration; singleValue.stopTracking(); - if (config.toValue instanceof Animated) { + if (configuration.toValue instanceof Animated) { singleValue.track(new AnimatedTracking( singleValue, - config.toValue, + configuration.toValue, TimingAnimation, singleConfig, callback @@ -2011,11 +2083,29 @@ var timing = function( } else { singleValue.animate(new TimingAnimation(singleConfig), callback); } + }; + + return maybeVectorAnim(value, config, timing) || { + start: function(callback?: ?EndCallback): void { + start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, + + reset: function(): void { + value.resetAnimation(); + }, + + _startNativeLoop: function(iterations?: number): void { + var singleConfig = { ...config, iterations }; + start(value, singleConfig); + }, + + _isUsingNativeDriver: function(): boolean { + return config.useNativeDriver || false; + } }; }; @@ -2023,18 +2113,38 @@ var decay = function( value: AnimatedValue | AnimatedValueXY, config: DecayAnimationConfig, ): CompositeAnimation { - return maybeVectorAnim(value, config, decay) || { - start: function(callback?: ?EndCallback): void { - callback = _combineCallbacks(callback, config); - var singleValue: any = value; - var singleConfig: any = config; + var start = function( + animatedValue: AnimatedValue | AnimatedValueXY, + configuration: DecayAnimationConfig, + callback?: ?EndCallback): void { + callback = _combineCallbacks(callback, configuration); + var singleValue: any = animatedValue; + var singleConfig: any = configuration; singleValue.stopTracking(); singleValue.animate(new DecayAnimation(singleConfig), callback); + }; + + return maybeVectorAnim(value, config, decay) || { + start: function(callback?: ?EndCallback): void { + start(value, config, callback); }, stop: function(): void { value.stopAnimation(); }, + + reset: function(): void { + value.resetAnimation(); + }, + + _startNativeLoop: function(iterations?: number): void { + var singleConfig = { ...config, iterations }; + start(value, singleConfig); + }, + + _isUsingNativeDriver: function(): boolean { + return config.useNativeDriver || false; + } }; }; @@ -2071,6 +2181,23 @@ var sequence = function( if (current < animations.length) { animations[current].stop(); } + }, + + reset: function() { + animations.forEach((animation, idx) => { + if (idx <= current) { + animation.reset(); + } + }); + current = 0; + }, + + _startNativeLoop: function() { + throw new Error('Loops run using the native driver cannot contain Animated.sequence animations'); + }, + + _isUsingNativeDriver: function(): boolean { + return false; } }; }; @@ -2122,6 +2249,22 @@ var parallel = function( !hasEnded[idx] && animation.stop(); hasEnded[idx] = true; }); + }, + + reset: function(): void { + animations.forEach((animation, idx) => { + animation.reset(); + hasEnded[idx] = false; + doneCount = 0; + }); + }, + + _startNativeLoop: function() { + throw new Error('Loops run using the native driver cannot contain Animated.parallel animations'); + }, + + _isUsingNativeDriver: function(): boolean { + return false; } }; @@ -2145,6 +2288,59 @@ var stagger = function( })); }; +type LoopAnimationConfig = { iterations: number }; + +var loop = function( + animation: CompositeAnimation, + { iterations = -1 }: LoopAnimationConfig = {}, +): CompositeAnimation { + var isFinished = false; + var iterationsSoFar = 0; + return { + start: function(callback?: ?EndCallback) { + var restart = function(result: EndResult = {finished: true}): void { + if (isFinished || + (iterationsSoFar === iterations) || + (result.finished === false)) { + callback && callback(result); + } else { + iterationsSoFar++; + animation.reset(); + animation.start(restart); + } + }; + if (!animation || iterations === 0) { + callback && callback({finished: true}); + } else { + if (animation._isUsingNativeDriver()) { + animation._startNativeLoop(iterations); + } else { + restart(); // Start looping recursively on the js thread + } + } + }, + + stop: function(): void { + isFinished = true; + animation.stop(); + }, + + reset: function(): void { + iterationsSoFar = 0; + isFinished = false; + animation.reset(); + }, + + _startNativeLoop: function() { + throw new Error('Loops run using the native driver cannot contain Animated.loop animations'); + }, + + _isUsingNativeDriver: function(): boolean { + return animation._isUsingNativeDriver(); + } + }; +}; + type Mapping = {[key: string]: Mapping} | AnimatedValue; type EventConfig = { listener?: ?Function, @@ -2606,6 +2802,13 @@ module.exports = { * sequence with successive delays. Nice for doing trailing effects. */ stagger, + /** + * Loops a given animation continuously, so that each time it reaches the + * end, it resets and begins again from the start. Can specify number of + * times to loop using the key 'iterations' in the config. Will loop without + * blocking the UI thread if the child animation is set to 'useNativeDriver'. + */ + loop, /** * Takes an array of mappings and extracts values from each arg accordingly, diff --git a/Libraries/Animated/src/__tests__/Animated-test.js b/Libraries/Animated/src/__tests__/Animated-test.js index fc313a8af0582b..c081f1e9b7a67a 100644 --- a/Libraries/Animated/src/__tests__/Animated-test.js +++ b/Libraries/Animated/src/__tests__/Animated-test.js @@ -33,7 +33,11 @@ describe('Animated tests', () => { outputRange: [100, 200], })}, {scale: anim}, - ] + ], + shadowOffset: { + width: anim, + height: anim, + }, } }, callback); @@ -47,6 +51,10 @@ describe('Animated tests', () => { {translateX: 100}, {scale: 0}, ], + shadowOffset: { + width: 0, + height: 0, + }, }, }); @@ -62,6 +70,10 @@ describe('Animated tests', () => { {translateX: 150}, {scale: 0.5}, ], + shadowOffset: { + width: 0.5, + height: 0.5, + }, }, }); @@ -214,6 +226,183 @@ describe('Animated tests', () => { }); }); + describe('Animated Loop', () => { + + it('loops indefinitely if config not specified', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 + expect(animation.reset).toHaveBeenCalledTimes(4); + expect(cb).not.toBeCalled(); + }); + + it('loops indefinitely if iterations is -1', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { iterations: -1 }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 + expect(animation.reset).toHaveBeenCalledTimes(4); + expect(cb).not.toBeCalled(); + }); + + it('loops indefinitely if iterations not specified', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { anotherKey: 'value' }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 + expect(animation.reset).toHaveBeenCalledTimes(4); + expect(cb).not.toBeCalled(); + }); + + it('loops three times if iterations is 3', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { iterations: 3 }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 2 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 3 + expect(animation.reset).toHaveBeenCalledTimes(3); + expect(cb).toBeCalledWith({finished: true}); + }); + + it('does not loop if iterations is 1', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { iterations: 1 }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).toBeCalled(); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(cb).toBeCalledWith({finished: true}); + }); + + it('does not animate if iterations is 0', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation, { iterations: 0 }); + + expect(animation.start).not.toBeCalled(); + + loop.start(cb); + + expect(animation.start).not.toBeCalled(); + expect(cb).toBeCalledWith({ finished: true }); + }); + + it('supports interrupting an indefinite loop', () => { + var animation = {start: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + Animated.loop(animation).start(cb); + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: true}); // End of loop 1 + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).not.toBeCalled(); + + animation.start.mock.calls[0][0]({finished: false}); // Interrupt loop + expect(animation.reset).toHaveBeenCalledTimes(2); + expect(cb).toBeCalledWith({finished: false}); + }); + + it('supports stopping loop', () => { + var animation = {start: jest.fn(), stop: jest.fn(), reset: jest.fn(), _isUsingNativeDriver: () => false}; + var cb = jest.fn(); + + var loop = Animated.loop(animation); + loop.start(cb); + loop.stop(); + + expect(animation.start).toBeCalled(); + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(animation.stop).toBeCalled(); + + animation.start.mock.calls[0][0]({finished: false}); // Interrupt loop + expect(animation.reset).toHaveBeenCalledTimes(1); + expect(cb).toBeCalledWith({finished: false}); + }); + }); + describe('Animated Parallel', () => { it('works with an empty parallel', () => { diff --git a/Libraries/Animated/src/__tests__/AnimatedNative-test.js b/Libraries/Animated/src/__tests__/AnimatedNative-test.js new file mode 100644 index 00000000000000..1d46f647476255 --- /dev/null +++ b/Libraries/Animated/src/__tests__/AnimatedNative-test.js @@ -0,0 +1,678 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +jest + .disableAutomock() + .setMock('Text', {}) + .setMock('View', {}) + .setMock('Image', {}) + .setMock('React', {Component: class {}}) + .setMock('NativeModules', { + NativeAnimatedModule: {}, + }) + .mock('NativeEventEmitter') + // findNodeHandle is imported from ReactNative so mock that whole module. + .setMock('ReactNative', {findNodeHandle: () => 1}); + +const Animated = require('Animated'); +const NativeAnimatedHelper = require('NativeAnimatedHelper'); + +function createAndMountComponent(ComponentClass, props) { + const component = new ComponentClass(); + component.props = props; + component.componentWillMount(); + // Simulate that refs were set. + component._component = {}; + component.componentDidMount(); + return component; +} + +describe('Native Animated', () => { + + const nativeAnimatedModule = require('NativeModules').NativeAnimatedModule; + + beforeEach(() => { + nativeAnimatedModule.addAnimatedEventToView = jest.fn(); + nativeAnimatedModule.connectAnimatedNodes = jest.fn(); + nativeAnimatedModule.connectAnimatedNodeToView = jest.fn(); + nativeAnimatedModule.createAnimatedNode = jest.fn(); + nativeAnimatedModule.disconnectAnimatedNodeFromView = jest.fn(); + nativeAnimatedModule.disconnectAnimatedNodes = jest.fn(); + nativeAnimatedModule.dropAnimatedNode = jest.fn(); + nativeAnimatedModule.extractAnimatedNodeOffset = jest.fn(); + nativeAnimatedModule.flattenAnimatedNodeOffset = jest.fn(); + nativeAnimatedModule.removeAnimatedEventFromView = jest.fn(); + nativeAnimatedModule.setAnimatedNodeOffset = jest.fn(); + nativeAnimatedModule.setAnimatedNodeValue = jest.fn(); + nativeAnimatedModule.startAnimatingNode = jest.fn(); + nativeAnimatedModule.startListeningToAnimatedNodeValue = jest.fn(); + nativeAnimatedModule.stopAnimation = jest.fn(); + nativeAnimatedModule.stopListeningToAnimatedNodeValue = jest.fn(); + + // jest environment doesn't have cancelAnimationFrame :( + if (!global.cancelAnimationFrame) { + global.cancelAnimationFrame = jest.fn(); + } + }); + + describe('Animated Value', () => { + it('proxies `setValue` correctly', () => { + const anim = new Animated.Value(0); + Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); + + const c = createAndMountComponent(Animated.View, { + style: { + opacity: anim, + }, + }); + + // We expect `setValue` not to propagate down to `setNativeProps`, otherwise it may try to access `setNativeProps` + // via component refs table that we override here. + c.refs = { + node: { + setNativeProps: jest.genMockFunction(), + }, + }; + + anim.setValue(0.5); + + expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(jasmine.any(Number), 0.5); + expect(c.refs.node.setNativeProps).not.toHaveBeenCalled(); + }); + + it('should set offset', () => { + const anim = new Animated.Value(0); + anim.setOffset(10); + anim.__makeNative(); + createAndMountComponent(Animated.View, { + style: { + opacity: anim, + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + {type: 'value', value: 0, offset: 10}, + ); + anim.setOffset(20); + expect(nativeAnimatedModule.setAnimatedNodeOffset) + .toBeCalledWith(jasmine.any(Number), 20); + }); + + it('should flatten offset', () => { + const anim = new Animated.Value(0); + anim.__makeNative(); + createAndMountComponent(Animated.View, { + style: { + opacity: anim, + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + {type: 'value', value: 0, offset: 0}, + ); + anim.flattenOffset(); + expect(nativeAnimatedModule.flattenAnimatedNodeOffset) + .toBeCalledWith(jasmine.any(Number)); + }); + + it('should extract offset', () => { + const anim = new Animated.Value(0); + anim.__makeNative(); + createAndMountComponent(Animated.View, { + style: { + opacity: anim, + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + {type: 'value', value: 0, offset: 0}, + ); + anim.extractOffset(); + expect(nativeAnimatedModule.extractAnimatedNodeOffset) + .toBeCalledWith(jasmine.any(Number)); + }); + }); + + describe('Animated Listeners', () => { + it('should get updates', () => { + const value1 = new Animated.Value(0); + value1.__makeNative(); + const listener = jest.fn(); + const id = value1.addListener(listener); + expect(nativeAnimatedModule.startListeningToAnimatedNodeValue) + .toHaveBeenCalledWith(value1.__getNativeTag()); + + NativeAnimatedHelper.nativeEventEmitter.emit( + 'onAnimatedValueUpdate', + {value: 42, tag: value1.__getNativeTag()}, + ); + expect(listener).toHaveBeenCalledTimes(1); + expect(listener).toBeCalledWith({value: 42}); + expect(value1.__getValue()).toBe(42); + + NativeAnimatedHelper.nativeEventEmitter.emit( + 'onAnimatedValueUpdate', + {value: 7, tag: value1.__getNativeTag()}, + ); + expect(listener).toHaveBeenCalledTimes(2); + expect(listener).toBeCalledWith({value: 7}); + expect(value1.__getValue()).toBe(7); + + value1.removeListener(id); + expect(nativeAnimatedModule.stopListeningToAnimatedNodeValue) + .toHaveBeenCalledWith(value1.__getNativeTag()); + + NativeAnimatedHelper.nativeEventEmitter.emit( + 'onAnimatedValueUpdate', + {value: 1492, tag: value1.__getNativeTag()}, + ); + expect(listener).toHaveBeenCalledTimes(2); + expect(value1.__getValue()).toBe(7); + }); + + it('should removeAll', () => { + const value1 = new Animated.Value(0); + value1.__makeNative(); + const listener = jest.fn(); + [1,2,3,4].forEach(() => value1.addListener(listener)); + expect(nativeAnimatedModule.startListeningToAnimatedNodeValue) + .toHaveBeenCalledWith(value1.__getNativeTag()); + + NativeAnimatedHelper.nativeEventEmitter.emit( + 'onAnimatedValueUpdate', + {value: 42, tag: value1.__getNativeTag()}, + ); + expect(listener).toHaveBeenCalledTimes(4); + expect(listener).toBeCalledWith({value: 42}); + + value1.removeAllListeners(); + expect(nativeAnimatedModule.stopListeningToAnimatedNodeValue) + .toHaveBeenCalledWith(value1.__getNativeTag()); + + NativeAnimatedHelper.nativeEventEmitter.emit( + 'onAnimatedValueUpdate', + {value: 7, tag: value1.__getNativeTag()}, + ); + expect(listener).toHaveBeenCalledTimes(4); + }); + }); + + describe('Animated Events', () => { + it('should map events', () => { + const value = new Animated.Value(0); + value.__makeNative(); + const event = Animated.event( + [{nativeEvent: {state: {foo: value}}}], + {useNativeDriver: true}, + ); + const c = createAndMountComponent(Animated.View, {onTouchMove: event}); + expect(nativeAnimatedModule.addAnimatedEventToView).toBeCalledWith( + jasmine.any(Number), + 'onTouchMove', + {nativeEventPath: ['state', 'foo'], animatedValueTag: value.__getNativeTag()}, + ); + + c.componentWillUnmount(); + expect(nativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith( + jasmine.any(Number), + 'onTouchMove', + value.__getNativeTag(), + ); + }); + + it('should throw on invalid event path', () => { + const value = new Animated.Value(0); + value.__makeNative(); + const event = Animated.event( + [{notNativeEvent: {foo: value}}], + {useNativeDriver: true}, + ); + expect(() => createAndMountComponent(Animated.View, {onTouchMove: event})) + .toThrowError(/nativeEvent/); + expect(nativeAnimatedModule.addAnimatedEventToView).not.toBeCalled(); + }); + + it('should call listeners', () => { + const value = new Animated.Value(0); + value.__makeNative(); + const listener = jest.fn(); + const event = Animated.event( + [{nativeEvent: {foo: value}}], + {useNativeDriver: true, listener}, + ); + const handler = event.__getHandler(); + handler({foo: 42}); + expect(listener).toHaveBeenCalledTimes(1); + expect(listener).toBeCalledWith({foo: 42}); + }); + }); + + describe('Animated Graph', () => { + it('creates and detaches nodes', () => { + const anim = new Animated.Value(0); + const c = createAndMountComponent(Animated.View, { + style: { + opacity: anim, + }, + }); + + Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); + + c.componentWillUnmount(); + + expect(nativeAnimatedModule.createAnimatedNode).toHaveBeenCalledTimes(3); + expect(nativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(2); + + expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( + jasmine.any(Number), + jasmine.any(Number), + {type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1}, + jasmine.any(Function) + ); + + expect(nativeAnimatedModule.disconnectAnimatedNodes).toHaveBeenCalledTimes(2); + expect(nativeAnimatedModule.dropAnimatedNode).toHaveBeenCalledTimes(3); + }); + + it('sends a valid description for value, style and props nodes', () => { + const anim = new Animated.Value(0); + createAndMountComponent(Animated.View, { + style: { + opacity: anim, + }, + }); + + Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); + + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(jasmine.any(Number), {type: 'value', value: 0, offset: 0}); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(jasmine.any(Number), {type: 'style', style: {opacity: jasmine.any(Number)}}); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(jasmine.any(Number), {type: 'props', props: {style: jasmine.any(Number)}}); + }); + + it('sends a valid graph description for Animated.add nodes', () => { + const first = new Animated.Value(1); + const second = new Animated.Value(2); + first.__makeNative(); + second.__makeNative(); + + createAndMountComponent(Animated.View, { + style: { + opacity: Animated.add(first, second), + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + {type: 'addition', input: jasmine.any(Array)}, + ); + const additionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( + (call) => call[1].type === 'addition' + ); + expect(additionCalls.length).toBe(1); + const additionCall = additionCalls[0]; + const additionNodeTag = additionCall[0]; + const additionConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( + (call) => call[1] === additionNodeTag + ); + expect(additionConnectionCalls.length).toBe(2); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(additionCall[1].input[0], {type: 'value', value: 1, offset: 0}); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(additionCall[1].input[1], {type: 'value', value: 2, offset: 0}); + }); + + it('sends a valid graph description for Animated.multiply nodes', () => { + const first = new Animated.Value(2); + const second = new Animated.Value(1); + first.__makeNative(); + second.__makeNative(); + + createAndMountComponent(Animated.View, { + style: { + opacity: Animated.multiply(first, second), + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + {type: 'multiplication', input: jasmine.any(Array)}, + ); + const multiplicationCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( + (call) => call[1].type === 'multiplication' + ); + expect(multiplicationCalls.length).toBe(1); + const multiplicationCall = multiplicationCalls[0]; + const multiplicationNodeTag = multiplicationCall[0]; + const multiplicationConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( + (call) => call[1] === multiplicationNodeTag + ); + expect(multiplicationConnectionCalls.length).toBe(2); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(multiplicationCall[1].input[0], {type: 'value', value: 2, offset: 0}); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(multiplicationCall[1].input[1], {type: 'value', value: 1, offset: 0}); + }); + + it('sends a valid graph description for Animated.divide nodes', () => { + const first = new Animated.Value(4); + const second = new Animated.Value(2); + first.__makeNative(); + second.__makeNative(); + + createAndMountComponent(Animated.View, { + style: { + opacity: Animated.divide(first, second), + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(jasmine.any(Number), {type: 'division', input: jasmine.any(Array)}); + const divisionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( + (call) => call[1].type === 'division' + ); + expect(divisionCalls.length).toBe(1); + const divisionCall = divisionCalls[0]; + const divisionNodeTag = divisionCall[0]; + const divisionConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( + (call) => call[1] === divisionNodeTag + ); + expect(divisionConnectionCalls.length).toBe(2); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(divisionCall[1].input[0], {type: 'value', value: 4, offset: 0}); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(divisionCall[1].input[1], {type: 'value', value: 2, offset: 0}); + }); + + it('sends a valid graph description for Animated.modulo nodes', () => { + const value = new Animated.Value(4); + value.__makeNative(); + + createAndMountComponent(Animated.View, { + style: { + opacity: Animated.modulo(value, 4), + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + {type: 'modulus', modulus: 4, input: jasmine.any(Number)}, + ); + const moduloCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( + (call) => call[1].type === 'modulus' + ); + expect(moduloCalls.length).toBe(1); + const moduloCall = moduloCalls[0]; + const moduloNodeTag = moduloCall[0]; + const moduloConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( + (call) => call[1] === moduloNodeTag + ); + expect(moduloConnectionCalls.length).toBe(1); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(moduloCall[1].input, {type: 'value', value: 4, offset: 0}); + }); + + it('sends a valid graph description for interpolate() nodes', () => { + const value = new Animated.Value(10); + value.__makeNative(); + + createAndMountComponent(Animated.View, { + style: { + opacity: value.interpolate({ + inputRange: [10, 20], + outputRange: [0, 1], + }), + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + {type: 'value', value: 10, offset: 0} + ); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(jasmine.any(Number), { + type: 'interpolation', + inputRange: [10, 20], + outputRange: [0, 1], + extrapolateLeft: 'extend', + extrapolateRight: 'extend', + }); + const interpolationNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find( + (call) => call[1].type === 'interpolation' + )[0]; + const valueNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find( + (call) => call[1].type === 'value' + )[0]; + expect(nativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(valueNodeTag, interpolationNodeTag); + }); + + it('sends a valid graph description for transform nodes', () => { + const value = new Animated.Value(0); + value.__makeNative(); + + createAndMountComponent(Animated.View, { + style: { + transform: [{translateX: value}, {scale: 2}], + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + { + type: 'transform', + transforms: [{ + nodeTag: jasmine.any(Number), + property: 'translateX', + type: 'animated', + }, { + value: 2, + property: 'scale', + type: 'static', + }], + }, + ); + }); + + it('sends a valid graph description for Animated.diffClamp nodes', () => { + const value = new Animated.Value(2); + value.__makeNative(); + + createAndMountComponent(Animated.View, { + style: { + opacity: Animated.diffClamp(value, 0, 20), + }, + }); + + expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith( + jasmine.any(Number), + {type: 'diffclamp', input: jasmine.any(Number), max: 20, min: 0}, + ); + const diffClampCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter( + (call) => call[1].type === 'diffclamp' + ); + expect(diffClampCalls.length).toBe(1); + const diffClampCall = diffClampCalls[0]; + const diffClampNodeTag = diffClampCall[0]; + const diffClampConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter( + (call) => call[1] === diffClampNodeTag + ); + expect(diffClampConnectionCalls.length).toBe(1); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(diffClampCall[1].input, {type: 'value', value: 2, offset: 0}); + }); + + it('doesn\'t call into native API if useNativeDriver is set to false', () => { + const anim = new Animated.Value(0); + + const c = createAndMountComponent(Animated.View, { + style: { + opacity: anim, + }, + }); + + Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: false}).start(); + + c.componentWillUnmount(); + + expect(nativeAnimatedModule.createAnimatedNode).not.toBeCalled(); + }); + + it('fails when trying to run non-native animation on native node', () => { + const anim = new Animated.Value(0); + + createAndMountComponent(Animated.View, { + style: { + opacity: anim, + }, + }); + + Animated.timing(anim, {toValue: 10, duration: 50, useNativeDriver: true}).start(); + jest.runAllTimers(); + + Animated.timing(anim, {toValue: 4, duration: 500, useNativeDriver: false}).start(); + expect(jest.runAllTimers).toThrow(); + }); + + it('fails for unsupported styles', () => { + const anim = new Animated.Value(0); + + createAndMountComponent(Animated.View, { + style: { + left: anim, + }, + }); + + const animation = Animated.timing(anim, {toValue: 10, duration: 50, useNativeDriver: true}); + expect(animation.start).toThrowError(/left/); + }); + + it('works for any `static` props and styles', () => { + // Passing "unsupported" props should work just fine as long as they are not animated + const value = new Animated.Value(0); + value.__makeNative(); + + createAndMountComponent(Animated.View, { + style: { + left: 10, + top: 20, + opacity: value, + }, + removeClippedSubviews: true, + }); + + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(jasmine.any(Number), { type: 'style', style: { opacity: jasmine.any(Number) }}); + expect(nativeAnimatedModule.createAnimatedNode) + .toBeCalledWith(jasmine.any(Number), { type: 'props', props: { style: jasmine.any(Number) }}); + }); + }); + + describe('Animations', () => { + it('sends a valid timing animation description', () => { + const anim = new Animated.Value(0); + Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start(); + + expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( + jasmine.any(Number), + jasmine.any(Number), + {type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1}, + jasmine.any(Function) + ); + }); + + it('sends a valid spring animation description', () => { + const anim = new Animated.Value(0); + Animated.spring(anim, {toValue: 10, friction: 5, tension: 164, useNativeDriver: true}).start(); + expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( + jasmine.any(Number), + jasmine.any(Number), + { + type: 'spring', + friction: 16, + initialVelocity: 0, + overshootClamping: false, + restDisplacementThreshold: 0.001, + restSpeedThreshold: 0.001, + tension: 679.08, + toValue: 10, + iterations: 1, + }, + jasmine.any(Function) + ); + + Animated.spring(anim, {toValue: 10, bounciness: 8, speed: 10, useNativeDriver: true}).start(); + expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( + jasmine.any(Number), + jasmine.any(Number), + { + type: 'spring', + friction: 23.05223140901191, + initialVelocity: 0, + overshootClamping: false, + restDisplacementThreshold: 0.001, + restSpeedThreshold: 0.001, + tension: 299.61882352941177, + toValue: 10, + iterations: 1, + }, + jasmine.any(Function) + ); + }); + + it('sends a valid decay animation description', () => { + const anim = new Animated.Value(0); + Animated.decay(anim, {velocity: 10, deceleration: 0.1, useNativeDriver: true}).start(); + + expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( + jasmine.any(Number), + jasmine.any(Number), + {type: 'decay', deceleration: 0.1, velocity: 10, iterations: 1}, + jasmine.any(Function) + ); + }); + + it('works with Animated.loop', () => { + const anim = new Animated.Value(0); + Animated.loop( + Animated.decay(anim, {velocity: 10, deceleration: 0.1, useNativeDriver: true}), + { iterations: 10 }, + ).start(); + + expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( + jasmine.any(Number), + jasmine.any(Number), + {type: 'decay', deceleration: 0.1, velocity: 10, iterations: 10}, + jasmine.any(Function) + ); + }); + + it('sends stopAnimation command to native', () => { + const value = new Animated.Value(0); + const animation = Animated.timing(value, {toValue: 10, duration: 50, useNativeDriver: true}); + + animation.start(); + expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith( + jasmine.any(Number), + jasmine.any(Number), + {type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1}, + jasmine.any(Function) + ); + const animationId = nativeAnimatedModule.startAnimatingNode.mock.calls[0][0]; + + animation.stop(); + expect(nativeAnimatedModule.stopAnimation).toBeCalledWith(animationId); + }); + }); +}); diff --git a/Libraries/BatchedBridge/MessageQueue.js b/Libraries/BatchedBridge/MessageQueue.js index 2c5cc61de1a385..ac4c2a2bc32ada 100644 --- a/Libraries/BatchedBridge/MessageQueue.js +++ b/Libraries/BatchedBridge/MessageQueue.js @@ -41,20 +41,13 @@ const TRACE_TAG_REACT_APPS = 1 << 17; const DEBUG_INFO_LIMIT = 32; -const guard = (fn) => { - try { - fn(); - } catch (error) { - ErrorUtils.reportFatalError(error); - } -}; - class MessageQueue { _callableModules: {[key: string]: Object}; _queue: [Array, Array, Array, number]; _callbacks: []; _callbackID: number; _callID: number; + _inCall: number; _lastFlush: number; _eventLoopStartTime: number; @@ -104,7 +97,7 @@ class MessageQueue { } callFunctionReturnFlushedQueue(module: string, method: string, args: Array) { - guard(() => { + this.__guard(() => { this.__callFunction(module, method, args); this.__callImmediates(); }); @@ -114,7 +107,7 @@ class MessageQueue { callFunctionReturnResultAndFlushedQueue(module: string, method: string, args: Array) { let result; - guard(() => { + this.__guard(() => { result = this.__callFunction(module, method, args); this.__callImmediates(); }); @@ -123,7 +116,7 @@ class MessageQueue { } invokeCallbackAndReturnFlushedQueue(cbID: number, args: Array) { - guard(() => { + this.__guard(() => { this.__invokeCallback(cbID, args); this.__callImmediates(); }); @@ -188,10 +181,12 @@ class MessageQueue { const now = new Date().getTime(); if (global.nativeFlushQueueImmediate && - now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) { - global.nativeFlushQueueImmediate(this._queue); + (now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS || + this._inCall === 0)) { + var queue = this._queue; this._queue = [[], [], [], this._callID]; this._lastFlush = now; + global.nativeFlushQueueImmediate(queue); } Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length); if (__DEV__ && this.__spy && isFinite(moduleID)) { @@ -214,12 +209,23 @@ class MessageQueue { } /** - * "Private" methods + * Private methods */ + __guard(fn: () => void) { + this._inCall++; + try { + fn(); + } catch (error) { + ErrorUtils.reportFatalError(error); + } finally { + this._inCall--; + } + } + __callImmediates() { Systrace.beginEvent('JSTimersExecution.callImmediates()'); - guard(() => JSTimersExecution.callImmediates()); + this.__guard(() => JSTimersExecution.callImmediates()); Systrace.endEvent(); } diff --git a/Libraries/BatchedBridge/NativeModules.js b/Libraries/BatchedBridge/NativeModules.js index 071f04f742bd82..3fa30bc8a94c09 100644 --- a/Libraries/BatchedBridge/NativeModules.js +++ b/Libraries/BatchedBridge/NativeModules.js @@ -13,7 +13,6 @@ const BatchedBridge = require('BatchedBridge'); -const defineLazyObjectProperty = require('defineLazyObjectProperty'); const invariant = require('fbjs/lib/invariant'); type ModuleConfig = [ @@ -125,6 +124,7 @@ if (global.nativeModuleProxy) { const bridgeConfig = global.__fbBatchedBridgeConfig; invariant(bridgeConfig, '__fbBatchedBridgeConfig is not set, cannot invoke native modules'); + const defineLazyObjectProperty = require('defineLazyObjectProperty'); (bridgeConfig.remoteModuleConfig || []).forEach((config: ModuleConfig, moduleID: number) => { // Initially this config will only contain the module name when running in JSC. The actual // configuration of the module will be lazily loaded. diff --git a/Libraries/CameraRoll/CameraRoll.js b/Libraries/CameraRoll/CameraRoll.js index 2a4c23f9721314..5d94eb40d89c37 100644 --- a/Libraries/CameraRoll/CameraRoll.js +++ b/Libraries/CameraRoll/CameraRoll.js @@ -11,7 +11,7 @@ */ 'use strict'; -var ReactPropTypes = require('React').PropTypes +var {PropTypes, checkPropTypes} = require('React'); var RCTCameraRollManager = require('NativeModules').CameraRollManager; var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker'); @@ -47,34 +47,34 @@ var getPhotosParamChecker = createStrictShapeTypeChecker({ * The number of photos wanted in reverse order of the photo application * (i.e. most recent first for SavedPhotos). */ - first: ReactPropTypes.number.isRequired, + first: PropTypes.number.isRequired, /** * A cursor that matches `page_info { end_cursor }` returned from a previous * call to `getPhotos` */ - after: ReactPropTypes.string, + after: PropTypes.string, /** * Specifies which group types to filter the results to. */ - groupTypes: ReactPropTypes.oneOf(GROUP_TYPES_OPTIONS), + groupTypes: PropTypes.oneOf(GROUP_TYPES_OPTIONS), /** * Specifies filter on group names, like 'Recent Photos' or custom album * titles. */ - groupName: ReactPropTypes.string, + groupName: PropTypes.string, /** * Specifies filter on asset type */ - assetType: ReactPropTypes.oneOf(ASSET_TYPE_OPTIONS), + assetType: PropTypes.oneOf(ASSET_TYPE_OPTIONS), /** * Filter by mimetype (e.g. image/jpeg). */ - mimeTypes: ReactPropTypes.arrayOf(ReactPropTypes.string), + mimeTypes: PropTypes.arrayOf(PropTypes.string), }); /** @@ -82,30 +82,30 @@ var getPhotosParamChecker = createStrictShapeTypeChecker({ */ var getPhotosReturnChecker = createStrictShapeTypeChecker({ // $FlowFixMe(>=0.41.0) - edges: ReactPropTypes.arrayOf(createStrictShapeTypeChecker({ + edges: PropTypes.arrayOf(createStrictShapeTypeChecker({ node: createStrictShapeTypeChecker({ - type: ReactPropTypes.string.isRequired, - group_name: ReactPropTypes.string.isRequired, + type: PropTypes.string.isRequired, + group_name: PropTypes.string.isRequired, image: createStrictShapeTypeChecker({ - uri: ReactPropTypes.string.isRequired, - height: ReactPropTypes.number.isRequired, - width: ReactPropTypes.number.isRequired, - isStored: ReactPropTypes.bool, + uri: PropTypes.string.isRequired, + height: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + isStored: PropTypes.bool, }).isRequired, - timestamp: ReactPropTypes.number.isRequired, + timestamp: PropTypes.number.isRequired, location: createStrictShapeTypeChecker({ - latitude: ReactPropTypes.number, - longitude: ReactPropTypes.number, - altitude: ReactPropTypes.number, - heading: ReactPropTypes.number, - speed: ReactPropTypes.number, + latitude: PropTypes.number, + longitude: PropTypes.number, + altitude: PropTypes.number, + heading: PropTypes.number, + speed: PropTypes.number, }), }).isRequired, })).isRequired, page_info: createStrictShapeTypeChecker({ - has_next_page: ReactPropTypes.bool.isRequired, - start_cursor: ReactPropTypes.string, - end_cursor: ReactPropTypes.string, + has_next_page: PropTypes.bool.isRequired, + start_cursor: PropTypes.string, + end_cursor: PropTypes.string, }).isRequired, }); @@ -214,7 +214,7 @@ class CameraRoll { */ static getPhotos(params) { if (__DEV__) { - getPhotosParamChecker({params}, 'params', 'CameraRoll.getPhotos'); + checkPropTypes({params: getPhotosParamChecker}, {params}, 'params', 'CameraRoll.getPhotos'); } if (arguments.length > 1) { console.warn('CameraRoll.getPhotos(tag, success, error) is deprecated. Use the returned Promise instead'); @@ -222,7 +222,8 @@ class CameraRoll { if (__DEV__) { const callback = arguments[1]; successCallback = (response) => { - getPhotosReturnChecker( + checkPropTypes( + {response: getPhotosReturnChecker}, {response}, 'response', 'CameraRoll.getPhotos callback' diff --git a/Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj b/Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj index 97f4860bf8cfc7..8e8acf275d7047 100644 --- a/Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj +++ b/Libraries/CameraRoll/RCTCameraRoll.xcodeproj/project.pbxproj @@ -153,6 +153,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -200,6 +201,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Libraries/Components/ActivityIndicator/ActivityIndicator.js b/Libraries/Components/ActivityIndicator/ActivityIndicator.js index 74630e39ea60e7..8676f897a4583f 100644 --- a/Libraries/Components/ActivityIndicator/ActivityIndicator.js +++ b/Libraries/Components/ActivityIndicator/ActivityIndicator.js @@ -18,6 +18,8 @@ const React = require('React'); const StyleSheet = require('StyleSheet'); const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + const requireNativeComponent = require('requireNativeComponent'); const PropTypes = React.PropTypes; @@ -40,7 +42,7 @@ const ActivityIndicator = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * Whether to show the indicator (true, the default) or hide it (false). */ diff --git a/Libraries/Components/AppleTV/TVEventHandler.android.js b/Libraries/Components/AppleTV/TVEventHandler.android.js index 6d52d0d5eff88b..dc5a1d6b94e931 100644 --- a/Libraries/Components/AppleTV/TVEventHandler.android.js +++ b/Libraries/Components/AppleTV/TVEventHandler.android.js @@ -1,32 +1,11 @@ /** - * Copyright (c) 2016-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule TVEventHandler * @flow */ diff --git a/Libraries/Components/AppleTV/TVEventHandler.ios.js b/Libraries/Components/AppleTV/TVEventHandler.ios.js index bda9737459a4e0..c01f2b065a76fd 100644 --- a/Libraries/Components/AppleTV/TVEventHandler.ios.js +++ b/Libraries/Components/AppleTV/TVEventHandler.ios.js @@ -1,32 +1,11 @@ /** - * Copyright (c) 2016-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule TVEventHandler * @flow */ diff --git a/Libraries/Components/Button.js b/Libraries/Components/Button.js index 8c0fd50429c889..eea0b1ffcd29ae 100644 --- a/Libraries/Components/Button.js +++ b/Libraries/Components/Button.js @@ -124,7 +124,7 @@ class Button extends React.Component { disabled={disabled} onPress={onPress}> - {formattedTitle} + {formattedTitle} ); diff --git a/Libraries/Components/DatePicker/DatePickerIOS.ios.js b/Libraries/Components/DatePicker/DatePickerIOS.ios.js index bb001d7e166d12..1d8dfd5bc4e829 100644 --- a/Libraries/Components/DatePicker/DatePickerIOS.ios.js +++ b/Libraries/Components/DatePicker/DatePickerIOS.ios.js @@ -18,6 +18,8 @@ const React = require('React'); const StyleSheet = require('StyleSheet'); const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + const requireNativeComponent = require('requireNativeComponent'); const PropTypes = React.PropTypes; @@ -43,7 +45,7 @@ const DatePickerIOS = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * The currently selected date. */ diff --git a/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js b/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js index 4898abd11f75c7..6fcca0f53dc7ec 100644 --- a/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js +++ b/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js @@ -20,6 +20,8 @@ var StyleSheet = require('StyleSheet'); var UIManager = require('UIManager'); var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + var DrawerConsts = UIManager.AndroidDrawerLayout.Constants; var dismissKeyboard = require('dismissKeyboard'); @@ -73,7 +75,7 @@ var DrawerLayoutAndroid = React.createClass({ }, propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * Determines whether the keyboard gets dismissed in response to a drag. * - 'none' (the default), drags do not dismiss the keyboard. diff --git a/Libraries/Components/Keyboard/Keyboard.js b/Libraries/Components/Keyboard/Keyboard.js index 8ec70bb68fc1ec..9e936e6d7804f5 100644 --- a/Libraries/Components/Keyboard/Keyboard.js +++ b/Libraries/Components/Keyboard/Keyboard.js @@ -99,6 +99,9 @@ let Keyboard = { * - `keyboardWillChangeFrame` * - `keyboardDidChangeFrame` * + * Note that if you set `android:windowSoftInputMode` to `adjustResize` or `adjustNothing`, + * only `keyboardDidShow` and `keyboardDidHide` events will available on Android. + * * @param {function} callback function to be called when the event fires. */ addListener(eventName: KeyboardEventName, callback: KeyboardEventListener) { diff --git a/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/Libraries/Components/Keyboard/KeyboardAvoidingView.js index f20fb0323da9cd..ad310e50368b16 100644 --- a/Libraries/Components/Keyboard/KeyboardAvoidingView.js +++ b/Libraries/Components/Keyboard/KeyboardAvoidingView.js @@ -18,6 +18,8 @@ const React = require('React'); const TimerMixin = require('react-timer-mixin'); const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + const PropTypes = React.PropTypes; import type EmitterSubscription from 'EmitterSubscription'; @@ -57,13 +59,13 @@ const KeyboardAvoidingView = React.createClass({ mixins: [TimerMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, behavior: PropTypes.oneOf(['height', 'position', 'padding']), /** * The style of the content container(View) when behavior is 'position'. */ - contentContainerStyle: View.propTypes.style, + contentContainerStyle: ViewPropTypes.style, /** * This is the distance between the top of the user screen and the react native view, diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js index 90a6c37e9820c1..3b5697371eb33b 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -13,7 +13,6 @@ var EventEmitter = require('EventEmitter'); var Image = require('Image'); -var NavigationContext = require('NavigationContext'); var RCTNavigatorManager = require('NativeModules').NavigatorManager; var React = require('React'); var ReactNative = require('ReactNative'); @@ -22,6 +21,8 @@ var StyleSheet = require('StyleSheet'); var TVEventHandler = require('TVEventHandler'); var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + var invariant = require('fbjs/lib/invariant'); var logError = require('logError'); var requireNativeComponent = require('requireNativeComponent'); @@ -133,10 +134,10 @@ type Event = Object; * animations and behavior from UIKIt. * * As the name implies, it is only available on iOS. Take a look at - * [`Navigator`](docs/navigator.html) for a similar solution for your - * cross-platform needs, or check out - * [react-native-navigation](https://github.com/wix/react-native-navigation), a - * component that aims to provide native navigation on both iOS and Android. + * [`React Navigation`](https://reactnavigation.org/) for a cross-platform + * solution in JavaScript, or check out either of these components for native + * solutions: [native-navigation](http://airbnb.io/native-navigation/), + * [react-native-navigation](https://github.com/wix/react-native-navigation). * * To set up the navigator, provide the `initialRoute` prop with a route * object. A route object is used to describe each scene that your app @@ -406,7 +407,7 @@ var NavigatorIOS = React.createClass({ /** * Styles for the navigation item containing the component. */ - wrapperStyle: View.propTypes.style, + wrapperStyle: ViewPropTypes.style, /** * Boolean value that indicates whether the navigation bar is hidden. @@ -458,7 +459,7 @@ var NavigatorIOS = React.createClass({ * The default wrapper style for components in the navigator. * A common use case is to set the `backgroundColor` for every scene. */ - itemWrapperStyle: View.propTypes.style, + itemWrapperStyle: ViewPropTypes.style, /** * The default color used for the buttons in the navigation bar. @@ -497,7 +498,6 @@ var NavigatorIOS = React.createClass({ }, navigator: (undefined: ?Object), - navigationContext: new NavigationContext(), componentWillMount: function() { // Precompute a pack of callbacks that's frequently generated and passed to @@ -513,19 +513,14 @@ var NavigatorIOS = React.createClass({ resetTo: this.resetTo, popToRoute: this.popToRoute, popToTop: this.popToTop, - navigationContext: this.navigationContext, }; - this._emitWillFocus(this.state.routeStack[this.state.observedTopOfStack]); }, componentDidMount: function() { - this._emitDidFocus(this.state.routeStack[this.state.observedTopOfStack]); this._enableTVEventHandler(); }, componentWillUnmount: function() { - this.navigationContext.dispose(); - this.navigationContext = new NavigationContext(); this._disableTVEventHandler(); }, @@ -606,7 +601,6 @@ var NavigatorIOS = React.createClass({ _handleNavigatorStackChanged: function(e: Event) { var newObservedTopOfStack = e.nativeEvent.stackLength - 1; - this._emitDidFocus(this.state.routeStack[newObservedTopOfStack]); invariant( newObservedTopOfStack <= this.state.requestedTopOfStack, @@ -659,14 +653,6 @@ var NavigatorIOS = React.createClass({ }); }, - _emitDidFocus: function(route: Route) { - this.navigationContext.emit('didfocus', {route: route}); - }, - - _emitWillFocus: function(route: Route) { - this.navigationContext.emit('willfocus', {route: route}); - }, - /** * Navigate forward to a new route. * @param route The new route to navigate to. @@ -676,7 +662,6 @@ var NavigatorIOS = React.createClass({ // Make sure all previous requests are caught up first. Otherwise reject. if (this.state.requestedTopOfStack === this.state.observedTopOfStack) { this._tryLockNavigator(() => { - this._emitWillFocus(route); var nextStack = this.state.routeStack.concat([route]); var nextIDStack = this.state.idStack.concat([getuid()]); @@ -707,7 +692,6 @@ var NavigatorIOS = React.createClass({ this._tryLockNavigator(() => { var newRequestedTopOfStack = this.state.requestedTopOfStack - n; invariant(newRequestedTopOfStack >= 0, 'Cannot pop below 0'); - this._emitWillFocus(this.state.routeStack[newRequestedTopOfStack]); this.setState({ requestedTopOfStack: newRequestedTopOfStack, makingNavigatorRequest: true, @@ -756,8 +740,6 @@ var NavigatorIOS = React.createClass({ updatingAllIndicesAtOrBeyond: index, }); - this._emitWillFocus(route); - this._emitDidFocus(route); }, /** diff --git a/Libraries/Components/Picker/Picker.js b/Libraries/Components/Picker/Picker.js index 1f8441814f87ab..e5aa6893932062 100644 --- a/Libraries/Components/Picker/Picker.js +++ b/Libraries/Components/Picker/Picker.js @@ -20,7 +20,7 @@ var React = require('React'); var StyleSheetPropType = require('StyleSheetPropType'); var TextStylePropTypes = require('TextStylePropTypes'); var UnimplementedView = require('UnimplementedView'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var ViewStylePropTypes = require('ViewStylePropTypes'); var itemStylePropType = StyleSheetPropType(TextStylePropTypes); @@ -33,6 +33,44 @@ var pickerStyleType = StyleSheetPropType({ var MODE_DIALOG = 'dialog'; var MODE_DROPDOWN = 'dropdown'; +/** + * Individual selectable item in a Picker. + */ +class PickerItem extends React.Component { + props: { + label: string, + value?: any, + color?: ColorPropType, + testID?: string, + }; + + static propTypes = { + /** + * Text to display for this item. + */ + label: React.PropTypes.string.isRequired, + /** + * The value to be passed to picker's `onValueChange` callback when + * this item is selected. Can be a string or an integer. + */ + value: React.PropTypes.any, + /** + * Color of this item's text. + * @platform android + */ + color: ColorPropType, + /** + * Used to locate the item in end-to-end tests. + */ + testID: React.PropTypes.string, + }; + + render() { + // The items are not rendered directly + throw null; + } +} + /** * Renders the native picker component on iOS and Android. Example: * @@ -65,13 +103,15 @@ class Picker extends React.Component { */ static MODE_DROPDOWN = MODE_DROPDOWN; + static Item = PickerItem; + static defaultProps = { mode: MODE_DIALOG, }; // $FlowFixMe(>=0.41.0) static propTypes = { - ...View.propTypes, + ...ViewPropTypes, style: pickerStyleType, /** * Value matching value of one of the items. Can be a string or an integer. @@ -127,43 +167,4 @@ class Picker extends React.Component { } } -/** - * Individual selectable item in a Picker. - */ -// $FlowFixMe found when converting React.createClass to ES6 -Picker.Item = class extends React.Component { - props: { - label: string, - value?: any, - color?: ColorPropType, - testID?: string, - }; - - static propTypes = { - /** - * Text to display for this item. - */ - label: React.PropTypes.string.isRequired, - /** - * The value to be passed to picker's `onValueChange` callback when - * this item is selected. Can be a string or an integer. - */ - value: React.PropTypes.any, - /** - * Color of this item's text. - * @platform android - */ - color: ColorPropType, - /** - * Used to locate the item in end-to-end tests. - */ - testID: React.PropTypes.string, - }; - - render() { - // The items are not rendered directly - throw null; - } -}; - module.exports = Picker; diff --git a/Libraries/Components/Picker/PickerAndroid.android.js b/Libraries/Components/Picker/PickerAndroid.android.js index 4c42c4e011a6c5..511b4d3194625c 100644 --- a/Libraries/Components/Picker/PickerAndroid.android.js +++ b/Libraries/Components/Picker/PickerAndroid.android.js @@ -16,7 +16,7 @@ var ColorPropType = require('ColorPropType'); var React = require('React'); var StyleSheet = require('StyleSheet'); var StyleSheetPropType = require('StyleSheetPropType'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var ViewStylePropTypes = require('ViewStylePropTypes'); var processColor = require('processColor'); @@ -51,7 +51,7 @@ class PickerAndroid extends React.Component { state: *; static propTypes = { - ...View.propTypes, + ...ViewPropTypes, style: pickerStyleType, selectedValue: React.PropTypes.any, enabled: ReactPropTypes.bool, @@ -116,7 +116,8 @@ class PickerAndroid extends React.Component { if (this.props.onValueChange) { var position = event.nativeEvent.position; if (position >= 0) { - var value = this.props.children[position].props.value; + var children = React.Children.toArray(this.props.children); + var value = children[position].props.value; this.props.onValueChange(value, position); } else { this.props.onValueChange(null, position); diff --git a/Libraries/Components/Picker/PickerIOS.ios.js b/Libraries/Components/Picker/PickerIOS.ios.js index 93a429cbe43773..9bdacdddd1d386 100644 --- a/Libraries/Components/Picker/PickerIOS.ios.js +++ b/Libraries/Components/Picker/PickerIOS.ios.js @@ -18,6 +18,7 @@ var StyleSheet = require('StyleSheet'); var StyleSheetPropType = require('StyleSheetPropType'); var TextStylePropTypes = require('TextStylePropTypes'); var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var processColor = require('processColor'); var itemStylePropType = StyleSheetPropType(TextStylePropTypes); @@ -27,7 +28,7 @@ var PickerIOS = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, itemStyle: itemStylePropType, onValueChange: React.PropTypes.func, selectedValue: React.PropTypes.any, // string or integer basically diff --git a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js index 451ddab2209093..a38c25c4697683 100644 --- a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js +++ b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js @@ -12,7 +12,7 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var ColorPropType = require('ColorPropType'); var requireNativeComponent = require('requireNativeComponent'); @@ -66,7 +66,7 @@ var indeterminateType = function(props, propName, componentName) { */ var ProgressBarAndroid = React.createClass({ propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * Style of the ProgressBar. One of: * diff --git a/Libraries/Components/ProgressViewIOS/ProgressViewIOS.ios.js b/Libraries/Components/ProgressViewIOS/ProgressViewIOS.ios.js index 6ee4db298d08e3..a8287f8b456994 100644 --- a/Libraries/Components/ProgressViewIOS/ProgressViewIOS.ios.js +++ b/Libraries/Components/ProgressViewIOS/ProgressViewIOS.ios.js @@ -15,7 +15,7 @@ var Image = require('Image'); var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); var StyleSheet = require('StyleSheet'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var requireNativeComponent = require('requireNativeComponent'); @@ -29,7 +29,7 @@ var ProgressViewIOS = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * The progress bar style. */ diff --git a/Libraries/Components/RefreshControl/RefreshControl.js b/Libraries/Components/RefreshControl/RefreshControl.js index a1eff537d8a2ef..02db53302fd1fe 100644 --- a/Libraries/Components/RefreshControl/RefreshControl.js +++ b/Libraries/Components/RefreshControl/RefreshControl.js @@ -15,7 +15,7 @@ const ColorPropType = require('ColorPropType'); const NativeMethodsMixin = require('NativeMethodsMixin'); const Platform = require('Platform'); const React = require('React'); -const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); const requireNativeComponent = require('requireNativeComponent'); @@ -79,7 +79,7 @@ const RefreshControl = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * Called when the view starts refreshing. */ diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index 955d7ea43be729..76f58fa69471e4 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -12,6 +12,7 @@ 'use strict'; var Dimensions = require('Dimensions'); +var FrameRateLogger = require('FrameRateLogger'); var Platform = require('Platform'); var Keyboard = require('Keyboard'); var ReactNative = require('ReactNative'); @@ -24,6 +25,7 @@ var { getInstanceFromNode } = require('ReactNativeComponentTree'); var { ScrollViewManager } = require('NativeModules'); var invariant = require('fbjs/lib/invariant'); +var performanceNow = require('fbjs/lib/performanceNow'); /** * Mixin that can be integrated in order to handle scrolling that plays well @@ -295,6 +297,7 @@ var ScrollResponderMixin = { * Invoke this from an `onScrollBeginDrag` event. */ scrollResponderHandleScrollBeginDrag: function(e: Event) { + FrameRateLogger.beginScroll(); // TODO: track all scrolls after implementing onScrollEndAnimation this.props.onScrollBeginDrag && this.props.onScrollBeginDrag(e); }, @@ -302,6 +305,16 @@ var ScrollResponderMixin = { * Invoke this from an `onScrollEndDrag` event. */ scrollResponderHandleScrollEndDrag: function(e: Event) { + const {velocity} = e.nativeEvent; + // - If we are animating, then this is a "drag" that is stopping the scrollview and momentum end + // will fire. + // - If velocity is non-zero, then the interaction will stop when momentum scroll ends or + // another drag starts and ends. + // - If we don't get velocity, better to stop the interaction twice than not stop it. + if (!this.scrollResponderIsAnimating() && + (!velocity || velocity.x === 0 && velocity.y === 0)) { + FrameRateLogger.endScroll(); + } this.props.onScrollEndDrag && this.props.onScrollEndDrag(e); }, @@ -309,7 +322,7 @@ var ScrollResponderMixin = { * Invoke this from an `onMomentumScrollBegin` event. */ scrollResponderHandleMomentumScrollBegin: function(e: Event) { - this.state.lastMomentumScrollBeginTime = Date.now(); + this.state.lastMomentumScrollBeginTime = performanceNow(); this.props.onMomentumScrollBegin && this.props.onMomentumScrollBegin(e); }, @@ -317,7 +330,8 @@ var ScrollResponderMixin = { * Invoke this from an `onMomentumScrollEnd` event. */ scrollResponderHandleMomentumScrollEnd: function(e: Event) { - this.state.lastMomentumScrollEndTime = Date.now(); + FrameRateLogger.endScroll(); + this.state.lastMomentumScrollEndTime = performanceNow(); this.props.onMomentumScrollEnd && this.props.onMomentumScrollEnd(e); }, @@ -358,7 +372,7 @@ var ScrollResponderMixin = { * a touch has just started or ended. */ scrollResponderIsAnimating: function(): boolean { - var now = Date.now(); + var now = performanceNow(); var timeSinceLastMomentumScrollEnd = now - this.state.lastMomentumScrollEndTime; var isAnimating = timeSinceLastMomentumScrollEnd < IS_ANIMATING_TOUCH_START_THRESHOLD_MS || this.state.lastMomentumScrollEndTime < this.state.lastMomentumScrollBeginTime; diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 0f167228b70d73..387465ab3a1b93 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -23,6 +23,7 @@ const ScrollViewStickyHeader = require('ScrollViewStickyHeader'); const StyleSheet = require('StyleSheet'); const StyleSheetPropType = require('StyleSheetPropType'); const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); const ViewStylePropTypes = require('ViewStylePropTypes'); const dismissKeyboard = require('dismissKeyboard'); @@ -55,7 +56,7 @@ const requireNativeComponent = require('requireNativeComponent'); * * On the other hand, this has a performance downside. Imagine you have a very * long list of items you want to display, maybe several screens worth of - * content. Creating JS components and native views for everythign all at once, + * content. Creating JS components and native views for everything all at once, * much of which may not even be shown, will contribute to slow rendering and * increased memory usage. * @@ -70,7 +71,7 @@ const requireNativeComponent = require('requireNativeComponent'); // $FlowFixMe(>=0.41.0) const ScrollView = React.createClass({ propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * Controls whether iOS should automatically adjust the content inset * for scroll views that are placed behind a navigation bar or @@ -383,7 +384,7 @@ const ScrollView = React.createClass({ _scrollAnimatedValue: (new Animated.Value(0): Animated.Value), _scrollAnimatedValueAttachment: (null: ?{detach: () => void}), _stickyHeaderRefs: (new Map(): Map), - + _headerLayoutYs: (new Map(): Map), getInitialState: function() { return this.scrollResponderMixinGetInitialState(); }, @@ -391,6 +392,7 @@ const ScrollView = React.createClass({ componentWillMount: function() { this._scrollAnimatedValue = new Animated.Value(0); this._stickyHeaderRefs = new Map(); + this._headerLayoutYs = new Map(); }, componentDidMount: function() { @@ -482,6 +484,11 @@ const ScrollView = React.createClass({ this.scrollTo({x, y, animated: false}); }, + _getKeyForIndex: function(index, childArray) { + const child = childArray[index]; + return child && child.key; + }, + _updateAnimatedNodeAttachment: function() { if (this.props.stickyHeaderIndices && this.props.stickyHeaderIndices.length > 0) { if (!this._scrollAnimatedValueAttachment) { @@ -498,21 +505,34 @@ const ScrollView = React.createClass({ } }, - _setStickyHeaderRef: function(index, ref) { - this._stickyHeaderRefs.set(index, ref); + _setStickyHeaderRef: function(key, ref) { + if (ref) { + this._stickyHeaderRefs.set(key, ref); + } else { + this._stickyHeaderRefs.delete(key); + } }, - _onStickyHeaderLayout: function(index, event) { + _onStickyHeaderLayout: function(index, event, key) { if (!this.props.stickyHeaderIndices) { return; } + const childArray = React.Children.toArray(this.props.children); + if (key !== this._getKeyForIndex(index, childArray)) { + // ignore stale layout update + return; + } - const previousHeaderIndex = this.props.stickyHeaderIndices[ - this.props.stickyHeaderIndices.indexOf(index) - 1 - ]; + const layoutY = event.nativeEvent.layout.y; + this._headerLayoutYs.set(key, layoutY); + + const indexOfIndex = this.props.stickyHeaderIndices.indexOf(index); + const previousHeaderIndex = this.props.stickyHeaderIndices[indexOfIndex - 1]; if (previousHeaderIndex != null) { - const previousHeader = this._stickyHeaderRefs.get(previousHeaderIndex); - previousHeader && previousHeader.setNextHeaderY(event.nativeEvent.layout.y); + const previousHeader = this._stickyHeaderRefs.get( + this._getKeyForIndex(previousHeaderIndex, childArray) + ); + previousHeader && previousHeader.setNextHeaderY(layoutY); } }, @@ -599,34 +619,38 @@ const ScrollView = React.createClass({ }; } - const {stickyHeaderIndices} = this.props; - const hasStickyHeaders = stickyHeaderIndices && stickyHeaderIndices.length > 0; - const children = stickyHeaderIndices && hasStickyHeaders ? - React.Children.toArray(this.props.children).map((child, index) => { - const stickyHeaderIndex = stickyHeaderIndices.indexOf(index); - if (child && stickyHeaderIndex >= 0) { - return ( - this._setStickyHeaderRef(index, ref)} - onLayout={(event) => this._onStickyHeaderLayout(index, event)} - scrollAnimatedValue={this._scrollAnimatedValue}> - {child} - - ); - } else { - return child; - } - }) : - this.props.children; - const contentContainer = + const {stickyHeaderIndices} = this.props; + const hasStickyHeaders = stickyHeaderIndices && stickyHeaderIndices.length > 0; + const childArray = hasStickyHeaders && React.Children.toArray(this.props.children); + const children = hasStickyHeaders ? + childArray.map((child, index) => { + const indexOfIndex = child ? stickyHeaderIndices.indexOf(index) : -1; + if (indexOfIndex > -1) { + const key = child.key; + const nextIndex = stickyHeaderIndices[indexOfIndex + 1]; + return ( + this._setStickyHeaderRef(key, ref)} + nextHeaderLayoutY={ + this._headerLayoutYs.get(this._getKeyForIndex(nextIndex, childArray)) + } + onLayout={(event) => this._onStickyHeaderLayout(index, event, key)} + scrollAnimatedValue={this._scrollAnimatedValue}> + {child} + + ); + } else { + return child; + } + }) : + this.props.children; + const contentContainer = {children} ; diff --git a/Libraries/Components/ScrollView/ScrollViewStickyHeader.js b/Libraries/Components/ScrollView/ScrollViewStickyHeader.js index da640b56090adc..8db0f0b4f43b99 100644 --- a/Libraries/Components/ScrollView/ScrollViewStickyHeader.js +++ b/Libraries/Components/ScrollView/ScrollViewStickyHeader.js @@ -17,19 +17,30 @@ const StyleSheet = require('StyleSheet'); type Props = { children?: React.Element<*>, - scrollAnimatedValue: Animated.Value, + nextHeaderLayoutY: ?number, onLayout: (event: Object) => void, + scrollAnimatedValue: Animated.Value, }; class ScrollViewStickyHeader extends React.Component { props: Props; - state = { - measured: false, - layoutY: 0, - layoutHeight: 0, - nextHeaderLayoutY: (null: ?number), + state: { + measured: boolean, + layoutY: number, + layoutHeight: number, + nextHeaderLayoutY: ?number, }; + constructor(props: Props, context: Object) { + super(props, context); + this.state = { + measured: false, + layoutY: 0, + layoutHeight: 0, + nextHeaderLayoutY: props.nextHeaderLayoutY, + }; + } + setNextHeaderY(y: number) { this.setState({ nextHeaderLayoutY: y }); } @@ -65,8 +76,10 @@ class ScrollViewStickyHeader extends React.Component { // scroll indefinetly. const inputRange = [-1, 0, layoutY]; const outputRange: Array = [0, 0, 0]; - if (nextHeaderLayoutY != null) { - const collisionPoint = nextHeaderLayoutY - layoutHeight; + // Sometimes headers jump around so we make sure we don't violate the monotonic inputRange + // condition. + const collisionPoint = (nextHeaderLayoutY || 0) - layoutHeight; + if (collisionPoint >= layoutY) { inputRange.push(collisionPoint, collisionPoint + 1); outputRange.push(collisionPoint - layoutY, collisionPoint - layoutY); } else { diff --git a/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.ios.js b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.ios.js index 200e198e1c025c..9ac6f3cef5f29d 100644 --- a/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.ios.js +++ b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.ios.js @@ -14,7 +14,7 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); var StyleSheet = require('StyleSheet'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var requireNativeComponent = require('requireNativeComponent'); @@ -54,7 +54,7 @@ var SegmentedControlIOS = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * The labels for the control's segment buttons, in order. */ diff --git a/Libraries/Components/Slider/Slider.js b/Libraries/Components/Slider/Slider.js index 3fbe92fc5f38af..a6e03e8e2cd8c7 100644 --- a/Libraries/Components/Slider/Slider.js +++ b/Libraries/Components/Slider/Slider.js @@ -18,7 +18,7 @@ var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var Platform = require('Platform'); var React = require('React'); var StyleSheet = require('StyleSheet'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var requireNativeComponent = require('requireNativeComponent'); @@ -34,13 +34,13 @@ var Slider = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * Used to style and layout the `Slider`. See `StyleSheet.js` and * `ViewStylePropTypes.js` for more info. */ - style: View.propTypes.style, + style: ViewPropTypes.style, /** * Initial value of the slider. The value should be between minimumValue @@ -126,8 +126,9 @@ var Slider = React.createClass({ onValueChange: PropTypes.func, /** - * Callback called when the user finishes changing the value (e.g. when - * the slider is released). + * Callback that is called when the user releases the slider, + * regardless if the value has changed. The current value is passed + * as an argument to the callback handler. */ onSlidingComplete: PropTypes.func, diff --git a/Libraries/Components/Switch/Switch.js b/Libraries/Components/Switch/Switch.js index f614164cab8577..04ed872dc84d5d 100644 --- a/Libraries/Components/Switch/Switch.js +++ b/Libraries/Components/Switch/Switch.js @@ -16,7 +16,7 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var Platform = require('Platform'); var React = require('React'); var StyleSheet = require('StyleSheet'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var requireNativeComponent = require('requireNativeComponent'); @@ -41,7 +41,7 @@ type DefaultProps = { // $FlowFixMe(>=0.41.0) var Switch = React.createClass({ propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * The value of the switch. If true the switch will be turned on. * Default value is false. diff --git a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js index 5ccab62e11fe7d..3ccabc5828f7e9 100644 --- a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js @@ -15,7 +15,7 @@ var ColorPropType = require('ColorPropType'); var React = require('React'); var StyleSheet = require('StyleSheet'); var TabBarItemIOS = require('TabBarItemIOS'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var requireNativeComponent = require('requireNativeComponent'); @@ -34,8 +34,8 @@ class TabBarIOS extends React.Component { // $FlowFixMe(>=0.41.0) static propTypes = { - ...View.propTypes, - style: View.propTypes.style, + ...ViewPropTypes, + style: ViewPropTypes.style, /** * Color of text on unselected tabs */ diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js index 63f0cd56d15b22..39eb95ab2adcaf 100644 --- a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js @@ -18,11 +18,13 @@ var StaticContainer = require('StaticContainer.react'); var StyleSheet = require('StyleSheet'); var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + var requireNativeComponent = require('requireNativeComponent'); class TabBarItemIOS extends React.Component { static propTypes = { - ...View.propTypes, + ...ViewPropTypes, /** * Little red bubble that sits at the top right of the icon. */ @@ -80,7 +82,7 @@ class TabBarItemIOS extends React.Component { /** * React style object. */ - style: View.propTypes.style, + style: ViewPropTypes.style, /** * Text that appears under the icon. It is ignored when a system icon * is defined. diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index e9b2de7d5474e3..20a060f8d3868e 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -24,7 +24,7 @@ const TextInputState = require('TextInputState'); const TimerMixin = require('react-timer-mixin'); const TouchableWithoutFeedback = require('TouchableWithoutFeedback'); const UIManager = require('UIManager'); -const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); const emptyFunction = require('fbjs/lib/emptyFunction'); const invariant = require('fbjs/lib/invariant'); @@ -176,7 +176,7 @@ const TextInput = React.createClass({ }, propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * Can tell `TextInput` to automatically capitalize certain characters. * @@ -452,7 +452,7 @@ const TextInput = React.createClass({ * Useful for simple use-cases where you do not want to deal with listening * to events and updating the value prop to keep the controlled state in sync. */ - defaultValue: PropTypes.node, + defaultValue: PropTypes.string, /** * When the clear button should appear on the right side of the text view. * @platform ios @@ -540,13 +540,6 @@ const TextInput = React.createClass({ */ mixins: [NativeMethodsMixin, TimerMixin], - viewConfig: - ((Platform.OS === 'ios' && RCTTextField ? - RCTTextField.viewConfig : - (Platform.OS === 'android' && AndroidTextInput ? - AndroidTextInput.viewConfig : - {})) : Object), - /** * Returns `true` if the input is currently focused; `false` otherwise. */ @@ -678,6 +671,7 @@ const TextInput = React.createClass({ if (props.inputView) { children = [children, props.inputView]; } + props.style.unshift(styles.multilineInput); textContainer = {textContainer} @@ -754,6 +749,7 @@ const TextInput = React.createClass({ accessible={this.props.accessible} accessibilityLabel={this.props.accessibilityLabel} accessibilityComponentType={this.props.accessibilityComponentType} + nativeID={this.props.nativeID} testID={this.props.testID}> {textContainer} @@ -867,6 +863,12 @@ var styles = StyleSheet.create({ input: { alignSelf: 'stretch', }, + multilineInput: { + // This default top inset makes RCTTextView seem as close as possible + // to single-line RCTTextField defaults, using the system defaults + // of font size 17 and a height of 31 points. + paddingTop: 5, + }, }); module.exports = TextInput; diff --git a/Libraries/Components/ToolbarAndroid/ToolbarAndroid.android.js b/Libraries/Components/ToolbarAndroid/ToolbarAndroid.android.js index ccb6e9662f473a..201c9c3fc0801c 100644 --- a/Libraries/Components/ToolbarAndroid/ToolbarAndroid.android.js +++ b/Libraries/Components/ToolbarAndroid/ToolbarAndroid.android.js @@ -16,7 +16,7 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var UIManager = require('UIManager'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var ColorPropType = require('ColorPropType'); var requireNativeComponent = require('requireNativeComponent'); @@ -70,7 +70,7 @@ var ToolbarAndroid = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { - ...View.propTypes, + ...ViewPropTypes, /** * Sets possible actions on the toolbar as part of the action menu. These are displayed as icons * or text on the right side of the widget. If they don't fit they are placed in an 'overflow' diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index 333cf6cbf79a9b..ae861f5e115688 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -141,6 +141,7 @@ var TouchableBounce = React.createClass({ accessibilityLabel={this.props.accessibilityLabel} accessibilityComponentType={this.props.accessibilityComponentType} accessibilityTraits={this.props.accessibilityTraits} + nativeID={this.props.nativeID} testID={this.props.testID} hitSlop={this.props.hitSlop} onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder} diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 4651d9416842c2..df821100c71363 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -22,6 +22,7 @@ var TimerMixin = require('react-timer-mixin'); var Touchable = require('Touchable'); var TouchableWithoutFeedback = require('TouchableWithoutFeedback'); var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var ensureComponentIsNative = require('ensureComponentIsNative'); var ensurePositiveDelayProps = require('ensurePositiveDelayProps'); @@ -79,7 +80,7 @@ var TouchableHighlight = React.createClass({ * active. */ underlayColor: ColorPropType, - style: View.propTypes.style, + style: ViewPropTypes.style, /** * Called immediately after the underlay is shown */ @@ -265,6 +266,7 @@ var TouchableHighlight = React.createClass({ onResponderMove={this.touchableHandleResponderMove} onResponderRelease={this.touchableHandleResponderRelease} onResponderTerminate={this.touchableHandleResponderTerminate} + nativeID={this.props.nativeID} testID={this.props.testID}> {React.cloneElement( React.Children.only(this.props.children), diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js index 037a3b85ca8cfd..5707b61e378aaf 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js @@ -255,7 +255,7 @@ var TouchableNativeFeedback = React.createClass({ // We need to clone the actual element so that the ripple background drawable // can be applied directly to the background of this element rather than to - // a wrapper view as done in outher Touchable* + // a wrapper view as done in other Touchable* return React.cloneElement( child, childProps diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 40079f44538f00..0138fea92f52df 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -165,7 +165,7 @@ var TouchableOpacity = React.createClass({ _opacityFocused: function() { this.setOpacityTo(this.props.focusedOpacity); }, - + _getChildStyleOpacityWithDefault: function() { var childStyle = flattenStyle(this.props.style) || {}; return childStyle.opacity == undefined ? 1 : childStyle.opacity; @@ -179,6 +179,7 @@ var TouchableOpacity = React.createClass({ accessibilityComponentType={this.props.accessibilityComponentType} accessibilityTraits={this.props.accessibilityTraits} style={[this.props.style, {opacity: this.state.anim}]} + nativeID={this.props.nativeID} testID={this.props.testID} onLayout={this.props.onLayout} isTVSelectable={true} diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 582832b2a6292b..cd1f855ffee912 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -15,11 +15,15 @@ const EdgeInsetsPropType = require('EdgeInsetsPropType'); const React = require('React'); const TimerMixin = require('react-timer-mixin'); const Touchable = require('Touchable'); -const View = require('View'); const ensurePositiveDelayProps = require('ensurePositiveDelayProps'); const warning = require('fbjs/lib/warning'); +const { + AccessibilityComponentTypes, + AccessibilityTraits, +} = require('ViewAccessibility'); + type Event = Object; const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30}; @@ -37,10 +41,12 @@ const TouchableWithoutFeedback = React.createClass({ propTypes: { accessible: React.PropTypes.bool, - accessibilityComponentType: React.PropTypes.oneOf(View.AccessibilityComponentType), + accessibilityComponentType: React.PropTypes.oneOf( + AccessibilityComponentTypes + ), accessibilityTraits: React.PropTypes.oneOfType([ - React.PropTypes.oneOf(View.AccessibilityTraits), - React.PropTypes.arrayOf(React.PropTypes.oneOf(View.AccessibilityTraits)), + React.PropTypes.oneOf(AccessibilityTraits), + React.PropTypes.arrayOf(React.PropTypes.oneOf(AccessibilityTraits)), ]), /** * If true, disable all interactions for this component. @@ -170,6 +176,8 @@ const TouchableWithoutFeedback = React.createClass({ accessibilityComponentType: this.props.accessibilityComponentType, accessibilityTraits: this.props.accessibilityTraits, // $FlowFixMe(>=0.41.0) + nativeID: this.props.nativeID, + // $FlowFixMe(>=0.41.0) testID: this.props.testID, onLayout: this.props.onLayout, hitSlop: this.props.hitSlop, diff --git a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap index a24b4c1521c0b0..12896cafcaef50 100644 --- a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap +++ b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap @@ -9,6 +9,7 @@ exports[`TouchableHighlight renders correctly 1`] = ` hasTVPreferredFocus={undefined} hitSlop={undefined} isTVSelectable={true} + nativeID={undefined} onLayout={undefined} onResponderGrant={[Function]} onResponderMove={[Function]} @@ -30,6 +31,7 @@ exports[`TouchableHighlight renders correctly 1`] = ` Touchable diff --git a/Libraries/Components/View/ReactNativeViewAttributes.js b/Libraries/Components/View/ReactNativeViewAttributes.js index e5e83a40de9569..3a769ca5ed7525 100644 --- a/Libraries/Components/View/ReactNativeViewAttributes.js +++ b/Libraries/Components/View/ReactNativeViewAttributes.js @@ -23,6 +23,7 @@ ReactNativeViewAttributes.UIView = { accessibilityLiveRegion: true, accessibilityTraits: true, importantForAccessibility: true, + nativeID: true, testID: true, renderToHardwareTextureAndroid: true, shouldRasterizeIOS: true, diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index a519dfbb6b1edc..b054ef8cce42da 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -11,47 +11,28 @@ */ 'use strict'; -const EdgeInsetsPropType = require('EdgeInsetsPropType'); const NativeMethodsMixin = require('NativeMethodsMixin'); const NativeModules = require('NativeModules'); const Platform = require('Platform'); const React = require('React'); +const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes'); const ReactNativeViewAttributes = require('ReactNativeViewAttributes'); -const StyleSheetPropType = require('StyleSheetPropType'); -const ViewStylePropTypes = require('ViewStylePropTypes'); +const ViewPropTypes = require('ViewPropTypes'); const invariant = require('fbjs/lib/invariant'); +const warning = require('fbjs/lib/warning'); const { AccessibilityComponentTypes, AccessibilityTraits, } = require('ViewAccessibility'); -var TVViewPropTypes = {}; -if (Platform.isTVOS) { - TVViewPropTypes = require('TVViewPropTypes'); -} - const requireNativeComponent = require('requireNativeComponent'); -const PropTypes = React.PropTypes; - -const stylePropType = StyleSheetPropType(ViewStylePropTypes); - const forceTouchAvailable = (NativeModules.PlatformConstants && NativeModules.PlatformConstants.forceTouchAvailable) || false; -const statics = { - AccessibilityTraits, - AccessibilityComponentType: AccessibilityComponentTypes, - /** - * Is 3D Touch / Force Touch available (i.e. will touch events include `force`) - * @platform ios - */ - forceTouchAvailable, -}; - /** * The most fundamental component for building a UI, `View` is a container that supports layout with * [flexbox](docs/flexbox.html), [style](docs/style.html), @@ -106,6 +87,21 @@ const View = React.createClass({ // values had to be hardcoded. mixins: [NativeMethodsMixin], + // `propTypes` should not be accessed directly on View since this wrapper only + // exists for DEV mode. However it's important for them to be declared. + // If the object passed to `createClass` specifies `propTypes`, Flow will + // create a static type from it. This property will be over-written below with + // a warn-on-use getter though. + // TODO (bvaughn) Remove the warn-on-use comment after April 1. + propTypes: ViewPropTypes, + + // ReactElementValidator will (temporarily) use this private accessor when + // detected to avoid triggering the warning message. + // TODO (bvaughn) Remove this after April 1 ReactNative RC is tagged. + statics: { + __propTypesSecretDontUseThesePlease: ViewPropTypes + }, + /** * `NativeMethodsMixin` will look for this when invoking `setNativeProps`. We * make `this` look like an actual native component class. @@ -115,390 +111,6 @@ const View = React.createClass({ validAttributes: ReactNativeViewAttributes.RCTView }, - statics: { - ...statics, - }, - - propTypes: { - ...TVViewPropTypes, - - /** - * When `true`, indicates that the view is an accessibility element. By default, - * all the touchable elements are accessible. - */ - accessible: PropTypes.bool, - - /** - * Overrides the text that's read by the screen reader when the user interacts - * with the element. By default, the label is constructed by traversing all the - * children and accumulating all the `Text` nodes separated by space. - */ - accessibilityLabel: PropTypes.node, - - /** - * Indicates to accessibility services to treat UI component like a - * native one. Works for Android only. - * - * Possible values are one of: - * - * - `'none'` - * - `'button'` - * - `'radiobutton_checked'` - * - `'radiobutton_unchecked'` - * - * @platform android - */ - accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentTypes), - - /** - * Indicates to accessibility services whether the user should be notified - * when this view changes. Works for Android API >= 19 only. - * Possible values: - * - * - `'none'` - Accessibility services should not announce changes to this view. - * - `'polite'`- Accessibility services should announce changes to this view. - * - `'assertive'` - Accessibility services should interrupt ongoing speech to immediately announce changes to this view. - * - * See the [Android `View` docs](http://developer.android.com/reference/android/view/View.html#attr_android:accessibilityLiveRegion) - * for reference. - * - * @platform android - */ - accessibilityLiveRegion: PropTypes.oneOf([ - 'none', - 'polite', - 'assertive', - ]), - - /** - * Controls how view is important for accessibility which is if it - * fires accessibility events and if it is reported to accessibility services - * that query the screen. Works for Android only. - * - * Possible values: - * - * - `'auto'` - The system determines whether the view is important for accessibility - - * default (recommended). - * - `'yes'` - The view is important for accessibility. - * - `'no'` - The view is not important for accessibility. - * - `'no-hide-descendants'` - The view is not important for accessibility, - * nor are any of its descendant views. - * - * See the [Android `importantForAccessibility` docs](http://developer.android.com/reference/android/R.attr.html#importantForAccessibility) - * for reference. - * - * @platform android - */ - importantForAccessibility: PropTypes.oneOf([ - 'auto', - 'yes', - 'no', - 'no-hide-descendants', - ]), - - /** - * Provides additional traits to screen reader. By default no traits are - * provided unless specified otherwise in element. - * - * You can provide one trait or an array of many traits. - * - * Possible values for `AccessibilityTraits` are: - * - * - `'none'` - The element has no traits. - * - `'button'` - The element should be treated as a button. - * - `'link'` - The element should be treated as a link. - * - `'header'` - The element is a header that divides content into sections. - * - `'search'` - The element should be treated as a search field. - * - `'image'` - The element should be treated as an image. - * - `'selected'` - The element is selected. - * - `'plays'` - The element plays sound. - * - `'key'` - The element should be treated like a keyboard key. - * - `'text'` - The element should be treated as text. - * - `'summary'` - The element provides app summary information. - * - `'disabled'` - The element is disabled. - * - `'frequentUpdates'` - The element frequently changes its value. - * - `'startsMedia'` - The element starts a media session. - * - `'adjustable'` - The element allows adjustment over a range of values. - * - `'allowsDirectInteraction'` - The element allows direct touch interaction for VoiceOver users. - * - `'pageTurn'` - Informs VoiceOver that it should scroll to the next page when it finishes reading the contents of the element. - * - * See the [Accessibility guide](docs/accessibility.html#accessibilitytraits-ios) - * for more information. - * - * @platform ios - */ - accessibilityTraits: PropTypes.oneOfType([ - PropTypes.oneOf(AccessibilityTraits), - PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)), - ]), - - /** - * A value indicating whether VoiceOver should ignore the elements - * within views that are siblings of the receiver. - * Default is `false`. - * - * See the [Accessibility guide](docs/accessibility.html#accessibilitytraits-ios) - * for more information. - * - * @platform ios - */ - accessibilityViewIsModal: PropTypes.bool, - - /** - * When `accessible` is true, the system will try to invoke this function - * when the user performs accessibility tap gesture. - */ - onAccessibilityTap: PropTypes.func, - - /** - * When `accessible` is `true`, the system will invoke this function when the - * user performs the magic tap gesture. - */ - onMagicTap: PropTypes.func, - - /** - * Used to locate this view in end-to-end tests. - * - * > This disables the 'layout-only view removal' optimization for this view! - */ - testID: PropTypes.string, - - /** - * For most touch interactions, you'll simply want to wrap your component in - * `TouchableHighlight` or `TouchableOpacity`. Check out `Touchable.js`, - * `ScrollResponder.js` and `ResponderEventPlugin.js` for more discussion. - */ - - /** - * The View is now responding for touch events. This is the time to highlight and show the user - * what is happening. - * - * `View.props.onResponderGrant: (event) => {}`, where `event` is a synthetic touch event as - * described above. - */ - onResponderGrant: PropTypes.func, - - /** - * The user is moving their finger. - * - * `View.props.onResponderMove: (event) => {}`, where `event` is a synthetic touch event as - * described above. - */ - onResponderMove: PropTypes.func, - - /** - * Another responder is already active and will not release it to that `View` asking to be - * the responder. - * - * `View.props.onResponderReject: (event) => {}`, where `event` is a synthetic touch event as - * described above. - */ - onResponderReject: PropTypes.func, - - /** - * Fired at the end of the touch. - * - * `View.props.onResponderRelease: (event) => {}`, where `event` is a synthetic touch event as - * described above. - */ - onResponderRelease: PropTypes.func, - - /** - * The responder has been taken from the `View`. Might be taken by other views after a call to - * `onResponderTerminationRequest`, or might be taken by the OS without asking (e.g., happens - * with control center/ notification center on iOS) - * - * `View.props.onResponderTerminate: (event) => {}`, where `event` is a synthetic touch event as - * described above. - */ - onResponderTerminate: PropTypes.func, - - /** - * Some other `View` wants to become responder and is asking this `View` to release its - * responder. Returning `true` allows its release. - * - * `View.props.onResponderTerminationRequest: (event) => {}`, where `event` is a synthetic touch - * event as described above. - */ - onResponderTerminationRequest: PropTypes.func, - - /** - * Does this view want to become responder on the start of a touch? - * - * `View.props.onStartShouldSetResponder: (event) => [true | false]`, where `event` is a - * synthetic touch event as described above. - */ - onStartShouldSetResponder: PropTypes.func, - - /** - * If a parent `View` wants to prevent a child `View` from becoming responder on a touch start, - * it should have this handler which returns `true`. - * - * `View.props.onStartShouldSetResponderCapture: (event) => [true | false]`, where `event` is a - * synthetic touch event as described above. - */ - onStartShouldSetResponderCapture: PropTypes.func, - - /** - * Does this view want to "claim" touch responsiveness? This is called for every touch move on - * the `View` when it is not the responder. - * - * `View.props.onMoveShouldSetResponder: (event) => [true | false]`, where `event` is a - * synthetic touch event as described above. - */ - onMoveShouldSetResponder: PropTypes.func, - - /** - * If a parent `View` wants to prevent a child `View` from becoming responder on a move, - * it should have this handler which returns `true`. - * - * `View.props.onMoveShouldSetResponderCapture: (event) => [true | false]`, where `event` is a - * synthetic touch event as described above. - */ - onMoveShouldSetResponderCapture: PropTypes.func, - - /** - * This defines how far a touch event can start away from the view. - * Typical interface guidelines recommend touch targets that are at least - * 30 - 40 points/density-independent pixels. - * - * For example, if a touchable view has a height of 20 the touchable height can be extended to - * 40 with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}` - * - * > The touch area never extends past the parent view bounds and the Z-index - * > of sibling views always takes precedence if a touch hits two overlapping - * > views. - */ - hitSlop: EdgeInsetsPropType, - - /** - * Invoked on mount and layout changes with: - * - * `{nativeEvent: { layout: {x, y, width, height}}}` - * - * This event is fired immediately once the layout has been calculated, but - * the new layout may not yet be reflected on the screen at the time the - * event is received, especially if a layout animation is in progress. - */ - onLayout: PropTypes.func, - - /** - * Controls whether the `View` can be the target of touch events. - * - * - `'auto'`: The View can be the target of touch events. - * - `'none'`: The View is never the target of touch events. - * - `'box-none'`: The View is never the target of touch events but it's - * subviews can be. It behaves like if the view had the following classes - * in CSS: - * ``` - * .box-none { - * pointer-events: none; - * } - * .box-none * { - * pointer-events: all; - * } - * ``` - * - `'box-only'`: The view can be the target of touch events but it's - * subviews cannot be. It behaves like if the view had the following classes - * in CSS: - * ``` - * .box-only { - * pointer-events: all; - * } - * .box-only * { - * pointer-events: none; - * } - * ``` - * > Since `pointerEvents` does not affect layout/appearance, and we are - * > already deviating from the spec by adding additional modes, we opt to not - * > include `pointerEvents` on `style`. On some platforms, we would need to - * > implement it as a `className` anyways. Using `style` or not is an - * > implementation detail of the platform. - */ - pointerEvents: PropTypes.oneOf([ - 'box-none', - 'none', - 'box-only', - 'auto', - ]), - style: stylePropType, - - /** - * This is a special performance property exposed by `RCTView` and is useful - * for scrolling content when there are many subviews, most of which are - * offscreen. For this property to be effective, it must be applied to a - * view that contains many subviews that extend outside its bound. The - * subviews must also have `overflow: hidden`, as should the containing view - * (or one of its superviews). - */ - removeClippedSubviews: PropTypes.bool, - - /** - * Whether this `View` should render itself (and all of its children) into a - * single hardware texture on the GPU. - * - * On Android, this is useful for animations and interactions that only - * modify opacity, rotation, translation, and/or scale: in those cases, the - * view doesn't have to be redrawn and display lists don't need to be - * re-executed. The texture can just be re-used and re-composited with - * different parameters. The downside is that this can use up limited video - * memory, so this prop should be set back to false at the end of the - * interaction/animation. - * - * @platform android - */ - renderToHardwareTextureAndroid: PropTypes.bool, - - /** - * Whether this `View` should be rendered as a bitmap before compositing. - * - * On iOS, this is useful for animations and interactions that do not - * modify this component's dimensions nor its children; for example, when - * translating the position of a static view, rasterization allows the - * renderer to reuse a cached bitmap of a static view and quickly composite - * it during each frame. - * - * Rasterization incurs an off-screen drawing pass and the bitmap consumes - * memory. Test and measure when using this property. - * - * @platform ios - */ - shouldRasterizeIOS: PropTypes.bool, - - /** - * Views that are only used to layout their children or otherwise don't draw - * anything may be automatically removed from the native hierarchy as an - * optimization. Set this property to `false` to disable this optimization and - * ensure that this `View` exists in the native view hierarchy. - * - * @platform android - */ - collapsable: PropTypes.bool, - - /** - * Whether this `View` needs to rendered offscreen and composited with an alpha - * in order to preserve 100% correct colors and blending behavior. The default - * (`false`) falls back to drawing the component and its children with an alpha - * applied to the paint used to draw each element instead of rendering the full - * component offscreen and compositing it back with an alpha value. This default - * may be noticeable and undesired in the case where the `View` you are setting - * an opacity on has multiple overlapping elements (e.g. multiple overlapping - * `View`s, or text and a background). - * - * Rendering offscreen to preserve correct alpha behavior is extremely - * expensive and hard to debug for non-native developers, which is why it is - * not turned on by default. If you do need to enable this property for an - * animation, consider combining it with renderToHardwareTextureAndroid if the - * view **contents** are static (i.e. it doesn't need to be redrawn each frame). - * If that property is enabled, this View will be rendered off-screen once, - * saved in a hardware texture, and then composited onto the screen with an alpha - * each frame without having to switch rendering targets on the GPU. - * - * @platform android - */ - needsOffscreenAlphaCompositing: PropTypes.bool, - }, - contextTypes: { isInAParentText: React.PropTypes.bool, }, @@ -516,6 +128,70 @@ const View = React.createClass({ }, }); +// Warn about unsupported use of View static properties as these will no longer +// be supported with React fiber. This warning message will go away in the next +// ReactNative release. Use defineProperty() rather than createClass() statics +// because the mixin process auto-triggers the 1-time warning message. +// TODO (bvaughn) Remove this after April 1 ReactNative RC is tagged. +function mixinStatics (target) { + let warnedAboutAccessibilityTraits = false; + let warnedAboutAccessibilityComponentType = false; + let warnedAboutForceTouchAvailable = false; + let warnedAboutPropTypes = false; + + // $FlowFixMe https://github.com/facebook/flow/issues/285 + Object.defineProperty(target, 'AccessibilityTraits', { + get: function() { + warning( + warnedAboutAccessibilityTraits, + 'View.AccessibilityTraits has been deprecated and will be ' + + 'removed in a future version of ReactNative. Use ' + + 'ViewAccessibility.AccessibilityTraits instead.' + ); + warnedAboutAccessibilityTraits = true; + return AccessibilityTraits; + } + }); + // $FlowFixMe https://github.com/facebook/flow/issues/285 + Object.defineProperty(target, 'AccessibilityComponentType', { + get: function() { + warning( + warnedAboutAccessibilityComponentType, + 'View.AccessibilityComponentType has been deprecated and will be ' + + 'removed in a future version of ReactNative. Use ' + + 'ViewAccessibility.AccessibilityComponentTypes instead.' + ); + warnedAboutAccessibilityComponentType = true; + return AccessibilityComponentTypes; + } + }); + // $FlowFixMe https://github.com/facebook/flow/issues/285 + Object.defineProperty(target, 'forceTouchAvailable', { + get: function() { + warning( + warnedAboutForceTouchAvailable, + 'View.forceTouchAvailable has been deprecated and will be removed ' + + 'in a future version of ReactNative. Use ' + + 'NativeModules.PlatformConstants.forceTouchAvailable instead.' + ); + warnedAboutForceTouchAvailable = true; + return forceTouchAvailable; + } + }); + // $FlowFixMe https://github.com/facebook/flow/issues/285 + Object.defineProperty(target, 'propTypes', { + get: function() { + warning( + warnedAboutPropTypes, + 'View.propTypes has been deprecated and will be removed in a future ' + + 'version of ReactNative. Use ViewPropTypes instead.' + ); + warnedAboutPropTypes = true; + return ViewPropTypes; + } + }); +} + const RCTView = requireNativeComponent('RCTView', View, { nativeOnly: { nativeBackgroundAndroid: true, @@ -536,11 +212,21 @@ if (__DEV__) { } } +// TODO (bvaughn) Remove feature flags once all static View accessors are gone. +// We temporarily wrap fiber native views with the create-class View above, +// Because external code sometimes accesses static properties of this view. let ViewToExport = RCTView; -if (__DEV__) { +if ( + __DEV__ || + ReactNativeFeatureFlags.useFiber +) { + mixinStatics(View); ViewToExport = View; } else { - Object.assign(RCTView, statics); + // TODO (bvaughn) Remove this mixin once all static View accessors are gone. + mixinStatics((RCTView : any)); } -module.exports = ViewToExport; +// TODO (bvaughn) Temporarily mask Flow warnings for View property accesses. +// We're wrapping the string type (Fiber) for now to avoid any actual problems. +module.exports = ((ViewToExport : any) : typeof View); diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js new file mode 100644 index 00000000000000..a0f8b8ff5f7208 --- /dev/null +++ b/Libraries/Components/View/ViewPropTypes.js @@ -0,0 +1,419 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ViewPropTypes + * @flow + */ +'use strict'; + +const EdgeInsetsPropType = require('EdgeInsetsPropType'); +const Platform = require('Platform'); +const StyleSheetPropType = require('StyleSheetPropType'); +const ViewStylePropTypes = require('ViewStylePropTypes'); + +const { PropTypes } = require('React'); +const { + AccessibilityComponentTypes, + AccessibilityTraits, +} = require('ViewAccessibility'); + +var TVViewPropTypes = {}; +if (Platform.isTVOS) { + TVViewPropTypes = require('TVViewPropTypes'); +} + +const stylePropType = StyleSheetPropType(ViewStylePropTypes); + +module.exports = { + ...TVViewPropTypes, + + /** + * When `true`, indicates that the view is an accessibility element. By default, + * all the touchable elements are accessible. + */ + accessible: PropTypes.bool, + + /** + * Overrides the text that's read by the screen reader when the user interacts + * with the element. By default, the label is constructed by traversing all the + * children and accumulating all the `Text` nodes separated by space. + */ + accessibilityLabel: PropTypes.node, + + /** + * Indicates to accessibility services to treat UI component like a + * native one. Works for Android only. + * + * Possible values are one of: + * + * - `'none'` + * - `'button'` + * - `'radiobutton_checked'` + * - `'radiobutton_unchecked'` + * + * @platform android + */ + accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentTypes), + + /** + * Indicates to accessibility services whether the user should be notified + * when this view changes. Works for Android API >= 19 only. + * Possible values: + * + * - `'none'` - Accessibility services should not announce changes to this view. + * - `'polite'`- Accessibility services should announce changes to this view. + * - `'assertive'` - Accessibility services should interrupt ongoing speech to immediately announce changes to this view. + * + * See the [Android `View` docs](http://developer.android.com/reference/android/view/View.html#attr_android:accessibilityLiveRegion) + * for reference. + * + * @platform android + */ + accessibilityLiveRegion: PropTypes.oneOf([ + 'none', + 'polite', + 'assertive', + ]), + + /** + * Controls how view is important for accessibility which is if it + * fires accessibility events and if it is reported to accessibility services + * that query the screen. Works for Android only. + * + * Possible values: + * + * - `'auto'` - The system determines whether the view is important for accessibility - + * default (recommended). + * - `'yes'` - The view is important for accessibility. + * - `'no'` - The view is not important for accessibility. + * - `'no-hide-descendants'` - The view is not important for accessibility, + * nor are any of its descendant views. + * + * See the [Android `importantForAccessibility` docs](http://developer.android.com/reference/android/R.attr.html#importantForAccessibility) + * for reference. + * + * @platform android + */ + importantForAccessibility: PropTypes.oneOf([ + 'auto', + 'yes', + 'no', + 'no-hide-descendants', + ]), + + /** + * Provides additional traits to screen reader. By default no traits are + * provided unless specified otherwise in element. + * + * You can provide one trait or an array of many traits. + * + * Possible values for `AccessibilityTraits` are: + * + * - `'none'` - The element has no traits. + * - `'button'` - The element should be treated as a button. + * - `'link'` - The element should be treated as a link. + * - `'header'` - The element is a header that divides content into sections. + * - `'search'` - The element should be treated as a search field. + * - `'image'` - The element should be treated as an image. + * - `'selected'` - The element is selected. + * - `'plays'` - The element plays sound. + * - `'key'` - The element should be treated like a keyboard key. + * - `'text'` - The element should be treated as text. + * - `'summary'` - The element provides app summary information. + * - `'disabled'` - The element is disabled. + * - `'frequentUpdates'` - The element frequently changes its value. + * - `'startsMedia'` - The element starts a media session. + * - `'adjustable'` - The element allows adjustment over a range of values. + * - `'allowsDirectInteraction'` - The element allows direct touch interaction for VoiceOver users. + * - `'pageTurn'` - Informs VoiceOver that it should scroll to the next page when it finishes reading the contents of the element. + * + * See the [Accessibility guide](docs/accessibility.html#accessibilitytraits-ios) + * for more information. + * + * @platform ios + */ + accessibilityTraits: PropTypes.oneOfType([ + PropTypes.oneOf(AccessibilityTraits), + PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)), + ]), + + /** + * A value indicating whether VoiceOver should ignore the elements + * within views that are siblings of the receiver. + * Default is `false`. + * + * See the [Accessibility guide](docs/accessibility.html#accessibilitytraits-ios) + * for more information. + * + * @platform ios + */ + accessibilityViewIsModal: PropTypes.bool, + + /** + * When `accessible` is true, the system will try to invoke this function + * when the user performs accessibility tap gesture. + */ + onAccessibilityTap: PropTypes.func, + + /** + * When `accessible` is `true`, the system will invoke this function when the + * user performs the magic tap gesture. + */ + onMagicTap: PropTypes.func, + + /** + * Used to locate this view in end-to-end tests. + * + * > This disables the 'layout-only view removal' optimization for this view! + */ + testID: PropTypes.string, + + /** + * Used to locate this view from native classes. + * + * > This disables the 'layout-only view removal' optimization for this view! + * + * @platform android + */ + nativeID: PropTypes.string, + + /** + * For most touch interactions, you'll simply want to wrap your component in + * `TouchableHighlight` or `TouchableOpacity`. Check out `Touchable.js`, + * `ScrollResponder.js` and `ResponderEventPlugin.js` for more discussion. + */ + + /** + * The View is now responding for touch events. This is the time to highlight and show the user + * what is happening. + * + * `View.props.onResponderGrant: (event) => {}`, where `event` is a synthetic touch event as + * described above. + */ + onResponderGrant: PropTypes.func, + + /** + * The user is moving their finger. + * + * `View.props.onResponderMove: (event) => {}`, where `event` is a synthetic touch event as + * described above. + */ + onResponderMove: PropTypes.func, + + /** + * Another responder is already active and will not release it to that `View` asking to be + * the responder. + * + * `View.props.onResponderReject: (event) => {}`, where `event` is a synthetic touch event as + * described above. + */ + onResponderReject: PropTypes.func, + + /** + * Fired at the end of the touch. + * + * `View.props.onResponderRelease: (event) => {}`, where `event` is a synthetic touch event as + * described above. + */ + onResponderRelease: PropTypes.func, + + /** + * The responder has been taken from the `View`. Might be taken by other views after a call to + * `onResponderTerminationRequest`, or might be taken by the OS without asking (e.g., happens + * with control center/ notification center on iOS) + * + * `View.props.onResponderTerminate: (event) => {}`, where `event` is a synthetic touch event as + * described above. + */ + onResponderTerminate: PropTypes.func, + + /** + * Some other `View` wants to become responder and is asking this `View` to release its + * responder. Returning `true` allows its release. + * + * `View.props.onResponderTerminationRequest: (event) => {}`, where `event` is a synthetic touch + * event as described above. + */ + onResponderTerminationRequest: PropTypes.func, + + /** + * Does this view want to become responder on the start of a touch? + * + * `View.props.onStartShouldSetResponder: (event) => [true | false]`, where `event` is a + * synthetic touch event as described above. + */ + onStartShouldSetResponder: PropTypes.func, + + /** + * If a parent `View` wants to prevent a child `View` from becoming responder on a touch start, + * it should have this handler which returns `true`. + * + * `View.props.onStartShouldSetResponderCapture: (event) => [true | false]`, where `event` is a + * synthetic touch event as described above. + */ + onStartShouldSetResponderCapture: PropTypes.func, + + /** + * Does this view want to "claim" touch responsiveness? This is called for every touch move on + * the `View` when it is not the responder. + * + * `View.props.onMoveShouldSetResponder: (event) => [true | false]`, where `event` is a + * synthetic touch event as described above. + */ + onMoveShouldSetResponder: PropTypes.func, + + /** + * If a parent `View` wants to prevent a child `View` from becoming responder on a move, + * it should have this handler which returns `true`. + * + * `View.props.onMoveShouldSetResponderCapture: (event) => [true | false]`, where `event` is a + * synthetic touch event as described above. + */ + onMoveShouldSetResponderCapture: PropTypes.func, + + /** + * This defines how far a touch event can start away from the view. + * Typical interface guidelines recommend touch targets that are at least + * 30 - 40 points/density-independent pixels. + * + * For example, if a touchable view has a height of 20 the touchable height can be extended to + * 40 with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}` + * + * > The touch area never extends past the parent view bounds and the Z-index + * > of sibling views always takes precedence if a touch hits two overlapping + * > views. + */ + hitSlop: EdgeInsetsPropType, + + /** + * Invoked on mount and layout changes with: + * + * `{nativeEvent: { layout: {x, y, width, height}}}` + * + * This event is fired immediately once the layout has been calculated, but + * the new layout may not yet be reflected on the screen at the time the + * event is received, especially if a layout animation is in progress. + */ + onLayout: PropTypes.func, + + /** + * Controls whether the `View` can be the target of touch events. + * + * - `'auto'`: The View can be the target of touch events. + * - `'none'`: The View is never the target of touch events. + * - `'box-none'`: The View is never the target of touch events but it's + * subviews can be. It behaves like if the view had the following classes + * in CSS: + * ``` + * .box-none { + * pointer-events: none; + * } + * .box-none * { + * pointer-events: all; + * } + * ``` + * - `'box-only'`: The view can be the target of touch events but it's + * subviews cannot be. It behaves like if the view had the following classes + * in CSS: + * ``` + * .box-only { + * pointer-events: all; + * } + * .box-only * { + * pointer-events: none; + * } + * ``` + * > Since `pointerEvents` does not affect layout/appearance, and we are + * > already deviating from the spec by adding additional modes, we opt to not + * > include `pointerEvents` on `style`. On some platforms, we would need to + * > implement it as a `className` anyways. Using `style` or not is an + * > implementation detail of the platform. + */ + pointerEvents: PropTypes.oneOf([ + 'box-none', + 'none', + 'box-only', + 'auto', + ]), + style: stylePropType, + + /** + * This is a special performance property exposed by `RCTView` and is useful + * for scrolling content when there are many subviews, most of which are + * offscreen. For this property to be effective, it must be applied to a + * view that contains many subviews that extend outside its bound. The + * subviews must also have `overflow: hidden`, as should the containing view + * (or one of its superviews). + */ + removeClippedSubviews: PropTypes.bool, + + /** + * Whether this `View` should render itself (and all of its children) into a + * single hardware texture on the GPU. + * + * On Android, this is useful for animations and interactions that only + * modify opacity, rotation, translation, and/or scale: in those cases, the + * view doesn't have to be redrawn and display lists don't need to be + * re-executed. The texture can just be re-used and re-composited with + * different parameters. The downside is that this can use up limited video + * memory, so this prop should be set back to false at the end of the + * interaction/animation. + * + * @platform android + */ + renderToHardwareTextureAndroid: PropTypes.bool, + + /** + * Whether this `View` should be rendered as a bitmap before compositing. + * + * On iOS, this is useful for animations and interactions that do not + * modify this component's dimensions nor its children; for example, when + * translating the position of a static view, rasterization allows the + * renderer to reuse a cached bitmap of a static view and quickly composite + * it during each frame. + * + * Rasterization incurs an off-screen drawing pass and the bitmap consumes + * memory. Test and measure when using this property. + * + * @platform ios + */ + shouldRasterizeIOS: PropTypes.bool, + + /** + * Views that are only used to layout their children or otherwise don't draw + * anything may be automatically removed from the native hierarchy as an + * optimization. Set this property to `false` to disable this optimization and + * ensure that this `View` exists in the native view hierarchy. + * + * @platform android + */ + collapsable: PropTypes.bool, + + /** + * Whether this `View` needs to rendered offscreen and composited with an alpha + * in order to preserve 100% correct colors and blending behavior. The default + * (`false`) falls back to drawing the component and its children with an alpha + * applied to the paint used to draw each element instead of rendering the full + * component offscreen and compositing it back with an alpha value. This default + * may be noticeable and undesired in the case where the `View` you are setting + * an opacity on has multiple overlapping elements (e.g. multiple overlapping + * `View`s, or text and a background). + * + * Rendering offscreen to preserve correct alpha behavior is extremely + * expensive and hard to debug for non-native developers, which is why it is + * not turned on by default. If you do need to enable this property for an + * animation, consider combining it with renderToHardwareTextureAndroid if the + * view **contents** are static (i.e. it doesn't need to be redrawn each frame). + * If that property is enabled, this View will be rendered off-screen once, + * saved in a hardware texture, and then composited onto the screen with an alpha + * each frame without having to switch rendering targets on the GPU. + * + * @platform android + */ + needsOffscreenAlphaCompositing: PropTypes.bool, +}; diff --git a/Libraries/Components/ViewPager/ViewPagerAndroid.android.js b/Libraries/Components/ViewPager/ViewPagerAndroid.android.js index 7345901cf22f47..be70e5a12517a7 100644 --- a/Libraries/Components/ViewPager/ViewPagerAndroid.android.js +++ b/Libraries/Components/ViewPager/ViewPagerAndroid.android.js @@ -14,7 +14,7 @@ var React = require('React'); var ReactNative = require('ReactNative'); var UIManager = require('UIManager'); -var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var dismissKeyboard = require('dismissKeyboard'); var requireNativeComponent = require('requireNativeComponent'); @@ -81,7 +81,7 @@ class ViewPagerAndroid extends React.Component { }; static propTypes = { - ...View.propTypes, + ...ViewPropTypes, /** * Index of initial page that should be selected. Use `setPage` method to * update the page, and `onPageSelected` to monitor page changes diff --git a/Libraries/Components/WebView/WebView.android.js b/Libraries/Components/WebView/WebView.android.js index 4de8be62dbb289..2ded46b1b8e655 100644 --- a/Libraries/Components/WebView/WebView.android.js +++ b/Libraries/Components/WebView/WebView.android.js @@ -18,6 +18,8 @@ var StyleSheet = require('StyleSheet'); var UIManager = require('UIManager'); var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + var deprecatedPropType = require('deprecatedPropType'); var keyMirror = require('fbjs/lib/keyMirror'); var requireNativeComponent = require('requireNativeComponent'); @@ -46,7 +48,7 @@ var defaultRenderLoading = () => ( */ class WebView extends React.Component { static propTypes = { - ...View.propTypes, + ...ViewPropTypes, renderError: PropTypes.func, renderLoading: PropTypes.func, onLoad: PropTypes.func, @@ -59,7 +61,7 @@ class WebView extends React.Component { onMessage: PropTypes.func, onContentSizeChange: PropTypes.func, startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load - style: View.propTypes.style, + style: ViewPropTypes.style, html: deprecatedPropType( PropTypes.string, @@ -182,11 +184,18 @@ class WebView extends React.Component { 'always', 'compatibility' ]), + + /** + * Used on Android only, controls whether form autocomplete data should be saved + * @platform android + */ + saveFormDataDisabled: PropTypes.bool, }; static defaultProps = { javaScriptEnabled : true, scalesPageToFit: true, + saveFormDataDisabled: false }; state = { @@ -259,6 +268,7 @@ class WebView extends React.Component { mediaPlaybackRequiresUserAction={this.props.mediaPlaybackRequiresUserAction} allowUniversalAccessFromFileURLs={this.props.allowUniversalAccessFromFileURLs} mixedContentMode={this.props.mixedContentMode} + saveFormDataDisabled={this.props.saveFormDataDisabled} />; return ( diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 96488b2c3ccd92..eae1ed6f6f2ee7 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -19,6 +19,7 @@ var StyleSheet = require('StyleSheet'); var Text = require('Text'); var UIManager = require('UIManager'); var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var ScrollView = require('ScrollView'); var deprecatedPropType = require('deprecatedPropType'); @@ -117,7 +118,7 @@ class WebView extends React.Component { static NavigationType = NavigationType; static propTypes = { - ...View.propTypes, + ...ViewPropTypes, html: deprecatedPropType( PropTypes.string, @@ -253,7 +254,7 @@ class WebView extends React.Component { /** * The style to apply to the `WebView`. */ - style: View.propTypes.style, + style: ViewPropTypes.style, /** * Determines the types of data converted to clickable URLs in the web view’s content. diff --git a/Libraries/Core/Timers/JSTimers.js b/Libraries/Core/Timers/JSTimers.js index 842b47ba5455fc..a236c07cb76510 100644 --- a/Libraries/Core/Timers/JSTimers.js +++ b/Libraries/Core/Timers/JSTimers.js @@ -13,10 +13,10 @@ // Note that the module JSTimers is split into two in order to solve a cycle // in dependencies. NativeModules > BatchedBridge > MessageQueue > JSTimersExecution -const RCTTiming = require('NativeModules').Timing; const JSTimersExecution = require('JSTimersExecution'); +const Platform = require('Platform'); -const parseErrorStack = require('parseErrorStack'); +const {Timing} = require('NativeModules'); import type {JSTimerType} from 'JSTimersExecution'; @@ -36,6 +36,7 @@ function _allocateCallback(func: Function, type: JSTimerType): number { JSTimersExecution.callbacks[freeIndex] = func; JSTimersExecution.types[freeIndex] = type; if (__DEV__) { + const parseErrorStack = require('parseErrorStack'); const e = (new Error() : any); e.framesToPop = 1; const stack = parseErrorStack(e); @@ -59,11 +60,19 @@ function _freeCallback(timerID: number) { JSTimersExecution._clearIndex(index); const type = JSTimersExecution.types[index]; if (type !== 'setImmediate' && type !== 'requestIdleCallback') { - RCTTiming.deleteTimer(timerID); + Timing.deleteTimer(timerID); } } } +const MAX_TIMER_DURATION_MS = 60 * 1000; +const IS_ANDROID = Platform.OS === 'android'; +const ANDROID_LONG_TIMER_MESSAGE = + 'Setting a timer for a long period of time, i.e. multiple minutes, is a ' + + 'performance and correctness issue on Android as it keeps the timer ' + + 'module awake, and timers can only be called when the app is in the foreground. ' + + 'See https://github.com/facebook/react-native/issues/12981 for more info.'; + /** * JS implementation of timer functions. Must be completely driven by an * external clock signal, all that's stored here is timerID, timer type, and @@ -75,8 +84,13 @@ const JSTimers = { * @param {number} duration Number of milliseconds. */ setTimeout: function(func: Function, duration: number, ...args?: any): number { + if (__DEV__ && IS_ANDROID && duration > MAX_TIMER_DURATION_MS) { + console.warn( + ANDROID_LONG_TIMER_MESSAGE + '\n' + '(Saw setTimeout with duration ' + + duration + 'ms)'); + } const id = _allocateCallback(() => func.apply(undefined, args), 'setTimeout'); - RCTTiming.createTimer(id, duration || 0, Date.now(), /* recurring */ false); + Timing.createTimer(id, duration || 0, Date.now(), /* recurring */ false); return id; }, @@ -85,8 +99,13 @@ const JSTimers = { * @param {number} duration Number of milliseconds. */ setInterval: function(func: Function, duration: number, ...args?: any): number { + if (__DEV__ && IS_ANDROID && duration > MAX_TIMER_DURATION_MS) { + console.warn( + ANDROID_LONG_TIMER_MESSAGE + '\n' + '(Saw setInterval with duration ' + + duration + 'ms)'); + } const id = _allocateCallback(() => func.apply(undefined, args), 'setInterval'); - RCTTiming.createTimer(id, duration || 0, Date.now(), /* recurring */ true); + Timing.createTimer(id, duration || 0, Date.now(), /* recurring */ true); return id; }, @@ -105,7 +124,7 @@ const JSTimers = { */ requestAnimationFrame: function(func : Function) { const id = _allocateCallback(func, 'requestAnimationFrame'); - RCTTiming.createTimer(id, 1, Date.now(), /* recurring */ false); + Timing.createTimer(id, 1, Date.now(), /* recurring */ false); return id; }, @@ -115,7 +134,7 @@ const JSTimers = { */ requestIdleCallback: function(func : Function) { if (JSTimersExecution.requestIdleCallbacks.length === 0) { - RCTTiming.setSendIdleEvents(true); + Timing.setSendIdleEvents(true); } const id = _allocateCallback(func, 'requestIdleCallback'); @@ -131,7 +150,7 @@ const JSTimers = { } if (JSTimersExecution.requestIdleCallbacks.length === 0) { - RCTTiming.setSendIdleEvents(false); + Timing.setSendIdleEvents(false); } }, diff --git a/Libraries/CustomComponents/LICENSE b/Libraries/CustomComponents/LICENSE deleted file mode 100644 index ef4ae1360ea149..00000000000000 --- a/Libraries/CustomComponents/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -LICENSE AGREEMENT - -For React Native Custom Components software - -Copyright (c) 2015, Facebook, Inc. All rights reserved. - -Facebook, Inc. ("Facebook") owns all right, title and interest, including all intellectual property and other proprietary rights, in and to the React Native Custom Components software (the "Software"). Subject to your compliance with these terms, you are hereby granted a non-exclusive, worldwide, royalty-free copyright license to (1) use and copy the Software; and (2) reproduce and distribute the Software as part of your own software ("Your Software"). Facebook reserves all rights not expressly granted to you in this license agreement. - -THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationAnimatedValueSubscription.js b/Libraries/CustomComponents/NavigationExperimental/NavigationAnimatedValueSubscription.js deleted file mode 100644 index 5f06907124f826..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationAnimatedValueSubscription.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationAnimatedValueSubscription - * @flow - */ -'use strict'; - -import type { - NavigationAnimatedValue -} from 'NavigationTypeDefinition'; - -class NavigationAnimatedValueSubscription { - _value: NavigationAnimatedValue; - _token: string; - - constructor(value: NavigationAnimatedValue, callback: Function) { - this._value = value; - this._token = value.addListener(callback); - } - - remove(): void { - this._value.removeListener(this._token); - } -} - -module.exports = NavigationAnimatedValueSubscription; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js deleted file mode 100644 index e2f42fed3ed1ee..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationCard - * @flow - */ -'use strict'; - -const Animated = require('Animated'); -const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder'); -const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator'); -const NavigationPagerPanResponder = require('NavigationPagerPanResponder'); -const NavigationPagerStyleInterpolator = require('NavigationPagerStyleInterpolator'); -const NavigationPointerEventsContainer = require('NavigationPointerEventsContainer'); -const NavigationPropTypes = require('NavigationPropTypes'); -const React = require('React'); -const StyleSheet = require('StyleSheet'); - -import type { - NavigationPanPanHandlers, - NavigationSceneRenderer, - NavigationSceneRendererProps, -} from 'NavigationTypeDefinition'; - -type Props = NavigationSceneRendererProps & { - onComponentRef: (ref: any) => void, - onNavigateBack: ?Function, - panHandlers: ?NavigationPanPanHandlers, - pointerEvents: string, - renderScene: NavigationSceneRenderer, - style: any, -}; - -const {PropTypes} = React; - -/** - * Component that renders the scene as card for the . - */ -class NavigationCard extends React.Component { - props: Props; - - static propTypes = { - ...NavigationPropTypes.SceneRendererProps, - onComponentRef: PropTypes.func.isRequired, - onNavigateBack: PropTypes.func, - panHandlers: NavigationPropTypes.panHandlers, - pointerEvents: PropTypes.string.isRequired, - renderScene: PropTypes.func.isRequired, - style: PropTypes.any, - }; - - render(): React.Element { - const { - panHandlers, - pointerEvents, - renderScene, - style, - ...props /* NavigationSceneRendererProps */ - } = this.props; - - const viewStyle = style === undefined ? - NavigationCardStackStyleInterpolator.forHorizontal(props) : - style; - - const viewPanHandlers = panHandlers === undefined ? - NavigationCardStackPanResponder.forHorizontal({ - ...props, - onNavigateBack: this.props.onNavigateBack, - }) : - panHandlers; - - return ( - - {renderScene(props)} - - ); - } -} - -const styles = StyleSheet.create({ - main: { - backgroundColor: '#E9E9EF', - bottom: 0, - left: 0, - position: 'absolute', - right: 0, - shadowColor: 'black', - shadowOffset: {width: 0, height: 0}, - shadowOpacity: 0.4, - shadowRadius: 10, - top: 0, - }, -}); - -NavigationCard = NavigationPointerEventsContainer.create(NavigationCard); - -NavigationCard.CardStackPanResponder = NavigationCardStackPanResponder; -NavigationCard.CardStackStyleInterpolator = NavigationCardStackStyleInterpolator; -NavigationCard.PagerPanResponder = NavigationPagerPanResponder; -NavigationCard.PagerStyleInterpolator = NavigationPagerStyleInterpolator; - -module.exports = NavigationCard; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js deleted file mode 100644 index 3a54633e7eaa8f..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js +++ /dev/null @@ -1,329 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationCardStack - * @flow - */ -'use strict'; - -const NativeAnimatedModule = require('NativeModules').NativeAnimatedModule; -const NavigationCard = require('NavigationCard'); -const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder'); -const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator'); -const NavigationPropTypes = require('NavigationPropTypes'); -const NavigationTransitioner = require('NavigationTransitioner'); -const React = require('React'); -const StyleSheet = require('StyleSheet'); -const View = require('View'); - -const {PropTypes} = React; -const {Directions} = NavigationCardStackPanResponder; - -import type { - NavigationState, - NavigationSceneRenderer, - NavigationSceneRendererProps, - NavigationTransitionProps, - NavigationStyleInterpolator, -} from 'NavigationTypeDefinition'; - -import type { - NavigationGestureDirection, -} from 'NavigationCardStackPanResponder'; - -type Props = { - direction: NavigationGestureDirection, - navigationState: NavigationState, - onNavigateBack?: Function, - renderHeader: ?NavigationSceneRenderer, - renderScene: NavigationSceneRenderer, - cardStyle?: any, - style: any, - gestureResponseDistance?: ?number, - enableGestures: ?boolean, - cardStyleInterpolator?: ?NavigationStyleInterpolator, - scenesStyle?: any, -}; - -type DefaultProps = { - direction: NavigationGestureDirection, - enableGestures: boolean, -}; - -/** - * A controlled navigation view that renders a stack of cards. - * - * ```html - * +------------+ - * +-| Header | - * +-+ |------------| - * | | | | - * | | | Focused | - * | | | Card | - * | | | | - * +-+ | | - * +-+ | - * +------------+ - * ``` - * - * ## Example - * - * ```js - * - * class App extends React.Component { - * constructor(props, context) { - * this.state = { - * navigation: { - * index: 0, - * routes: [ - * {key: 'page 1'}, - * }, - * }, - * }; - * } - * - * render() { - * return ( - * - * ); - * } - * - * _renderScene: (props) => { - * return ( - * - * {props.scene.route.key} - * - * ); - * }; - * ``` - */ -class NavigationCardStack extends React.Component { - _render : NavigationSceneRenderer; - _renderScene : NavigationSceneRenderer; - - static propTypes = { - /** - * Custom style applied to the card. - */ - cardStyle: PropTypes.any, - - /** - * Direction of the cards movement. Value could be `horizontal` or - * `vertical`. Default value is `horizontal`. - */ - direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]), - - /** - * The distance from the edge of the card which gesture response can start - * for. Default value is `30`. - */ - gestureResponseDistance: PropTypes.number, - - /** - * An interpolator function that is passed an object parameter of type - * NavigationSceneRendererProps and should return a style object to apply to - * the transitioning navigation card. - * - * Default interpolator transitions translateX, scale, and opacity. - */ - cardStyleInterpolator: PropTypes.func, - - /** - * Enable gestures. Default value is true. - * - * When disabled, transition animations will be handled natively, which - * improves performance of the animation. In future iterations, gestures - * will also work with native-driven animation. - */ - enableGestures: PropTypes.bool, - - /** - * The controlled navigation state. Typically, the navigation state - * look like this: - * - * ```js - * const navigationState = { - * index: 0, // the index of the selected route. - * routes: [ // A list of routes. - * {key: 'page 1'}, // The 1st route. - * {key: 'page 2'}, // The second route. - * ], - * }; - * ``` - */ - navigationState: NavigationPropTypes.navigationState.isRequired, - - /** - * Callback that is called when the "back" action is performed. - * This happens when the back button is pressed or the back gesture is - * performed. - */ - onNavigateBack: PropTypes.func, - - /** - * Function that renders the header. - */ - renderHeader: PropTypes.func, - - /** - * Function that renders the a scene for a route. - */ - renderScene: PropTypes.func.isRequired, - - /** - * Custom style applied to the cards stack. - */ - style: View.propTypes.style, - - /** - * Custom style applied to the scenes stack. - */ - scenesStyle: View.propTypes.style, - }; - - static defaultProps: DefaultProps = { - direction: Directions.HORIZONTAL, - enableGestures: true, - }; - - constructor(props: Props, context: any) { - super(props, context); - } - - componentWillMount(): void { - this._render = this._render.bind(this); - this._renderScene = this._renderScene.bind(this); - } - - render(): React.Element { - return ( - - ); - } - - _configureTransition = () => { - const isVertical = this.props.direction === 'vertical'; - const animationConfig = {}; - if ( - !!NativeAnimatedModule - - // Gestures do not work with the current iteration of native animation - // driving. When gestures are disabled, we can drive natively. - && !this.props.enableGestures - - // Native animation support also depends on the transforms used: - && NavigationCardStackStyleInterpolator.canUseNativeDriver(isVertical) - ) { - animationConfig.useNativeDriver = true; - } - return animationConfig; - } - - _render(props: NavigationTransitionProps): React.Element { - const { - renderHeader, - } = this.props; - - const header = renderHeader ? {renderHeader(props)} : null; - - const scenes = props.scenes.map( - scene => this._renderScene({ - ...props, - scene, - }) - ); - - return ( - - - {scenes} - - {header} - - ); - } - - _renderScene(props: NavigationSceneRendererProps): React.Element { - const isVertical = this.props.direction === 'vertical'; - - const interpolator = this.props.cardStyleInterpolator || (isVertical ? - NavigationCardStackStyleInterpolator.forVertical : - NavigationCardStackStyleInterpolator.forHorizontal); - - const style = interpolator(props); - - let panHandlers = null; - - if (this.props.enableGestures) { - const panHandlersProps = { - ...props, - onNavigateBack: this.props.onNavigateBack, - gestureResponseDistance: this.props.gestureResponseDistance, - }; - panHandlers = isVertical ? - NavigationCardStackPanResponder.forVertical(panHandlersProps) : - NavigationCardStackPanResponder.forHorizontal(panHandlersProps); - } - - return ( - - ); - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - // Header is physically rendered after scenes so that Header won't be - // covered by the shadows of the scenes. - // That said, we'd have use `flexDirection: 'column-reverse'` to move - // Header above the scenes. - flexDirection: 'column-reverse', - }, - scenes: { - flex: 1, - }, -}); - -module.exports = NavigationCardStack; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackPanResponder.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackPanResponder.js deleted file mode 100644 index 6ebf0661026658..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackPanResponder.js +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule NavigationCardStackPanResponder - * @flow - */ -'use strict'; - -const Animated = require('Animated'); -const I18nManager = require('I18nManager'); -const NavigationAbstractPanResponder = require('NavigationAbstractPanResponder'); - -const clamp = require('clamp'); - -import type { - NavigationPanPanHandlers, - NavigationSceneRendererProps, -} from 'NavigationTypeDefinition'; - -const emptyFunction = () => {}; - -/** - * The duration of the card animation in milliseconds. - */ -const ANIMATION_DURATION = 250; - -/** - * The threshold to invoke the `onNavigateBack` action. - * For instance, `1 / 3` means that moving greater than 1 / 3 of the width of - * the view will navigate. - */ -const POSITION_THRESHOLD = 1 / 3; - -/** - * The threshold (in pixels) to start the gesture action. - */ -const RESPOND_THRESHOLD = 15; - -/** - * The threshold (in pixels) to finish the gesture action. - */ -const DISTANCE_THRESHOLD = 100; - -/** - * Primitive gesture directions. - */ -const Directions = { - 'HORIZONTAL': 'horizontal', - 'VERTICAL': 'vertical', -}; - -export type NavigationGestureDirection = 'horizontal' | 'vertical'; - -type Props = NavigationSceneRendererProps & { - onNavigateBack: ?Function, - /** - * The distance from the edge of the navigator which gesture response can start for. - **/ - gestureResponseDistance: ?number, -}; - -/** - * Pan responder that handles gesture for a card in the cards stack. - * - * +------------+ - * +-+ | - * +-+ | | - * | | | | - * | | | Focused | - * | | | Card | - * | | | | - * +-+ | | - * +-+ | - * +------------+ - */ -class NavigationCardStackPanResponder extends NavigationAbstractPanResponder { - - _isResponding: boolean; - _isVertical: boolean; - _props: Props; - _startValue: number; - - constructor( - direction: NavigationGestureDirection, - props: Props, - ) { - super(); - this._isResponding = false; - this._isVertical = direction === Directions.VERTICAL; - this._props = props; - this._startValue = 0; - - // Hack to make this work with native driven animations. We add a single listener - // so the JS value of the following animated values gets updated. We rely on - // some Animated private APIs and not doing so would require using a bunch of - // value listeners but we'd have to remove them to not leak and I'm not sure - // when we'd do that with the current structure we have. `stopAnimation` callback - // is also broken with native animated values that have no listeners so if we - // want to remove this we have to fix this too. - this._addNativeListener(this._props.layout.width); - this._addNativeListener(this._props.layout.height); - this._addNativeListener(this._props.position); - } - - onMoveShouldSetPanResponder(event: any, gesture: any): boolean { - const props = this._props; - - if (props.navigationState.index !== props.scene.index) { - return false; - } - - const layout = props.layout; - const isVertical = this._isVertical; - const index = props.navigationState.index; - const currentDragDistance = gesture[isVertical ? 'dy' : 'dx']; - const currentDragPosition = gesture[isVertical ? 'moveY' : 'moveX']; - const maxDragDistance = isVertical ? - layout.height.__getValue() : - layout.width.__getValue(); - - const positionMax = isVertical ? - props.gestureResponseDistance : - /** - * For horizontal scroll views, a distance of 30 from the left of the screen is the - * standard maximum position to start touch responsiveness. - */ - props.gestureResponseDistance || 30; - - if (positionMax != null && currentDragPosition > positionMax) { - return false; - } - - return ( - Math.abs(currentDragDistance) > RESPOND_THRESHOLD && - maxDragDistance > 0 && - index > 0 - ); - } - - onPanResponderGrant(): void { - this._isResponding = false; - this._props.position.stopAnimation((value: number) => { - this._isResponding = true; - this._startValue = value; - }); - } - - onPanResponderMove(event: any, gesture: any): void { - if (!this._isResponding) { - return; - } - - const props = this._props; - const layout = props.layout; - const isVertical = this._isVertical; - const axis = isVertical ? 'dy' : 'dx'; - const index = props.navigationState.index; - const distance = isVertical ? - layout.height.__getValue() : - layout.width.__getValue(); - const currentValue = I18nManager.isRTL && axis === 'dx' ? - this._startValue + (gesture[axis] / distance) : - this._startValue - (gesture[axis] / distance); - - const value = clamp( - index - 1, - currentValue, - index - ); - - props.position.setValue(value); - } - - onPanResponderRelease(event: any, gesture: any): void { - if (!this._isResponding) { - return; - } - - this._isResponding = false; - - const props = this._props; - const isVertical = this._isVertical; - const axis = isVertical ? 'dy' : 'dx'; - const index = props.navigationState.index; - const distance = I18nManager.isRTL && axis === 'dx' ? - -gesture[axis] : - gesture[axis]; - - props.position.stopAnimation((value: number) => { - this._reset(); - - if (!props.onNavigateBack) { - return; - } - - if ( - distance > DISTANCE_THRESHOLD || - value <= index - POSITION_THRESHOLD - ) { - props.onNavigateBack(); - } - }); - } - - onPanResponderTerminate(): void { - this._isResponding = false; - this._reset(); - } - - _reset(): void { - const props = this._props; - Animated.timing( - props.position, - { - toValue: props.navigationState.index, - duration: ANIMATION_DURATION, - useNativeDriver: props.position.__isNative, - } - ).start(); - } - - _addNativeListener(animatedValue) { - if (!animatedValue.__isNative) { - return; - } - - if (Object.keys(animatedValue._listeners).length === 0) { - animatedValue.addListener(emptyFunction); - } - } -} - -function createPanHandlers( - direction: NavigationGestureDirection, - props: Props, -): NavigationPanPanHandlers { - const responder = new NavigationCardStackPanResponder(direction, props); - return responder.panHandlers; -} - -function forHorizontal( - props: Props, -): NavigationPanPanHandlers { - return createPanHandlers(Directions.HORIZONTAL, props); -} - -function forVertical( - props: Props, -): NavigationPanPanHandlers { - return createPanHandlers(Directions.VERTICAL, props); -} - -module.exports = { - // constants - ANIMATION_DURATION, - DISTANCE_THRESHOLD, - POSITION_THRESHOLD, - RESPOND_THRESHOLD, - - // enums - Directions, - - // methods. - forHorizontal, - forVertical, -}; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js deleted file mode 100644 index e3ef1ad054306b..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationCardStackStyleInterpolator - * @flow - */ -'use strict'; - -const I18nManager = require('I18nManager'); - -import type { - NavigationSceneRendererProps, -} from 'NavigationTypeDefinition'; - -/** - * Utility that builds the style for the card in the cards stack. - * - * +------------+ - * +-+ | - * +-+ | | - * | | | | - * | | | Focused | - * | | | Card | - * | | | | - * +-+ | | - * +-+ | - * +------------+ - */ - -/** - * Render the initial style when the initial layout isn't measured yet. - */ -function forInitial(props: NavigationSceneRendererProps): Object { - const { - navigationState, - scene, - } = props; - - const focused = navigationState.index === scene.index; - const opacity = focused ? 1 : 0; - // If not focused, move the scene to the far away. - const translate = focused ? 0 : 1000000; - return { - opacity, - transform: [ - { translateX: translate }, - { translateY: translate }, - ], - }; -} - -function forHorizontal(props: NavigationSceneRendererProps): Object { - const { - layout, - position, - scene, - } = props; - - if (!layout.isMeasured) { - return forInitial(props); - } - - const index = scene.index; - const inputRange = [index - 1, index, index + 0.99, index + 1]; - const width = layout.initWidth; - const outputRange = I18nManager.isRTL ? - ([-width, 0, 10, 10]: Array) : - ([width, 0, -10, -10]: Array); - - - const opacity = position.interpolate({ - inputRange, - outputRange: ([1, 1, 0.3, 0]: Array), - }); - - const scale = position.interpolate({ - inputRange, - outputRange: ([1, 1, 0.95, 0.95]: Array), - }); - - const translateY = 0; - const translateX = position.interpolate({ - inputRange, - outputRange, - }); - - return { - opacity, - transform: [ - { scale }, - { translateX }, - { translateY }, - ], - }; -} - -function forVertical(props: NavigationSceneRendererProps): Object { - const { - layout, - position, - scene, - } = props; - - if (!layout.isMeasured) { - return forInitial(props); - } - - const index = scene.index; - const inputRange = [index - 1, index, index + 0.99, index + 1]; - const height = layout.initHeight; - - const opacity = position.interpolate({ - inputRange, - outputRange: ([1, 1, 0.3, 0]: Array), - }); - - const scale = position.interpolate({ - inputRange, - outputRange: ([1, 1, 0.95, 0.95]: Array), - }); - - const translateX = 0; - const translateY = position.interpolate({ - inputRange, - outputRange: ([height, 0, -10, -10]: Array), - }); - - return { - opacity, - transform: [ - { scale }, - { translateX }, - { translateY }, - ], - }; -} - -function canUseNativeDriver(isVertical: boolean): boolean { - // The native driver can be enabled for this interpolator because the scale, - // translateX, and translateY transforms are supported with the native - // animation driver. - - return true; -} - -module.exports = { - forHorizontal, - forVertical, - canUseNativeDriver, -}; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js deleted file mode 100644 index 87ce2f36aa94ca..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js +++ /dev/null @@ -1,291 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationHeader - * @flow - */ -'use strict'; - -const NavigationHeaderBackButton = require('NavigationHeaderBackButton'); -const NavigationHeaderStyleInterpolator = require('NavigationHeaderStyleInterpolator'); -const NavigationHeaderTitle = require('NavigationHeaderTitle'); -const NavigationPropTypes = require('NavigationPropTypes'); -const React = require('React'); -const ReactComponentWithPureRenderMixin = require('react/lib/ReactComponentWithPureRenderMixin'); -const ReactNative = require('react-native'); -const TVEventHandler = require('TVEventHandler'); - -const { - Animated, - Platform, - StyleSheet, - View, -} = ReactNative; - -import type { - NavigationSceneRendererProps, - NavigationStyleInterpolator, -} from 'NavigationTypeDefinition'; - -type SubViewProps = NavigationSceneRendererProps & { - onNavigateBack: ?Function, -}; - -type SubViewRenderer = (subViewProps: SubViewProps) => ?React.Element; - -type DefaultProps = { - renderLeftComponent: SubViewRenderer, - renderRightComponent: SubViewRenderer, - renderTitleComponent: SubViewRenderer, - statusBarHeight: number | Animated.Value, -}; - -type Props = NavigationSceneRendererProps & { - onNavigateBack: ?Function, - renderLeftComponent: SubViewRenderer, - renderRightComponent: SubViewRenderer, - renderTitleComponent: SubViewRenderer, - style?: any, - viewProps?: any, - statusBarHeight: number | Animated.Value, -}; - -type SubViewName = 'left' | 'title' | 'right'; - -const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56; -const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0; -const {PropTypes} = React; - -class NavigationHeader extends React.Component { - props: Props; - - static defaultProps = { - - renderTitleComponent: (props: SubViewProps) => { - const title = String(props.scene.route.title || ''); - return {title}; - }, - - renderLeftComponent: (props: SubViewProps) => { - if (props.scene.index === 0 || !props.onNavigateBack) { - return null; - } - return ( - - ); - }, - - renderRightComponent: (props: SubViewProps) => { - return null; - }, - - statusBarHeight: STATUSBAR_HEIGHT, - }; - - static propTypes = { - ...NavigationPropTypes.SceneRendererProps, - onNavigateBack: PropTypes.func, - renderLeftComponent: PropTypes.func, - renderRightComponent: PropTypes.func, - renderTitleComponent: PropTypes.func, - style: View.propTypes.style, - statusBarHeight: PropTypes.number, - viewProps: PropTypes.shape(View.propTypes), - }; - - shouldComponentUpdate(nextProps: Props, nextState: any): boolean { - return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call( - this, - nextProps, - nextState - ); - } - - _tvEventHandler: TVEventHandler; - - componentDidMount(): void { - this._tvEventHandler = new TVEventHandler(); - this._tvEventHandler.enable(this, function(cmp, evt) { - if (evt && evt.eventType === 'menu') { - cmp.props.onNavigateBack && cmp.props.onNavigateBack(); - } - }); - } - - componentWillUnmount(): void { - if (this._tvEventHandler) { - this._tvEventHandler.disable(); - delete this._tvEventHandler; - } - } - - render(): React.Element { - const { scenes, style, viewProps } = this.props; - - const scenesProps = scenes.map(scene => { - const props = NavigationPropTypes.extractSceneRendererProps(this.props); - props.scene = scene; - return props; - }); - - const barHeight = (this.props.statusBarHeight instanceof Animated.Value) - ? Animated.add(this.props.statusBarHeight, new Animated.Value(APPBAR_HEIGHT)) - : APPBAR_HEIGHT + this.props.statusBarHeight; - - return ( - - {scenesProps.map(this._renderLeft, this)} - {scenesProps.map(this._renderTitle, this)} - {scenesProps.map(this._renderRight, this)} - - ); - } - - _renderLeft = (props: NavigationSceneRendererProps): ?React.Element => { - return this._renderSubView( - props, - 'left', - this.props.renderLeftComponent, - NavigationHeaderStyleInterpolator.forLeft, - ); - }; - - _renderTitle = (props: NavigationSceneRendererProps): ?React.Element => { - return this._renderSubView( - props, - 'title', - this.props.renderTitleComponent, - NavigationHeaderStyleInterpolator.forCenter, - ); - }; - - _renderRight = (props: NavigationSceneRendererProps): ?React.Element => { - return this._renderSubView( - props, - 'right', - this.props.renderRightComponent, - NavigationHeaderStyleInterpolator.forRight, - ); - }; - - _renderSubView( - props: NavigationSceneRendererProps, - name: SubViewName, - renderer: SubViewRenderer, - styleInterpolator: NavigationStyleInterpolator, - ): ?React.Element { - const { - scene, - navigationState, - } = props; - - const { - index, - isStale, - key, - } = scene; - - const offset = navigationState.index - index; - - if (Math.abs(offset) > 2) { - // Scene is far away from the active scene. Hides it to avoid unnecessary - // rendering. - return null; - } - - const subViewProps = {...props, onNavigateBack: this.props.onNavigateBack}; - const subView = renderer(subViewProps); - if (subView === null) { - return null; - } - - const pointerEvents = offset !== 0 || isStale ? 'none' : 'box-none'; - return ( - - {subView} - - ); - } - - static HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT; - static Title = NavigationHeaderTitle; - static BackButton = NavigationHeaderBackButton; - -} - -const styles = StyleSheet.create({ - appbar: { - alignItems: 'center', - backgroundColor: Platform.OS === 'ios' ? '#EFEFF2' : '#FFF', - borderBottomColor: 'rgba(0, 0, 0, .15)', - borderBottomWidth: Platform.OS === 'ios' ? StyleSheet.hairlineWidth : 0, - elevation: 4, - flexDirection: 'row', - justifyContent: 'flex-start', - }, - - title: { - bottom: 0, - left: APPBAR_HEIGHT, - position: 'absolute', - right: APPBAR_HEIGHT, - top: 0, - }, - - left: { - bottom: 0, - left: 0, - position: 'absolute', - top: 0, - }, - - right: { - bottom: 0, - position: 'absolute', - right: 0, - top: 0, - }, -}); - -module.exports = NavigationHeader; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js deleted file mode 100644 index f3e901f3fd20b7..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule NavigationHeaderBackButton - * @flow -*/ -'use strict'; - -const React = require('react'); -const ReactNative = require('react-native'); - -const { - I18nManager, - Image, - Platform, - StyleSheet, - TouchableOpacity, -} = ReactNative; - -type Props = { - imageStyle?: any, - onPress: Function, - style?: any, -}; - -const NavigationHeaderBackButton = (props: Props) => ( - - - -); - -NavigationHeaderBackButton.propTypes = { - onPress: React.PropTypes.func.isRequired -}; - -const styles = StyleSheet.create({ - buttonContainer: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - }, - button: { - height: 24, - width: 24, - margin: Platform.OS === 'ios' ? 10 : 16, - resizeMode: 'contain', - transform: [{scaleX: I18nManager.isRTL ? -1 : 1}], - } -}); - -module.exports = NavigationHeaderBackButton; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderStyleInterpolator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderStyleInterpolator.js deleted file mode 100644 index 38a8689c069cfa..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderStyleInterpolator.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationHeaderStyleInterpolator - * @flow - */ -'use strict'; - -const I18nManager = require('I18nManager'); - -import type { - NavigationSceneRendererProps, -} from 'NavigationTypeDefinition'; - -/** - * Utility that builds the style for the navigation header. - * - * +-------------+-------------+-------------+ - * | | | | - * | Left | Title | Right | - * | Component | Component | Component | - * | | | | - * +-------------+-------------+-------------+ - */ - -function forLeft(props: NavigationSceneRendererProps): Object { - const {position, scene} = props; - const {index} = scene; - return { - opacity: position.interpolate({ - inputRange: [ index - 1, index, index + 1 ], - outputRange: ([ 0, 1, 0 ]: Array), - }), - }; -} - -function forCenter(props: NavigationSceneRendererProps): Object { - const {position, scene} = props; - const {index} = scene; - return { - opacity:position.interpolate({ - inputRange: [ index - 1, index, index + 1 ], - outputRange: ([ 0, 1, 0 ]: Array), - }), - transform: [ - { - translateX: position.interpolate({ - inputRange: [ index - 1, index + 1 ], - outputRange: I18nManager.isRTL ? - ([ -200, 200 ]: Array) : - ([ 200, -200 ]: Array), - }), - } - ], - }; -} - -function forRight(props: NavigationSceneRendererProps): Object { - const {position, scene} = props; - const {index} = scene; - return { - opacity: position.interpolate({ - inputRange: [ index - 1, index, index + 1 ], - outputRange: ([ 0, 1, 0 ]: Array), - }), - }; -} - -module.exports = { - forCenter, - forLeft, - forRight, -}; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderTitle.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderTitle.js deleted file mode 100644 index 61d39d4608f7ac..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderTitle.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationHeaderTitle - * @flow - */ -'use strict'; - -const React = require('react'); -const ReactNative = require('react-native'); - -const { - Platform, - StyleSheet, - View, - Text, -} = ReactNative; - -type Props = { - children?: React.Element, - style?: any, - textStyle?: any, - viewProps?: any, -} - -const NavigationHeaderTitle = ({ children, style, textStyle, viewProps }: Props) => ( - - {children} - -); - -const styles = StyleSheet.create({ - title: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - marginHorizontal: 16 - }, - - titleText: { - flex: 1, - fontSize: 18, - fontWeight: '500', - color: 'rgba(0, 0, 0, .9)', - textAlign: Platform.OS === 'ios' ? 'center' : 'left' - } -}); - -NavigationHeaderTitle.propTypes = { - children: React.PropTypes.node.isRequired, - style: View.propTypes.style, - textStyle: Text.propTypes.style -}; - -module.exports = NavigationHeaderTitle; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js deleted file mode 100644 index 1cc071ed633759..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerPanResponder.js +++ /dev/null @@ -1,238 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule NavigationPagerPanResponder - * @flow - */ -'use strict'; - -const Animated = require('Animated'); -const NavigationAbstractPanResponder = require('NavigationAbstractPanResponder'); -const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder'); -const I18nManager = require('I18nManager'); - -const clamp = require('clamp'); - -import type { - NavigationPanPanHandlers, - NavigationSceneRendererProps, -} from 'NavigationTypeDefinition'; - -import type { - NavigationGestureDirection, -} from 'NavigationCardStackPanResponder'; - -type Props = NavigationSceneRendererProps & { - onNavigateBack: ?Function, - onNavigateForward: ?Function, -}; - -/** - * Primitive gesture directions. - */ -const { - ANIMATION_DURATION, - POSITION_THRESHOLD, - RESPOND_THRESHOLD, - Directions, -} = NavigationCardStackPanResponder; - -/** - * The threshold (in pixels) to finish the gesture action. - */ -const DISTANCE_THRESHOLD = 50; - -/** - * The threshold to trigger the gesture action. This determines the rate of the - * flick when the action will be triggered - */ -const VELOCITY_THRESHOLD = 1.5; - -/** - * Pan responder that handles gesture for a card in the cards list. - * - * +-------------+-------------+-------------+ - * | | | | - * | | | | - * | | | | - * | Next | Focused | Previous | - * | Card | Card | Card | - * | | | | - * | | | | - * | | | | - * +-------------+-------------+-------------+ - */ -class NavigationPagerPanResponder extends NavigationAbstractPanResponder { - - _isResponding: boolean; - _isVertical: boolean; - _props: Props; - _startValue: number; - - constructor( - direction: NavigationGestureDirection, - props: Props, - ) { - super(); - this._isResponding = false; - this._isVertical = direction === Directions.VERTICAL; - this._props = props; - this._startValue = 0; - } - - onMoveShouldSetPanResponder(event: any, gesture: any): boolean { - const props = this._props; - - if (props.navigationState.index !== props.scene.index) { - return false; - } - - const layout = props.layout; - const isVertical = this._isVertical; - const axis = isVertical ? 'dy' : 'dx'; - const index = props.navigationState.index; - const distance = isVertical ? - layout.height.__getValue() : - layout.width.__getValue(); - - return ( - Math.abs(gesture[axis]) > RESPOND_THRESHOLD && - distance > 0 && - index >= 0 - ); - } - - onPanResponderGrant(): void { - this._isResponding = false; - this._props.position.stopAnimation((value: number) => { - this._isResponding = true; - this._startValue = value; - }); - } - - onPanResponderMove(event: any, gesture: any): void { - if (!this._isResponding) { - return; - } - - const { - layout, - navigationState, - position, - scenes, - } = this._props; - - const isVertical = this._isVertical; - const axis = isVertical ? 'dy' : 'dx'; - const index = navigationState.index; - const distance = isVertical ? - layout.height.__getValue() : - layout.width.__getValue(); - const currentValue = I18nManager.isRTL && axis === 'dx' ? - this._startValue + (gesture[axis] / distance) : - this._startValue - (gesture[axis] / distance); - - const prevIndex = Math.max( - 0, - index - 1, - ); - - const nextIndex = Math.min( - index + 1, - scenes.length - 1, - ); - - const value = clamp( - prevIndex, - currentValue, - nextIndex, - ); - - position.setValue(value); - } - - onPanResponderRelease(event: any, gesture: any): void { - if (!this._isResponding) { - return; - } - - this._isResponding = false; - - const { - navigationState, - onNavigateBack, - onNavigateForward, - position, - } = this._props; - - const isVertical = this._isVertical; - const axis = isVertical ? 'dy' : 'dx'; - const velocityAxis = isVertical ? 'vy' : 'vx'; - const index = navigationState.index; - const distance = I18nManager.isRTL && axis === 'dx' ? - -gesture[axis] : - gesture[axis]; - const moveSpeed = I18nManager.isRTL && velocityAxis === 'vx' ? - -gesture[velocityAxis] : - gesture[velocityAxis]; - - position.stopAnimation((value: number) => { - this._reset(); - if ( - distance > DISTANCE_THRESHOLD || - value <= index - POSITION_THRESHOLD || - moveSpeed > VELOCITY_THRESHOLD - ) { - onNavigateBack && onNavigateBack(); - return; - } - - if ( - distance < -DISTANCE_THRESHOLD || - value >= index + POSITION_THRESHOLD || - moveSpeed < -VELOCITY_THRESHOLD - ) { - onNavigateForward && onNavigateForward(); - } - }); - } - - onPanResponderTerminate(): void { - this._isResponding = false; - this._reset(); - } - - _reset(): void { - const props = this._props; - Animated.timing( - props.position, - { - toValue: props.navigationState.index, - duration: ANIMATION_DURATION, - } - ).start(); - } -} - -function createPanHandlers( - direction: NavigationGestureDirection, - props: Props, -): NavigationPanPanHandlers { - const responder = new NavigationPagerPanResponder(direction, props); - return responder.panHandlers; -} - -function forHorizontal( - props: Props, -): NavigationPanPanHandlers { - return createPanHandlers(Directions.HORIZONTAL, props); -} - -module.exports = { - forHorizontal, -}; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js deleted file mode 100644 index 950aa5eb136a1c..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationPagerStyleInterpolator.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationPagerStyleInterpolator - * @flow - */ -'use strict'; - -const I18nManager = require('I18nManager'); - -import type { - NavigationSceneRendererProps, -} from 'NavigationTypeDefinition'; - -/** - * Utility that builds the style for the card in the cards list. - * - * +-------------+-------------+-------------+ - * | | | | - * | | | | - * | | | | - * | Next | Focused | Previous | - * | Card | Card | Card | - * | | | | - * | | | | - * | | | | - * +-------------+-------------+-------------+ - */ - -/** - * Render the initial style when the initial layout isn't measured yet. - */ -function forInitial(props: NavigationSceneRendererProps): Object { - const { - navigationState, - scene, - } = props; - - const focused = navigationState.index === scene.index; - const opacity = focused ? 1 : 0; - // If not focused, move the scene to the far away. - const dir = scene.index > navigationState.index ? 1 : -1; - const translate = focused ? 0 : (1000000 * dir); - return { - opacity, - transform: [ - { translateX: translate }, - { translateY: translate }, - ], - }; -} - -function forHorizontal(props: NavigationSceneRendererProps): Object { - const { - layout, - position, - scene, - } = props; - - if (!layout.isMeasured) { - return forInitial(props); - } - - const index = scene.index; - const inputRange = [index - 1, index, index + 1]; - const width = layout.initWidth; - const outputRange = I18nManager.isRTL ? - ([-width, 0, width]: Array) : - ([width, 0, -width]: Array); - - const translateX = position.interpolate({ - inputRange, - outputRange, - }); - - return { - opacity : 1, - shadowColor: 'transparent', - shadowRadius: 0, - transform: [ - { scale: 1 }, - { translateX }, - { translateY: 0 }, - ], - }; -} - -module.exports = { - forHorizontal, -}; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationPointerEventsContainer.js b/Libraries/CustomComponents/NavigationExperimental/NavigationPointerEventsContainer.js deleted file mode 100644 index a6ef36fc258e63..00000000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationPointerEventsContainer.js +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationPointerEventsContainer - * @flow - */ -'use strict'; - -const React = require('React'); -const NavigationAnimatedValueSubscription = require('NavigationAnimatedValueSubscription'); - -const invariant = require('fbjs/lib/invariant'); - -import type { - NavigationSceneRendererProps, -} from 'NavigationTypeDefinition'; - -type Props = NavigationSceneRendererProps; - -const MIN_POSITION_OFFSET = 0.01; - -/** - * Create a higher-order component that automatically computes the - * `pointerEvents` property for a component whenever navigation position - * changes. - */ -function create( - Component: ReactClass, -): ReactClass { - - class Container extends React.Component { - - _component: any; - _onComponentRef: (view: any) => void; - _onPositionChange: (data: {value: number}) => void; - _pointerEvents: string; - _positionListener: ?NavigationAnimatedValueSubscription; - - props: Props; - - constructor(props: Props, context: any) { - super(props, context); - this._pointerEvents = this._computePointerEvents(); - } - - componentWillMount(): void { - this._onPositionChange = this._onPositionChange.bind(this); - this._onComponentRef = this._onComponentRef.bind(this); - } - - componentDidMount(): void { - this._bindPosition(this.props); - } - - componentWillUnmount(): void { - this._positionListener && this._positionListener.remove(); - } - - componentWillReceiveProps(nextProps: Props): void { - this._bindPosition(nextProps); - } - - render(): React.Element { - this._pointerEvents = this._computePointerEvents(); - return ( - - ); - } - - _onComponentRef(component: any): void { - this._component = component; - if (component) { - invariant( - typeof component.setNativeProps === 'function', - 'component must implement method `setNativeProps`', - ); - } - } - - _bindPosition(props: NavigationSceneRendererProps): void { - this._positionListener && this._positionListener.remove(); - this._positionListener = new NavigationAnimatedValueSubscription( - props.position, - this._onPositionChange, - ); - } - - _onPositionChange(): void { - if (this._component) { - const pointerEvents = this._computePointerEvents(); - if (this._pointerEvents !== pointerEvents) { - this._pointerEvents = pointerEvents; - this._component.setNativeProps({pointerEvents}); - } - } - } - - _computePointerEvents(): string { - const { - navigationState, - position, - scene, - } = this.props; - - if (scene.isStale || navigationState.index !== scene.index) { - // The scene isn't focused. - return scene.index > navigationState.index ? - 'box-only' : - 'none'; - } - - const offset = position.__getAnimatedValue() - navigationState.index; - if (Math.abs(offset) > MIN_POSITION_OFFSET) { - // The positon is still away from scene's index. - // Scene's children should not receive touches until the position - // is close enough to scene's index. - return 'box-only'; - } - - return 'auto'; - } - } - return Container; -} - -module.exports = { - create, -}; diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.android.png deleted file mode 100644 index ad03a63bf3caba..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.android.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.ios.png deleted file mode 100644 index e43fa0622f1994..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.ios.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.android.png deleted file mode 100644 index 083db295f474b9..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.android.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.ios.png deleted file mode 100644 index 4244656b92fb70..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.ios.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.android.png deleted file mode 100644 index 6de0a1cbb365df..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.android.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.ios.png deleted file mode 100644 index a8b6e9a63e5954..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.ios.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.android.png deleted file mode 100644 index 15a983a67d97c9..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.android.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.ios.png deleted file mode 100644 index 07ea37b4e1e252..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.ios.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.android.png deleted file mode 100644 index 17e52e8550e566..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.android.png and /dev/null differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.ios.png deleted file mode 100644 index 7899053f61b38b..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.ios.png and /dev/null differ diff --git a/Libraries/CustomComponents/Navigator/Navigation/NavigationContext.js b/Libraries/CustomComponents/Navigator/Navigation/NavigationContext.js deleted file mode 100644 index 406e8be2fc6368..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/NavigationContext.js +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationContext - * @noflow - */ -'use strict'; - -var NavigationEvent = require('NavigationEvent'); -var NavigationEventEmitter = require('NavigationEventEmitter'); -var NavigationTreeNode = require('NavigationTreeNode'); - -var Set = require('Set'); - -var emptyFunction = require('fbjs/lib/emptyFunction'); -var invariant = require('fbjs/lib/invariant'); - -import type EventSubscription from 'EventSubscription'; - -var { - AT_TARGET, - BUBBLING_PHASE, - CAPTURING_PHASE, -} = NavigationEvent; - -// Event types that do not support event bubbling, capturing and -// reconciliation API (e.g event.preventDefault(), event.stopPropagation()). -var LegacyEventTypes = new Set([ - 'willfocus', - 'didfocus', -]); - -/** - * Class that contains the info and methods for app navigation. - */ -class NavigationContext { - __node: NavigationTreeNode; - _bubbleEventEmitter: ?NavigationEventEmitter; - _captureEventEmitter: ?NavigationEventEmitter; - _currentRoute: any; - _emitCounter: number; - _emitQueue: Array; - - constructor() { - this._bubbleEventEmitter = new NavigationEventEmitter(this); - this._captureEventEmitter = new NavigationEventEmitter(this); - this._currentRoute = null; - - // Sets the protected property `__node`. - this.__node = new NavigationTreeNode(this); - - this._emitCounter = 0; - this._emitQueue = []; - - this.addListener('willfocus', this._onFocus); - this.addListener('didfocus', this._onFocus); - } - - /* $FlowFixMe - get/set properties not yet supported */ - get parent(): ?NavigationContext { - var parent = this.__node.getParent(); - return parent ? parent.getValue() : null; - } - - /* $FlowFixMe - get/set properties not yet supported */ - get top(): ?NavigationContext { - var result = null; - var parentNode = this.__node.getParent(); - while (parentNode) { - result = parentNode.getValue(); - parentNode = parentNode.getParent(); - } - return result; - } - - /* $FlowFixMe - get/set properties not yet supported */ - get currentRoute(): any { - return this._currentRoute; - } - - appendChild(childContext: NavigationContext): void { - this.__node.appendChild(childContext.__node); - } - - addListener( - eventType: string, - listener: Function, - useCapture: ?boolean - ): EventSubscription { - if (LegacyEventTypes.has(eventType)) { - useCapture = false; - } - - var emitter = useCapture ? - this._captureEventEmitter : - this._bubbleEventEmitter; - - if (emitter) { - return emitter.addListener(eventType, listener, this); - } else { - return {remove: emptyFunction}; - } - } - - emit(eventType: String, data: any, didEmitCallback: ?Function): void { - if (this._emitCounter > 0) { - // An event cycle that was previously created hasn't finished yet. - // Put this event cycle into the queue and will finish them later. - var args: any = Array.prototype.slice.call(arguments); - this._emitQueue.push(args); - return; - } - - this._emitCounter++; - - if (LegacyEventTypes.has(eventType)) { - // Legacy events does not support event bubbling and reconciliation. - this.__emit( - eventType, - data, - null, - { - defaultPrevented: false, - eventPhase: AT_TARGET, - propagationStopped: true, - target: this, - } - ); - } else { - var targets = [this]; - var parentTarget = this.parent; - while (parentTarget) { - targets.unshift(parentTarget); - parentTarget = parentTarget.parent; - } - - var propagationStopped = false; - var defaultPrevented = false; - var callback = (event) => { - propagationStopped = propagationStopped || event.isPropagationStopped(); - defaultPrevented = defaultPrevented || event.defaultPrevented; - }; - - // Capture phase - targets.some((currentTarget) => { - if (propagationStopped) { - return true; - } - - var extraInfo = { - defaultPrevented, - eventPhase: CAPTURING_PHASE, - propagationStopped, - target: this, - }; - - currentTarget.__emit(eventType, data, callback, extraInfo); - }, this); - - // bubble phase - targets.reverse().some((currentTarget) => { - if (propagationStopped) { - return true; - } - var extraInfo = { - defaultPrevented, - eventPhase: BUBBLING_PHASE, - propagationStopped, - target: this, - }; - currentTarget.__emit(eventType, data, callback, extraInfo); - }, this); - } - - if (didEmitCallback) { - var event = NavigationEvent.pool(eventType, this, data); - propagationStopped && event.stopPropagation(); - defaultPrevented && event.preventDefault(); - didEmitCallback.call(this, event); - event.dispose(); - } - - this._emitCounter--; - while (this._emitQueue.length) { - var args: any = this._emitQueue.shift(); - this.emit.apply(this, args); - } - } - - dispose(): void { - // clean up everything. - this._bubbleEventEmitter && this._bubbleEventEmitter.removeAllListeners(); - this._captureEventEmitter && this._captureEventEmitter.removeAllListeners(); - this._bubbleEventEmitter = null; - this._captureEventEmitter = null; - this._currentRoute = null; - } - - // This method `__method` is protected. - __emit( - eventType: String, - data: any, - didEmitCallback: ?Function, - extraInfo: Object, - ): void { - var emitter; - switch (extraInfo.eventPhase) { - case CAPTURING_PHASE: // phase = 1 - emitter = this._captureEventEmitter; - break; - - case AT_TARGET: // phase = 2 - emitter = this._bubbleEventEmitter; - break; - - case BUBBLING_PHASE: // phase = 3 - emitter = this._bubbleEventEmitter; - break; - - default: - invariant(false, 'invalid event phase %s', extraInfo.eventPhase); - } - - if (extraInfo.target === this) { - // phase = 2 - extraInfo.eventPhase = AT_TARGET; - } - - if (emitter) { - emitter.emit( - eventType, - data, - didEmitCallback, - extraInfo - ); - } - } - - _onFocus(event: NavigationEvent): void { - invariant( - event.data && event.data.hasOwnProperty('route'), - 'event type "%s" should provide route', - event.type - ); - - this._currentRoute = event.data.route; - } -} - -module.exports = NavigationContext; diff --git a/Libraries/CustomComponents/Navigator/Navigation/NavigationEvent.js b/Libraries/CustomComponents/Navigator/Navigation/NavigationEvent.js deleted file mode 100644 index 797097cac1b8d4..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/NavigationEvent.js +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationEvent - * @flow - */ -'use strict'; - -const invariant = require('fbjs/lib/invariant'); - -class NavigationEventPool { - _list: Array; - - constructor() { - this._list = []; - } - - get(type: string, currentTarget: Object, data: any): NavigationEvent { - let event; - if (this._list.length > 0) { - event = this._list.pop(); - event.constructor.call(event, type, currentTarget, data); - } else { - event = new NavigationEvent(type, currentTarget, data); - } - return event; - } - - put(event: NavigationEvent) { - this._list.push(event); - } -} - -const _navigationEventPool = new NavigationEventPool(); - -/** - * The NavigationEvent interface represents any event of the navigation. - * It contains common properties and methods to any event. - * - * == Important Properties == - * - * - target: - * A reference to the navigation context that dispatched the event. It is - * different from event.currentTarget when the event handler is called during - * the bubbling or capturing phase of the event. - * - * - currentTarget: - * Identifies the current target for the event, as the event traverses the - * navigation context tree. It always refers to the navigation context the - * event handler has been attached to as opposed to event.target which - * identifies the navigation context on which the event occurred. - * - * - eventPhase: - * Returns an integer value which specifies the current evaluation phase of - * the event flow; possible values are listed in NavigationEvent phase - * constants below. - */ -class NavigationEvent { - static AT_TARGET: number; - static BUBBLING_PHASE: number; - static CAPTURING_PHASE: number; - static NONE: number; - - _currentTarget: ?Object; - _data: any; - _defaultPrevented: boolean; - _disposed: boolean; - _propagationStopped: boolean; - _type: string; - - target: ?Object; - - // Returns an integer value which specifies the current evaluation phase of - // the event flow. - eventPhase: number; - - static pool(type: string, currentTarget: Object, data: any): NavigationEvent { - return _navigationEventPool.get(type, currentTarget, data); - } - - constructor(type: string, currentTarget: Object, data: any) { - this.target = currentTarget; - this.eventPhase = NavigationEvent.NONE; - - this._type = type; - this._currentTarget = currentTarget; - this._data = data; - this._defaultPrevented = false; - this._disposed = false; - this._propagationStopped = false; - } - - get type(): string { - return this._type; - } - - get currentTarget(): ?Object { - return this._currentTarget; - } - - get data(): any { - return this._data; - } - - get defaultPrevented(): boolean { - return this._defaultPrevented; - } - - preventDefault(): void { - this._defaultPrevented = true; - } - - stopPropagation(): void { - this._propagationStopped = true; - } - - stop(): void { - this.preventDefault(); - this.stopPropagation(); - } - - isPropagationStopped(): boolean { - return this._propagationStopped; - } - - /** - * Dispose the event. - * NavigationEvent shall be disposed after being emitted by - * `NavigationEventEmitter`. - */ - dispose(): void { - invariant(!this._disposed, 'NavigationEvent is already disposed'); - this._disposed = true; - - // Clean up. - this.target = null; - this.eventPhase = NavigationEvent.NONE; - this._type = ''; - this._currentTarget = null; - this._data = null; - this._defaultPrevented = false; - - // Put this back to the pool to reuse the instance. - _navigationEventPool.put(this); - } -} - -/** - * Event phase constants. - * These values describe which phase the event flow is currently being - * evaluated. - */ - -// No event is being processed at this time. -NavigationEvent.NONE = 0; - -// The event is being propagated through the currentTarget's ancestor objects. -NavigationEvent.CAPTURING_PHASE = 1; - -// The event has arrived at the event's currentTarget. Event listeners registered for -// this phase are called at this time. -NavigationEvent.AT_TARGET = 2; - -// The event is propagating back up through the currentTarget's ancestors in reverse -// order, starting with the parent. This is known as bubbling, and occurs only -// if event propagation isn't prevented. Event listeners registered for this -// phase are triggered during this process. -NavigationEvent.BUBBLING_PHASE = 3; - -module.exports = NavigationEvent; diff --git a/Libraries/CustomComponents/Navigator/Navigation/NavigationEventEmitter.js b/Libraries/CustomComponents/Navigator/Navigation/NavigationEventEmitter.js deleted file mode 100644 index 339c957340389f..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/NavigationEventEmitter.js +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationEventEmitter - * @flow - */ -'use strict'; - -var EventEmitter = require('EventEmitter'); -var NavigationEvent = require('NavigationEvent'); - -type ExtraInfo = { - defaultPrevented: ?boolean, - eventPhase: ?number, - propagationStopped: ?boolean, - target: ?Object, -}; - -class NavigationEventEmitter extends EventEmitter { - _emitQueue: Array; - _emitting: boolean; - _target: Object; - - constructor(target: Object) { - super(); - this._emitting = false; - this._emitQueue = []; - this._target = target; - } - - emit( - eventType: string, - data: any, - didEmitCallback: ?Function, - extraInfo: ?ExtraInfo - ): void { - if (this._emitting) { - // An event cycle that was previously created hasn't finished yet. - // Put this event cycle into the queue and will finish them later. - var args: any = Array.prototype.slice.call(arguments); - this._emitQueue.push(args); - return; - } - - this._emitting = true; - - var event = NavigationEvent.pool(eventType, this._target, data); - - if (extraInfo) { - if (extraInfo.target) { - event.target = extraInfo.target; - } - - if (extraInfo.eventPhase) { - event.eventPhase = extraInfo.eventPhase; - } - - if (extraInfo.defaultPrevented) { - event.preventDefault(); - } - - if (extraInfo.propagationStopped) { - event.stopPropagation(); - } - } - - // EventEmitter#emit only takes `eventType` as `String`. Casting `eventType` - // to `String` to make @flow happy. - super.emit(String(eventType), event); - - if (typeof didEmitCallback === 'function') { - didEmitCallback.call(this._target, event); - } - event.dispose(); - - this._emitting = false; - - while (this._emitQueue.length) { - var args: any = this._emitQueue.shift(); - this.emit.apply(this, args); - } - } -} - -module.exports = NavigationEventEmitter; diff --git a/Libraries/CustomComponents/Navigator/Navigation/NavigationRouteStack.js b/Libraries/CustomComponents/Navigator/Navigation/NavigationRouteStack.js deleted file mode 100644 index fe40a609110e46..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/NavigationRouteStack.js +++ /dev/null @@ -1,283 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationRouteStack - * @flow - */ -'use strict'; - -var immutable = require('immutable'); -var invariant = require('fbjs/lib/invariant'); - -type IterationCallback = (route: any, index: number, key: string) => void; - -var {List, Set} = immutable; - -function isRouteEmpty(route: any): boolean { - return (route === undefined || route === null || route === '') || false; -} - -var _nextID = 0; - -class RouteNode { - key: string; - value: any; - constructor(route: any) { - // Key value gets bigger incrementally. Developer can compare the - // keys of two routes then know which route is added to the stack - // earlier. - this.key = String(_nextID++); - - this.value = route; - } -} - -var StackDiffRecord = immutable.Record({ - key: null, - route: null, - index: null, -}); - -/** - * The immutable route stack. - */ -class RouteStack { - _index: number; - - _routeNodes: List; - - constructor(index: number, routeNodes: List) { - invariant( - routeNodes.size > 0, - 'size must not be empty' - ); - - invariant( - index > -1 && index <= routeNodes.size - 1, - 'index out of bound' - ); - - this._routeNodes = routeNodes; - this._index = index; - } - - get size(): number { - return this._routeNodes.size; - } - - get index(): number { - return this._index; - } - - toArray(): Array { - var result = []; - var ii = 0; - var nodes = this._routeNodes; - while (ii < nodes.size) { - result.push(nodes.get(ii).value); - ii++; - } - return result; - } - - get(index: number): any { - if (index < 0 || index > this._routeNodes.size - 1) { - return null; - } - return this._routeNodes.get(index).value; - } - - /** - * Returns the key associated with the route. - * When a route is added to a stack, the stack creates a key for this route. - * The key will persist until the initial stack and its derived stack - * no longer contains this route. - */ - keyOf(route: any): ?string { - if (isRouteEmpty(route)) { - return null; - } - var index = this.indexOf(route); - return index > -1 ? - this._routeNodes.get(index).key : - null; - } - - indexOf(route: any): number { - if (isRouteEmpty(route)) { - return -1; - } - - var finder = (node) => { - return (node: RouteNode).value === route; - }; - - return this._routeNodes.findIndex(finder, this); - } - - slice(begin?: number, end?: number): RouteStack { - var routeNodes = this._routeNodes.slice(begin, end); - var index = Math.min(this._index, routeNodes.size - 1); - return this._update(index, routeNodes); - } - - /** - * Returns a new stack with the provided route appended, - * starting at this stack size. - */ - push(route: any): RouteStack { - - invariant( - !isRouteEmpty(route), - 'Must supply route to push' - ); - - invariant(this._routeNodes.indexOf(route) === -1, 'route must be unique'); - - // When pushing, removes the rest of the routes past the current index. - var routeNodes = this._routeNodes.withMutations((list: List) => { - list.slice(0, this._index + 1).push(new RouteNode(route)); - }); - - return this._update(routeNodes.size - 1, routeNodes); - } - - /** - * Returns a new stack a size ones less than this stack, - * excluding the last index in this stack. - */ - pop(): RouteStack { - invariant(this._routeNodes.size > 1, 'should not pop routeNodes stack to empty'); - - // When popping, removes the rest of the routes past the current index. - var routeNodes = this._routeNodes.slice(0, this._index); - return this._update(routeNodes.size - 1, routeNodes); - } - - jumpToIndex(index: number): RouteStack { - invariant( - index > -1 && index < this._routeNodes.size, - 'index out of bound' - ); - - return this._update(index, this._routeNodes); - } - - /** - * Replace a route in the navigation stack. - * - * `index` specifies the route in the stack that should be replaced. - * If it's negative, it counts from the back. - */ - replaceAtIndex(index: number, route: any): RouteStack { - invariant( - !isRouteEmpty(route), - 'Must supply route to replace' - ); - - if (this.get(index) === route) { - return this; - } - - invariant(this.indexOf(route) === -1, 'route must be unique'); - - if (index < 0) { - index += this._routeNodes.size; - } - - invariant( - index > -1 && index < this._routeNodes.size, - 'index out of bound' - ); - - var routeNodes = this._routeNodes.set(index, new RouteNode(route)); - return this._update(index, routeNodes); - } - - // Iterations - forEach(callback: IterationCallback, context: ?Object): void { - var ii = 0; - var nodes = this._routeNodes; - while (ii < nodes.size) { - var node = nodes.get(ii); - callback.call(context, node.value, ii, node.key); - ii++; - } - } - - mapToArray(callback: IterationCallback, context: ?Object): Array { - var result = []; - this.forEach((route, index, key) => { - result.push(callback.call(context, route, index, key)); - }); - return result; - } - - /** - * Returns a Set excluding any routes contained within the stack given. - */ - subtract(stack: RouteStack): Set { - var items = []; - this._routeNodes.forEach((node: RouteNode, index: number) => { - if (!stack._routeNodes.contains(node)) { - items.push( - new StackDiffRecord({ - route: node.value, - index: index, - key: node.key, - }) - ); - } - }); - return new Set(items); - } - - _update(index: number, routeNodes: List): RouteStack { - if (this._index === index && this._routeNodes === routeNodes) { - return this; - } - return new RouteStack(index, routeNodes); - } -} - -/** - * The first class data structure for NavigationContext to manage the navigation - * stack of routes. - */ -class NavigationRouteStack extends RouteStack { - constructor(index: number, routeNodes: Array) { - // For now, `RouteStack` internally, uses an immutable `List` to keep - // track of routeNodes. Since using `List` is really just the implementation - // detail, we don't want to accept `routeNodes` as `list` from constructor - // for developer. - var nodes = routeNodes.map((route) => { - invariant(!isRouteEmpty(route), 'route must not be mepty'); - return new RouteNode(route); - }); - - super(index, new List(nodes)); - } -} - -module.exports = NavigationRouteStack; diff --git a/Libraries/CustomComponents/Navigator/Navigation/NavigationTreeNode.js b/Libraries/CustomComponents/Navigator/Navigation/NavigationTreeNode.js deleted file mode 100644 index d188c366f74596..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/NavigationTreeNode.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigationTreeNode - * @flow - */ -'use strict'; - -var invariant = require('fbjs/lib/invariant'); -var immutable = require('immutable'); - -var {List} = immutable; - -/** - * Utility to build a tree of nodes. - * Note that this tree does not perform cyclic redundancy check - * while appending child node. - */ -class NavigationTreeNode { - __parent: ?NavigationTreeNode; - - _children: List; - - _value: any; - - constructor(value: any) { - this.__parent = null; - this._children = new List(); - this._value = value; - } - - getValue(): any { - return this._value; - } - - getParent(): ?NavigationTreeNode { - return this.__parent; - } - - getChildrenCount(): number { - return this._children.size; - } - - getChildAt(index: number): ?NavigationTreeNode { - return index > -1 && index < this._children.size ? - this._children.get(index) : - null; - } - - appendChild(child: NavigationTreeNode): void { - if (child.__parent) { - child.__parent.removeChild(child); - } - child.__parent = this; - this._children = this._children.push(child); - } - - removeChild(child: NavigationTreeNode): void { - var index = this._children.indexOf(child); - - invariant( - index > -1, - 'The node to be removed is not a child of this node.' - ); - - child.__parent = null; - - this._children = this._children.splice(index, 1); - } - - indexOf(child: NavigationTreeNode): number { - return this._children.indexOf(child); - } - - forEach(callback: Function, context: any): void { - this._children.forEach(callback, context); - } - - map(callback: Function, context: any): Array { - return this._children.map(callback, context).toJS(); - } - - some(callback: Function, context: any): boolean { - return this._children.some(callback, context); - } -} - - -module.exports = NavigationTreeNode; diff --git a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationContext-test.js b/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationContext-test.js deleted file mode 100644 index 9f867e704356a4..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationContext-test.js +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -'use strict'; - -jest - .disableAutomock() - .mock('ErrorUtils'); - -var NavigationContext = require('NavigationContext'); -var NavigationEvent = require('NavigationEvent'); - -describe('NavigationContext', () => { - it('defaults `currentRoute` to null', () => { - var context = new NavigationContext(); - expect(context.currentRoute).toEqual(null); - }); - - it('updates `currentRoute`', () => { - var context = new NavigationContext(); - context.emit('didfocus', {route: {name: 'a'}}); - expect(context.currentRoute.name).toEqual('a'); - }); - - it('has parent', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - expect(child.parent).toBe(parent); - }); - - it('has `top`', () => { - var top = new NavigationContext(); - var parent = new NavigationContext(); - var child = new NavigationContext(); - top.appendChild(parent); - parent.appendChild(child); - expect(child.top).toBe(top); - }); - - it('captures event', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - - var logs = []; - - var listener = (event) => { - var {currentTarget, eventPhase, target, type} = event; - logs.push({ - currentTarget, - eventPhase, - target, - type, - }); - }; - - parent.addListener('yo', listener, true); - child.addListener('yo', listener, true); - - child.emit('yo'); - - expect(logs).toEqual([ - { - currentTarget: parent, - eventPhase: NavigationEvent.CAPTURING_PHASE, - target: child, - type: 'yo', - }, - { - currentTarget: child, - eventPhase: NavigationEvent.AT_TARGET, - target: child, - type: 'yo', - } - ]); - }); - - it('bubbles events', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - - var logs = []; - - var listener = (event) => { - var {currentTarget, eventPhase, target, type} = event; - logs.push({ - currentTarget, - eventPhase, - target, - type, - }); - }; - - parent.addListener('yo', listener); - child.addListener('yo', listener); - - child.emit('yo'); - - expect(logs).toEqual([ - { - currentTarget: child, - eventPhase: NavigationEvent.AT_TARGET, - target: child, - type: 'yo', - }, - { - currentTarget: parent, - eventPhase: NavigationEvent.BUBBLING_PHASE, - target: child, - type: 'yo', - }, - ]); - }); - - it('stops event propagation at capture phase', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - - var counter = 0; - - parent.addListener('yo', event => event.stopPropagation(), true); - child.addListener('yo', event => counter++, true); - - child.emit('yo'); - - expect(counter).toBe(0); - }); - - it('stops event propagation at bubbling phase', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - - var counter = 0; - - parent.addListener('yo', event => counter++); - child.addListener('yo', event => event.stopPropagation()); - - child.emit('yo'); - - expect(counter).toBe(0); - }); - - it('prevents event at capture phase', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - - var val; - parent.addListener('yo', event => event.preventDefault(), true); - child.addListener('yo', event => val = event.defaultPrevented, true); - - child.emit('yo'); - - expect(val).toBe(true); - }); - - it('prevents event at bubble phase', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - - var val; - parent.addListener('yo', event => val = event.defaultPrevented); - child.addListener('yo', event => event.preventDefault()); - - child.emit('yo'); - - expect(val).toBe(true); - }); - - it('emits nested events in order at capture phase', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - - var logs = []; - - var listener = (event) => { - var {currentTarget, type} = event; - logs.push({ - currentTarget, - type, - }); - }; - - child.addListener('yo', event => { - // event `didyo` should be fired after the full propagation cycle of the - // `yo` event. - child.emit('didyo'); - }); - - parent.addListener('yo', listener, true); - parent.addListener('didyo', listener, true); - child.addListener('yo', listener, true); - - child.emit('yo'); - - expect(logs).toEqual([ - {type: 'yo', currentTarget: parent}, - {type: 'yo', currentTarget: child}, - {type: 'didyo', currentTarget: parent}, - ]); - }); - - it('emits nested events in order at bubbling phase', () => { - var parent = new NavigationContext(); - var child = new NavigationContext(); - parent.appendChild(child); - - var logs = []; - - var listener = (event) => { - var {currentTarget, type} = event; - logs.push({ - currentTarget, - type, - }); - }; - - child.addListener('yo', event => { - // event `didyo` should be fired after the full propagation cycle of the - // `yo` event. - child.emit('didyo'); - }); - - parent.addListener('yo', listener); - child.addListener('yo', listener); - parent.addListener('didyo', listener); - - child.emit('yo'); - - expect(logs).toEqual([ - {type: 'yo', currentTarget: child}, - {type: 'yo', currentTarget: parent}, - {type: 'didyo', currentTarget: parent}, - ]); - }); -}); diff --git a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationEvent-test.js b/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationEvent-test.js deleted file mode 100644 index e9a54290dcd9f4..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationEvent-test.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -'use strict'; - -jest - .unmock('NavigationEvent') - .unmock('fbjs/lib/invariant'); - -var NavigationEvent = require('NavigationEvent'); - -describe('NavigationEvent', () => { - it('constructs', () => { - var target = {}; - var event = new NavigationEvent('foo', target, 123); - expect(event.type).toBe('foo'); - expect(event.target).toBe(target); - expect(event.data).toBe(123); - }); - - it('constructs from pool', () => { - var target = {}; - var event = NavigationEvent.pool('foo', target, 123); - expect(event.type).toBe('foo'); - expect(event.target).toBe(target); - expect(event.data).toBe(123); - }); - - it('prevents default', () => { - var event = new NavigationEvent('foo', {}, 123); - expect(event.defaultPrevented).toBe(false); - event.preventDefault(); - expect(event.defaultPrevented).toBe(true); - }); - - it('recycles', () => { - var event1 = NavigationEvent.pool('foo', {}, 123); - event1.dispose(); - expect(event1.type).toBeFalsy(); - expect(event1.data).toBe(null); - expect(event1.target).toBe(null); - - var event2 = NavigationEvent.pool('bar', {}, 456); - expect(event2.type).toBe('bar'); - expect(event2).toBe(event1); - }); -}); diff --git a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationEventEmitter-test.js b/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationEventEmitter-test.js deleted file mode 100644 index 6704b1f0236a4a..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationEventEmitter-test.js +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -'use strict'; - -jest - .unmock('EmitterSubscription') - .unmock('EventSubscription') - .unmock('EventEmitter') - .unmock('EventSubscriptionVendor') - .unmock('NavigationEvent') - .unmock('NavigationEventEmitter'); - -var NavigationEventEmitter = require('NavigationEventEmitter'); - -describe('NavigationEventEmitter', () => { - it('emits event', () => { - var context = {}; - var emitter = new NavigationEventEmitter(context); - var logs = []; - - emitter.addListener('ping', (event) => { - var {type, data, target, defaultPrevented} = event; - - logs.push({ - data, - defaultPrevented, - target, - type, - }); - - }); - - emitter.emit('ping', 'hello'); - - expect(logs.length).toBe(1); - expect(logs[0].target).toBe(context); - expect(logs[0].type).toBe('ping'); - expect(logs[0].data).toBe('hello'); - expect(logs[0].defaultPrevented).toBe(false); - }); - - it('does not emit event that has no listeners', () => { - var context = {}; - var emitter = new NavigationEventEmitter(context); - var pinged = false; - - emitter.addListener('ping', () => { - pinged = true; - }); - - emitter.emit('yo', 'bo'); - expect(pinged).toBe(false); - }); - - it('puts nested emit call in a queue', () => { - var context = {}; - var emitter = new NavigationEventEmitter(context); - var logs = []; - - emitter.addListener('one', () => { - logs.push(1); - emitter.emit('two'); - logs.push(2); - }); - - emitter.addListener('two', () => { - logs.push(3); - emitter.emit('three'); - logs.push(4); - }); - - emitter.addListener('three', () => { - logs.push(5); - }); - - emitter.emit('one'); - - expect(logs).toEqual([1, 2, 3, 4, 5]); - }); - - it('puts nested emit call in a queue should be in sequence order', () => { - var context = {}; - var emitter = new NavigationEventEmitter(context); - var logs = []; - - emitter.addListener('one', () => { - logs.push(1); - emitter.emit('two'); - emitter.emit('three'); - logs.push(2); - }); - - emitter.addListener('two', () => { - logs.push(3); - logs.push(4); - }); - - emitter.addListener('three', () => { - logs.push(5); - }); - - emitter.emit('one'); - - expect(logs).toEqual([1, 2, 3, 4, 5]); - }); - - it('calls callback after emitting', () => { - var context = {}; - var emitter = new NavigationEventEmitter(context); - var logs = []; - - emitter.addListener('ping', (event) => { - var {type, data, target, defaultPrevented} = event; - logs.push({ - data, - defaultPrevented, - target, - type, - }); - event.preventDefault(); - }); - - emitter.emit('ping', 'hello', (event) => { - var {type, data, target, defaultPrevented} = event; - logs.push({ - data, - defaultPrevented, - target, - type, - }); - }); - - expect(logs.length).toBe(2); - expect(logs[1].target).toBe(context); - expect(logs[1].type).toBe('ping'); - expect(logs[1].data).toBe('hello'); - expect(logs[1].defaultPrevented).toBe(true); - }); - - it('calls callback after emitting the current event and before ' + - 'emitting the next event', () => { - var context = {}; - var emitter = new NavigationEventEmitter(context); - var logs = []; - - emitter.addListener('ping', (event) => { - logs.push('ping'); - emitter.emit('pong'); - }); - - emitter.addListener('pong', (event) => { - logs.push('pong'); - }); - - emitter.emit('ping', null, () => { - logs.push('did-ping'); - }); - - expect(logs).toEqual([ - 'ping', - 'did-ping', - 'pong', - ]); - }); -}); diff --git a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationRouteStack-test.js b/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationRouteStack-test.js deleted file mode 100644 index a27e5502f68fb3..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationRouteStack-test.js +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -'use strict'; - - -jest - .disableAutomock() - .mock('ErrorUtils'); - -var NavigationRouteStack = require('NavigationRouteStack'); - -function assetStringNotEmpty(str) { - expect(!!str && typeof str === 'string').toBe(true); -} - -describe('NavigationRouteStack:', () => { - // Different types of routes. - var ROUTES = [ - 'foo', - 1, - true, - {foo: 'bar'}, - ['foo'], - ]; - - // Basic - it('gets index', () => { - var stack = new NavigationRouteStack(1, ['a', 'b', 'c']); - expect(stack.index).toBe(1); - }); - - it('gets size', () => { - var stack = new NavigationRouteStack(1, ['a', 'b', 'c']); - expect(stack.size).toBe(3); - }); - - it('gets route', () => { - var stack = new NavigationRouteStack(0, ['a', 'b', 'c']); - expect(stack.get(2)).toBe('c'); - }); - - it('converts to an array', () => { - var stack = new NavigationRouteStack(0, ['a', 'b']); - expect(stack.toArray()).toEqual(['a', 'b']); - }); - - it('creates a new stack after mutation', () => { - var stack1 = new NavigationRouteStack(0, ['a', 'b']); - var stack2 = stack1.push('c'); - expect(stack1).not.toBe(stack2); - }); - - it('throws at index out of bound', () => { - expect(() => { - new NavigationRouteStack(-1, ['a', 'b']); - }).toThrow(); - - expect(() => { - new NavigationRouteStack(100, ['a', 'b']); - }).toThrow(); - }); - - it('finds index', () => { - var stack = new NavigationRouteStack(0, ['a', 'b']); - expect(stack.indexOf('b')).toBe(1); - expect(stack.indexOf('c')).toBe(-1); - }); - - // Key - it('gets key for route', () => { - var test = (route) => { - var stack = new NavigationRouteStack(0, ['a']); - var key = stack.push(route).keyOf(route); - expect(typeof key).toBe('string'); - expect(!!key).toBe(true); - }; - - ROUTES.forEach(test); - }); - - it('gets a key of larger value for route', () => { - var lastKey = ''; - var test = (route) => { - var stack = new NavigationRouteStack(0, ['a']); - var key = stack.push(route).keyOf(route); - expect(key > lastKey).toBe(true); - lastKey = key; - }; - - ROUTES.forEach(test); - }); - - it('gets an unique key for a different route', () => { - var stack = new NavigationRouteStack(0, ['a']); - var keys = {}; - - var test = (route) => { - stack = stack.push(route); - var key = stack.keyOf(route); - expect(keys[key]).toBe(undefined); - keys[key] = true; - }; - - ROUTES.forEach(test); - }); - - it('gets the same unique key for the same route', () => { - var test = (route) => { - var stack = new NavigationRouteStack(0, [route]); - expect(stack.keyOf(route)).toBe(stack.keyOf(route)); - }; - - ROUTES.forEach(test); - }); - - - it('gets the same unique key form the derived stack', () => { - var test = (route) => { - var stack = new NavigationRouteStack(0, [route]); - var derivedStack = stack.push('wow').pop().slice(0, 10).push('blah'); - expect(derivedStack.keyOf(route)).toBe(stack.keyOf(route)); - }; - - ROUTES.forEach(test); - }); - - it('gets a different key from a different stack', () => { - var test = (route) => { - var stack1 = new NavigationRouteStack(0, [route]); - var stack2 = new NavigationRouteStack(0, [route]); - expect(stack1.keyOf(route)).not.toBe(stack2.keyOf(route)); - }; - - ROUTES.forEach(test); - }); - - it('gets no key for a route that does not contains this route', () => { - var stack = new NavigationRouteStack(0, ['a']); - expect(stack.keyOf('b')).toBe(null); - }); - - it('gets a new key for a route that was removed and added again', () => { - var test = (route) => { - var stack = new NavigationRouteStack(0, ['a']); - - var key1 = stack.push(route).keyOf(route); - var key2 = stack.push(route).pop().push(route).keyOf(route); - expect(key1).not.toBe(key2); - }; - - ROUTES.forEach(test); - }); - - // Slice - it('slices', () => { - var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c', 'd']); - var stack2 = stack1.slice(1, 3); - expect(stack2).not.toBe(stack1); - expect(stack2.toArray()).toEqual(['b', 'c']); - }); - - it('may update index after slicing', () => { - var stack = new NavigationRouteStack(2, ['a', 'b', 'c']); - expect(stack.slice().index).toBe(2); - expect(stack.slice(0, 1).index).toBe(0); - expect(stack.slice(0, 2).index).toBe(1); - expect(stack.slice(0, 3).index).toBe(2); - expect(stack.slice(0, 100).index).toBe(2); - expect(stack.slice(-2).index).toBe(1); - }); - - it('slices without specifying params', () => { - var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c']); - var stack2 = stack1.slice(); - expect(stack2).toBe(stack1); - }); - - it('slices to from the end', () => { - var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c', 'd']); - var stack2 = stack1.slice(-2); - expect(stack2.toArray()).toEqual(['c', 'd']); - }); - - it('throws when slicing to empty', () => { - expect(() => { - var stack = new NavigationRouteStack(1, ['a', 'b']); - stack.slice(100); - }).toThrow(); - }); - - // Push - it('pushes route', () => { - var stack1 = new NavigationRouteStack(1, ['a', 'b']); - var stack2 = stack1.push('c'); - - expect(stack2).not.toBe(stack1); - expect(stack2.toArray()).toEqual(['a', 'b', 'c']); - expect(stack2.index).toBe(2); - expect(stack2.size).toBe(3); - }); - - it('throws when pushing empty route', () => { - expect(() => { - var stack = new NavigationRouteStack(1, ['a', 'b']); - stack.push(null); - }).toThrow(); - - expect(() => { - var stack = new NavigationRouteStack(1, ['a', 'b']); - stack.push(''); - }).toThrow(); - - expect(() => { - var stack = new NavigationRouteStack(1, ['a', 'b']); - stack.push(undefined); - }).toThrow(); - }); - - it('replaces routes on push', () => { - var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c']); - var stack2 = stack1.push('d'); - expect(stack2).not.toBe(stack1); - expect(stack2.toArray()).toEqual(['a', 'b', 'd']); - expect(stack2.index).toBe(2); - }); - - // Pop - it('pops route', () => { - var stack1 = new NavigationRouteStack(2, ['a', 'b', 'c']); - var stack2 = stack1.pop(); - expect(stack2).not.toBe(stack1); - expect(stack2.toArray()).toEqual(['a', 'b']); - expect(stack2.index).toBe(1); - expect(stack2.size).toBe(2); - }); - - it('replaces routes on pop', () => { - var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c']); - var stack2 = stack1.pop(); - expect(stack2).not.toBe(stack1); - expect(stack2.toArray()).toEqual(['a']); - expect(stack2.index).toBe(0); - }); - - it('throws when popping to empty stack', () => { - expect(() => { - var stack = new NavigationRouteStack(0, ['a']); - stack.pop(); - }).toThrow(); - }); - - // Jump - it('jumps to index', () => { - var stack1 = new NavigationRouteStack(0, ['a', 'b', 'c']); - var stack2 = stack1.jumpToIndex(2); - - expect(stack2).not.toBe(stack1); - expect(stack2.index).toBe(2); - }); - - it('throws then jumping to index out of bound', () => { - expect(() => { - var stack = new NavigationRouteStack(1, ['a', 'b']); - stack.jumpToIndex(2); - }).toThrow(); - - expect(() => { - var stack = new NavigationRouteStack(1, ['a', 'b']); - stack.jumpToIndex(-1); - }).toThrow(); - }); - - // Replace - it('replaces route at index', () => { - var stack1 = new NavigationRouteStack(1, ['a', 'b']); - var stack2 = stack1.replaceAtIndex(0, 'x'); - - expect(stack2).not.toBe(stack1); - expect(stack2.toArray()).toEqual(['x', 'b']); - expect(stack2.index).toBe(0); - }); - - it('replaces route at negative index', () => { - var stack1 = new NavigationRouteStack(1, ['a', 'b']); - var stack2 = stack1.replaceAtIndex(-1, 'x'); - - expect(stack2).not.toBe(stack1); - expect(stack2.toArray()).toEqual(['a', 'x']); - expect(stack2.index).toBe(1); - }); - - it('throws when replacing empty route', () => { - expect(() => { - var stack = new NavigationRouteStack(1, ['a', 'b']); - stack.replaceAtIndex(1, null); - }).toThrow(); - }); - - it('throws when replacing at index out of bound', () => { - expect(() => { - var stack = new NavigationRouteStack(1, ['a', 'b']); - stack.replaceAtIndex(100, 'x'); - }).toThrow(); - }); - - // Iteration - it('iterates each item', () => { - var stack = new NavigationRouteStack(0, ['a', 'b']); - var logs = []; - var keys = {}; - var context = {name: 'yo'}; - - stack.forEach(function (route, index, key) { - assetStringNotEmpty(key); - if (!keys.hasOwnProperty(key)) { - keys[key] = true; - logs.push([ - route, - index, - this.name, - ]); - } - }, context); - - expect(logs).toEqual([ - ['a', 0, 'yo'], - ['b', 1, 'yo'], - ]); - }); - - it('Maps to an array', () => { - var stack = new NavigationRouteStack(0, ['a', 'b']); - var keys = {}; - var context = {name: 'yo'}; - - var logs = stack.mapToArray(function(route, index, key) { - assetStringNotEmpty(key); - if (!keys.hasOwnProperty(key)) { - keys[key] = true; - return [ - route, - index, - this.name, - ]; - } - }, context); - - expect(logs).toEqual([ - ['a', 0, 'yo'], - ['b', 1, 'yo'], - ]); - }); - - // Diff - it('subtracts stack', () => { - var stack1 = new NavigationRouteStack(2, ['a', 'b', 'c']); - var stack2 = stack1.pop().pop().push('x').push('y'); - - var diff = stack1.subtract(stack2); - - var result = diff.toJS().map((record) => { - assetStringNotEmpty(record.key); - return { - index: record.index, - route: record.route, - }; - }); - - // route `b` and `c` are no longer in the stack. - expect(result).toEqual([ - { - index: 1, - route: 'b', - }, - { - index: 2, - route: 'c', - }, - ]); - }); - - it('only subtracts the derived stack', () => { - var stack1 = new NavigationRouteStack(2, ['a', 'b', 'c']); - var stack2 = new NavigationRouteStack(0, ['a']); - var diff = stack1.subtract(stack2); - - var result = diff.toJS().map((record) => { - assetStringNotEmpty(record.key); - return { - index: record.index, - route: record.route, - }; - }); - - expect(result).toEqual([ - { - index: 0, - route: 'a', - }, - { - index: 1, - route: 'b', - }, - { - index: 2, - route: 'c', - }, - ]); - - }); -}); diff --git a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationTreeNode-test.js b/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationTreeNode-test.js deleted file mode 100644 index f76d5e9bdada55..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationTreeNode-test.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - */ - -'use strict'; - -jest - .unmock('NavigationTreeNode') - .unmock('fbjs/lib/invariant') - .unmock('immutable'); - -var NavigationTreeNode = require('NavigationTreeNode'); - -describe('NavigationTreeNode-test', () => { - it('should be empty', () => { - var node = new NavigationTreeNode(); - expect(node.getValue()).toEqual(undefined); - expect(node.getParent()).toEqual(null); - expect(node.getChildrenCount()).toEqual(0); - expect(node.getChildAt(0)).toEqual(null); - }); - - - it('should contain value', () => { - var node = new NavigationTreeNode(123); - expect(node.getValue()).toEqual(123); - }); - - it('should appendChild', () => { - var papa = new NavigationTreeNode('hedger'); - var baby = new NavigationTreeNode('hedger jr'); - papa.appendChild(baby); - expect(papa.getChildAt(0)).toEqual(baby); - expect(papa.getChildrenCount()).toEqual(1); - expect(baby.getParent()).toEqual(papa); - }); - - it('should removeChild', () => { - var papa = new NavigationTreeNode('Eddard Stark'); - var baby = new NavigationTreeNode('Robb Stark'); - papa.appendChild(baby); - - papa.removeChild(baby); - expect(papa.getChildAt(0)).toEqual(null); - expect(papa.getChildrenCount()).toEqual(0); - expect(baby.getParent()).toEqual(null); - }); - - it('should not remove non-child', () => { - var papa = new NavigationTreeNode('dog'); - var baby = new NavigationTreeNode('cat'); - expect(papa.removeChild.bind(papa, baby)).toThrow(); - }); - - it('should find child', () => { - var papa = new NavigationTreeNode('Eddard Stark'); - var baby = new NavigationTreeNode('Robb Stark'); - - papa.appendChild(baby); - expect(papa.indexOf(baby)).toEqual(0); - - papa.removeChild(baby); - expect(papa.indexOf(baby)).toEqual(-1); - }); - - - it('should traverse each child', () => { - var parent = new NavigationTreeNode(); - parent.appendChild(new NavigationTreeNode('a')); - parent.appendChild(new NavigationTreeNode('b')); - parent.appendChild(new NavigationTreeNode('c')); - var result = []; - parent.forEach((child, index) => { - result[index] = child.getValue(); - }); - - expect(result).toEqual(['a', 'b', 'c']); - }); - - it('should map children', () => { - var parent = new NavigationTreeNode(); - parent.appendChild(new NavigationTreeNode('a')); - parent.appendChild(new NavigationTreeNode('b')); - parent.appendChild(new NavigationTreeNode('c')); - var result = parent.map((child, index) => { - return child.getValue(); - }); - - expect(result).toEqual(['a', 'b', 'c']); - }); - - it('should traverse some children', () => { - var parent = new NavigationTreeNode(); - parent.appendChild(new NavigationTreeNode('a')); - parent.appendChild(new NavigationTreeNode('b')); - parent.appendChild(new NavigationTreeNode('c')); - - var result = []; - var value = parent.some((child, index) => { - if (index > 1) { - return true; - } else { - result[index] = child.getValue(); - } - }); - - expect(value).toEqual(true); - expect(result).toEqual(['a', 'b']); - }); -}); diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js deleted file mode 100644 index 105e7281a0881e..00000000000000 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ /dev/null @@ -1,1366 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule Navigator - */ - /* eslint-disable no-extra-boolean-cast*/ -'use strict'; - -var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule; -var Dimensions = require('Dimensions'); -var InteractionMixin = require('InteractionMixin'); -var NavigationContext = require('NavigationContext'); -var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar'); -var NavigatorNavigationBar = require('NavigatorNavigationBar'); -var NavigatorSceneConfigs = require('NavigatorSceneConfigs'); -var PanResponder = require('PanResponder'); -var React = require('React'); -var StyleSheet = require('StyleSheet'); -var Subscribable = require('Subscribable'); -var TVEventHandler = require('TVEventHandler'); -var TimerMixin = require('react-timer-mixin'); -var View = require('View'); - -var clamp = require('clamp'); -var flattenStyle = require('flattenStyle'); -var invariant = require('fbjs/lib/invariant'); -var rebound = require('rebound'); - -var PropTypes = React.PropTypes; - -// TODO: this is not ideal because there is no guarantee that the navigator -// is full screen, however we don't have a good way to measure the actual -// size of the navigator right now, so this is the next best thing. -var SCREEN_WIDTH = Dimensions.get('window').width; -var SCREEN_HEIGHT = Dimensions.get('window').height; -var SCENE_DISABLED_NATIVE_PROPS = { - pointerEvents: 'none', - style: { - top: SCREEN_HEIGHT, - bottom: -SCREEN_HEIGHT, - opacity: 0, - }, -}; - -var __uid = 0; -function getuid() { - return __uid++; -} - -function getRouteID(route) { - if (route === null || typeof route !== 'object') { - return String(route); - } - - var key = '__navigatorRouteID'; - - if (!route.hasOwnProperty(key)) { - Object.defineProperty(route, key, { - enumerable: false, - configurable: false, - writable: false, - value: getuid(), - }); - } - return route[key]; -} - -// styles moved to the top of the file so getDefaultProps can refer to it -var styles = StyleSheet.create({ - container: { - flex: 1, - overflow: 'hidden', - }, - defaultSceneStyle: { - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - top: 0, - transform: [ - {translateX: 0}, - {translateY: 0}, - {scaleX: 1}, - {scaleY: 1}, - {rotate: '0deg'}, - {skewX: '0deg'}, - {skewY: '0deg'}, - ], - }, - baseScene: { - position: 'absolute', - overflow: 'hidden', - left: 0, - right: 0, - bottom: 0, - top: 0, - }, - disabledScene: { - top: SCREEN_HEIGHT, - bottom: -SCREEN_HEIGHT, - }, - transitioner: { - flex: 1, - backgroundColor: 'transparent', - overflow: 'hidden', - } -}); - -var GESTURE_ACTIONS = [ - 'pop', - 'jumpBack', - 'jumpForward', -]; - -/** - * `Navigator` handles the transition between different scenes in your app. - * It is implemented in JavaScript and is available on both iOS and Android. If - * you are targeting iOS only, you may also want to consider using - * [`NavigatorIOS`](docs/navigatorios.html) as it leverages native UIKit - * navigation. - * - * To set up the `Navigator` you provide one or more objects called routes, - * to identify each scene. You also provide a `renderScene` function that - * renders the scene for each route object. - * - * ``` - * import React, { Component } from 'react'; - * import { Text, Navigator, TouchableHighlight } from 'react-native'; - * - * export default class NavAllDay extends Component { - * render() { - * return ( - * - * Hello {route.title}! - * } - * style={{padding: 100}} - * /> - * ); - * } - * } - * ``` - * - * In the above example, `initialRoute` is used to specify the first route. It - * contains a `title` property that identifies the route. The `renderScene` - * prop returns a function that displays text based on the route's title. - * - * ### Additional Scenes - * - * The first example demonstrated one scene. To set up multiple scenes, you pass - * the `initialRouteStack` prop to `Navigator`: - * - * ``` - * render() { - * const routes = [ - * {title: 'First Scene', index: 0}, - * {title: 'Second Scene', index: 1}, - * ]; - * return ( - * - * { - * if (route.index === 0) { - * navigator.push(routes[1]); - * } else { - * navigator.pop(); - * } - * }}> - * Hello {route.title}! - * - * } - * style={{padding: 100}} - * /> - * ); - * } - * ``` - * - * In the above example, a `routes` variable is defined with two route objects - * representing two scenes. Each route has an `index` property that is used to - * manage the scene being rendered. The `renderScene` method is changed to - * either push or pop the navigator depending on the current route's index. - * Finally, the `Text` component in the scene is now wrapped in a - * `TouchableHighlight` component to help trigger the navigator transitions. - * - * ### Navigation Bar - * - * You can optionally pass in your own navigation bar by returning a - * `Navigator.NavigationBar` component to the `navigationBar` prop in - * `Navigator`. You can configure the navigation bar properties, through - * the `routeMapper` prop. There you set up the left, right, and title - * properties of the navigation bar: - * - * ``` - * - * // ... - * } - * navigationBar={ - * - * { return (Cancel); }, - * RightButton: (route, navigator, index, navState) => - * { return (Done); }, - * Title: (route, navigator, index, navState) => - * { return (Awesome Nav Bar); }, - * }} - * style={{backgroundColor: 'gray'}} - * /> - * } - * /> - * ``` - * - * When configuring the left, right, and title items for the navigation bar, - * you have access to info such as the current route object and navigation - * state. This allows you to customize the title for each scene as well as - * the buttons. For example, you can choose to hide the left button for one of - * the scenes. - * - * Typically you want buttons to represent the left and right buttons. Building - * on the previous example, you can set this up as follows: - * - * ``` - * LeftButton: (route, navigator, index, navState) => - * { - * if (route.index === 0) { - * return null; - * } else { - * return ( - * navigator.pop()}> - * Back - * - * ); - * } - * }, - * ``` - * - * This sets up a left navigator bar button that's visible on scenes after the - * the first one. When the button is tapped the navigator is popped. - * - * Another type of navigation bar, with breadcrumbs, is provided by - * `Navigator.BreadcrumbNavigationBar`. You can also provide your own navigation - * bar by passing it through the `navigationBar` prop. See the - * [UIExplorer](https://github.com/facebook/react-native/tree/master/Examples/UIExplorer) - * demo to try out both built-in navigation bars out and see how to use them. - * - * ### Scene Transitions - * - * To change the animation or gesture properties of the scene, provide a - * `configureScene` prop to get the config object for a given route: - * - * ``` - * - * // ... - * } - * configureScene={(route, routeStack) => - * Navigator.SceneConfigs.FloatFromBottom} - * /> - * ``` - * In the above example, the newly pushed scene will float up from the bottom. - * See `Navigator.SceneConfigs` for default animations and more info on - * available [scene config options](docs/navigator.html#configurescene). - */ -var Navigator = React.createClass({ - - propTypes: { - /** - * Optional function where you can configure scene animations and - * gestures. Will be invoked with `route` and `routeStack` parameters, - * where `route` corresponds to the current scene being rendered by the - * `Navigator` and `routeStack` is the set of currently mounted routes - * that the navigator could transition to. - * - * The function should return a scene configuration object. - * - * ``` - * (route, routeStack) => Navigator.SceneConfigs.FloatFromRight - * ``` - * - * Available scene configuration options are: - * - * - Navigator.SceneConfigs.PushFromRight (default) - * - Navigator.SceneConfigs.FloatFromRight - * - Navigator.SceneConfigs.FloatFromLeft - * - Navigator.SceneConfigs.FloatFromBottom - * - Navigator.SceneConfigs.FloatFromBottomAndroid - * - Navigator.SceneConfigs.FadeAndroid - * - Navigator.SceneConfigs.SwipeFromLeft - * - Navigator.SceneConfigs.HorizontalSwipeJump - * - Navigator.SceneConfigs.HorizontalSwipeJumpFromRight - * - Navigator.SceneConfigs.HorizontalSwipeJumpFromLeft - * - Navigator.SceneConfigs.VerticalUpSwipeJump - * - Navigator.SceneConfigs.VerticalDownSwipeJump - * - */ - configureScene: PropTypes.func, - - /** - * Required function which renders the scene for a given route. Will be - * invoked with the `route` and the `navigator` object. - * - * ``` - * (route, navigator) => - * - * ``` - */ - renderScene: PropTypes.func.isRequired, - - /** - * The initial route for navigation. A route is an object that the navigator - * will use to identify each scene it renders. - * - * If both `initialRoute` and `initialRouteStack` props are passed to - * `Navigator`, then `initialRoute` must be in a route in - * `initialRouteStack`. If `initialRouteStack` is passed as a prop but - * `initialRoute` is not, then `initialRoute` will default internally to - * the last item in `initialRouteStack`. - */ - initialRoute: PropTypes.object, - - /** - * Pass this in to provide a set of routes to initially mount. This prop - * is required if `initialRoute` is not provided to the navigator. If this - * prop is not passed in, it will default internally to an array - * containing only `initialRoute`. - */ - initialRouteStack: PropTypes.arrayOf(PropTypes.object), - - /** - * Pass in a function to get notified with the target route when - * the navigator component is mounted and before each navigator transition. - */ - onWillFocus: PropTypes.func, - - /** - * Will be called with the new route of each scene after the transition is - * complete or after the initial mounting. - */ - onDidFocus: PropTypes.func, - - /** - * Use this to provide an optional component representing a navigation bar - * that is persisted across scene transitions. This component will receive - * two props: `navigator` and `navState` representing the navigator - * component and its state. The component is re-rendered when the route - * changes. - */ - navigationBar: PropTypes.node, - - /** - * Optionally pass in the navigator object from a parent `Navigator`. - */ - navigator: PropTypes.object, - - /** - * Styles to apply to the container of each scene. - */ - sceneStyle: View.propTypes.style, - }, - - statics: { - BreadcrumbNavigationBar: NavigatorBreadcrumbNavigationBar, - NavigationBar: NavigatorNavigationBar, - SceneConfigs: NavigatorSceneConfigs, - }, - - mixins: [TimerMixin, InteractionMixin, Subscribable.Mixin], - - getDefaultProps: function() { - return { - configureScene: () => NavigatorSceneConfigs.PushFromRight, - sceneStyle: styles.defaultSceneStyle, - }; - }, - - getInitialState: function() { - this._navigationBarNavigator = this.props.navigationBarNavigator || this; - - this._renderedSceneMap = new Map(); - - this._sceneRefs = []; - - var routeStack = this.props.initialRouteStack || [this.props.initialRoute]; - invariant( - routeStack.length >= 1, - 'Navigator requires props.initialRoute or props.initialRouteStack.' - ); - var initialRouteIndex = routeStack.length - 1; - if (this.props.initialRoute) { - initialRouteIndex = routeStack.indexOf(this.props.initialRoute); - invariant( - initialRouteIndex !== -1, - 'initialRoute is not in initialRouteStack.' - ); - } - return { - sceneConfigStack: routeStack.map( - (route) => this.props.configureScene(route, routeStack) - ), - routeStack, - presentedIndex: initialRouteIndex, - transitionFromIndex: null, - activeGesture: null, - pendingGestureProgress: null, - transitionQueue: [], - }; - }, - - componentWillMount: function() { - // TODO(t7489503): Don't need this once ES6 Class landed. - this.__defineGetter__('navigationContext', this._getNavigationContext); - - this._subRouteFocus = []; - this.parentNavigator = this.props.navigator; - this._handlers = {}; - this.springSystem = new rebound.SpringSystem(); - this.spring = this.springSystem.createSpring(); - this.spring.setRestSpeedThreshold(0.05); - this.spring.setCurrentValue(0).setAtRest(); - this.spring.addListener({ - onSpringEndStateChange: () => { - if (!this._interactionHandle) { - this._interactionHandle = this.createInteractionHandle(); - } - }, - onSpringUpdate: () => { - this._handleSpringUpdate(); - }, - onSpringAtRest: () => { - this._completeTransition(); - }, - }); - this.panGesture = PanResponder.create({ - onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder, - onPanResponderRelease: this._handlePanResponderRelease, - onPanResponderMove: this._handlePanResponderMove, - onPanResponderTerminate: this._handlePanResponderTerminate, - }); - this._interactionHandle = null; - this._emitWillFocus(this.state.routeStack[this.state.presentedIndex]); - }, - - componentDidMount: function() { - this._handleSpringUpdate(); - this._emitDidFocus(this.state.routeStack[this.state.presentedIndex]); - this._enableTVEventHandler(); - }, - - componentWillUnmount: function() { - if (this._navigationContext) { - this._navigationContext.dispose(); - this._navigationContext = null; - } - - this.spring.destroy(); - - if (this._interactionHandle) { - this.clearInteractionHandle(this._interactionHandle); - } - - this._disableTVEventHandler(); - }, - - /** - * Reset every scene with an array of routes. - * - * @param {RouteStack} nextRouteStack Next route stack to reinitialize. - * All existing route stacks are destroyed and potentially recreated. There - * is no accompanying animation and this method immediately replaces and - * re-renders the navigation bar and the stack items. - */ - immediatelyResetRouteStack: function(nextRouteStack) { - var destIndex = nextRouteStack.length - 1; - this._emitWillFocus(nextRouteStack[destIndex]); - this.setState({ - routeStack: nextRouteStack, - sceneConfigStack: nextRouteStack.map( - route => this.props.configureScene(route, nextRouteStack) - ), - presentedIndex: destIndex, - activeGesture: null, - transitionFromIndex: null, - transitionQueue: [], - }, () => { - this._handleSpringUpdate(); - var navBar = this._navBar; - if (navBar && navBar.immediatelyRefresh) { - navBar.immediatelyRefresh(); - } - this._emitDidFocus(this.state.routeStack[this.state.presentedIndex]); - }); - }, - - _transitionTo: function(destIndex, velocity, jumpSpringTo, cb) { - if (this.state.presentedIndex === destIndex) { - cb && cb(); - return; - } - - if (this.state.transitionFromIndex !== null) { - // Navigation is still transitioning, put the `destIndex` into queue. - this.state.transitionQueue.push({ - destIndex, - velocity, - cb, - }); - return; - } - - this.state.transitionFromIndex = this.state.presentedIndex; - this.state.presentedIndex = destIndex; - this.state.transitionCb = cb; - this._onAnimationStart(); - if (AnimationsDebugModule) { - AnimationsDebugModule.startRecordingFps(); - } - var sceneConfig = this.state.sceneConfigStack[this.state.transitionFromIndex] || - this.state.sceneConfigStack[this.state.presentedIndex]; - invariant( - sceneConfig, - 'Cannot configure scene at index ' + this.state.transitionFromIndex - ); - if (jumpSpringTo != null) { - this.spring.setCurrentValue(jumpSpringTo); - } - this.spring.setOvershootClampingEnabled(true); - this.spring.getSpringConfig().friction = sceneConfig.springFriction; - this.spring.getSpringConfig().tension = sceneConfig.springTension; - this.spring.setVelocity(velocity || sceneConfig.defaultTransitionVelocity); - this.spring.setEndValue(1); - }, - - /** - * This happens for each frame of either a gesture or a transition. If both are - * happening, we only set values for the transition and the gesture will catch up later - */ - _handleSpringUpdate: function() { - if (!this.isMounted()) { - return; - } - // Prioritize handling transition in progress over a gesture: - if (this.state.transitionFromIndex != null) { - this._transitionBetween( - this.state.transitionFromIndex, - this.state.presentedIndex, - this.spring.getCurrentValue() - ); - } else if (this.state.activeGesture != null) { - var presentedToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); - this._transitionBetween( - this.state.presentedIndex, - presentedToIndex, - this.spring.getCurrentValue() - ); - } - }, - - /** - * This happens at the end of a transition started by transitionTo, and when the spring catches up to a pending gesture - */ - _completeTransition: function() { - if (!this.isMounted()) { - return; - } - - if (this.spring.getCurrentValue() !== 1 && this.spring.getCurrentValue() !== 0) { - // The spring has finished catching up to a gesture in progress. Remove the pending progress - // and we will be in a normal activeGesture state - if (this.state.pendingGestureProgress) { - this.state.pendingGestureProgress = null; - } - return; - } - this._onAnimationEnd(); - var presentedIndex = this.state.presentedIndex; - var didFocusRoute = this._subRouteFocus[presentedIndex] || this.state.routeStack[presentedIndex]; - - if (AnimationsDebugModule) { - AnimationsDebugModule.stopRecordingFps(Date.now()); - } - this.state.transitionFromIndex = null; - this.spring.setCurrentValue(0).setAtRest(); - this._hideScenes(); - if (this.state.transitionCb) { - this.state.transitionCb(); - this.state.transitionCb = null; - } - - this._emitDidFocus(didFocusRoute); - - if (this._interactionHandle) { - this.clearInteractionHandle(this._interactionHandle); - this._interactionHandle = null; - } - if (this.state.pendingGestureProgress) { - // A transition completed, but there is already another gesture happening. - // Enable the scene and set the spring to catch up with the new gesture - var gestureToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); - this._enableScene(gestureToIndex); - this.spring.setEndValue(this.state.pendingGestureProgress); - return; - } - if (this.state.transitionQueue.length) { - var queuedTransition = this.state.transitionQueue.shift(); - this._enableScene(queuedTransition.destIndex); - this._emitWillFocus(this.state.routeStack[queuedTransition.destIndex]); - this._transitionTo( - queuedTransition.destIndex, - queuedTransition.velocity, - null, - queuedTransition.cb - ); - } - }, - - _emitDidFocus: function(route) { - this.navigationContext.emit('didfocus', {route: route}); - - if (this.props.onDidFocus) { - this.props.onDidFocus(route); - } - }, - - _emitWillFocus: function(route) { - this.navigationContext.emit('willfocus', {route: route}); - - var navBar = this._navBar; - if (navBar && navBar.handleWillFocus) { - navBar.handleWillFocus(route); - } - if (this.props.onWillFocus) { - this.props.onWillFocus(route); - } - }, - - /** - * Hides all scenes that we are not currently on, gesturing to, or transitioning from - */ - _hideScenes: function() { - var gesturingToIndex = null; - if (this.state.activeGesture) { - gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); - } - for (var i = 0; i < this.state.routeStack.length; i++) { - if (i === this.state.presentedIndex || - i === this.state.transitionFromIndex || - i === gesturingToIndex) { - continue; - } - this._disableScene(i); - } - }, - - /** - * Push a scene off the screen, so that opacity:0 scenes will not block touches sent to the presented scenes - */ - _disableScene: function(sceneIndex) { - this._sceneRefs[sceneIndex] && - this._sceneRefs[sceneIndex].setNativeProps(SCENE_DISABLED_NATIVE_PROPS); - }, - - /** - * Put the scene back into the state as defined by props.sceneStyle, so transitions can happen normally - */ - _enableScene: function(sceneIndex) { - // First, determine what the defined styles are for scenes in this navigator - var sceneStyle = flattenStyle([styles.baseScene, this.props.sceneStyle]); - // Then restore the pointer events and top value for this scene - var enabledSceneNativeProps = { - pointerEvents: 'auto', - style: { - top: sceneStyle.top, - bottom: sceneStyle.bottom, - }, - }; - if (sceneIndex !== this.state.transitionFromIndex && - sceneIndex !== this.state.presentedIndex) { - // If we are not in a transition from this index, make sure opacity is 0 - // to prevent the enabled scene from flashing over the presented scene - enabledSceneNativeProps.style.opacity = 0; - } - this._sceneRefs[sceneIndex] && - this._sceneRefs[sceneIndex].setNativeProps(enabledSceneNativeProps); - }, - - _clearTransformations: function(sceneIndex) { - const defaultStyle = flattenStyle([styles.defaultSceneStyle]); - this._sceneRefs[sceneIndex].setNativeProps({ style: defaultStyle }); - }, - - _onAnimationStart: function() { - var fromIndex = this.state.presentedIndex; - var toIndex = this.state.presentedIndex; - if (this.state.transitionFromIndex != null) { - fromIndex = this.state.transitionFromIndex; - } else if (this.state.activeGesture) { - toIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); - } - this._setRenderSceneToHardwareTextureAndroid(fromIndex, true); - this._setRenderSceneToHardwareTextureAndroid(toIndex, true); - var navBar = this._navBar; - if (navBar && navBar.onAnimationStart) { - navBar.onAnimationStart(fromIndex, toIndex); - } - }, - - _onAnimationEnd: function() { - var max = this.state.routeStack.length - 1; - for (var index = 0; index <= max; index++) { - this._setRenderSceneToHardwareTextureAndroid(index, false); - } - - var navBar = this._navBar; - if (navBar && navBar.onAnimationEnd) { - navBar.onAnimationEnd(); - } - }, - - _setRenderSceneToHardwareTextureAndroid: function(sceneIndex, shouldRenderToHardwareTexture) { - var viewAtIndex = this._sceneRefs[sceneIndex]; - if (viewAtIndex === null || viewAtIndex === undefined) { - return; - } - viewAtIndex.setNativeProps({renderToHardwareTextureAndroid: shouldRenderToHardwareTexture}); - }, - - _handleTouchStart: function() { - this._eligibleGestures = GESTURE_ACTIONS; - }, - - _handleMoveShouldSetPanResponder: function(e, gestureState) { - var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - if (!sceneConfig) { - return false; - } - this._expectingGestureGrant = - this._matchGestureAction(this._eligibleGestures, sceneConfig.gestures, gestureState); - return !!this._expectingGestureGrant; - }, - - _doesGestureOverswipe: function(gestureName) { - var wouldOverswipeBack = this.state.presentedIndex <= 0 && - (gestureName === 'pop' || gestureName === 'jumpBack'); - var wouldOverswipeForward = this.state.presentedIndex >= this.state.routeStack.length - 1 && - gestureName === 'jumpForward'; - return wouldOverswipeForward || wouldOverswipeBack; - }, - - _deltaForGestureAction: function(gestureAction) { - switch (gestureAction) { - case 'pop': - case 'jumpBack': - return -1; - case 'jumpForward': - return 1; - default: - invariant(false, 'Unsupported gesture action ' + gestureAction); - return; - } - }, - - _handlePanResponderRelease: function(e, gestureState) { - var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - var releaseGestureAction = this.state.activeGesture; - if (!releaseGestureAction) { - // The gesture may have been detached while responder, so there is no action here - return; - } - var releaseGesture = sceneConfig.gestures[releaseGestureAction]; - var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); - if (this.spring.getCurrentValue() === 0) { - // The spring is at zero, so the gesture is already complete - this.spring.setCurrentValue(0).setAtRest(); - this._completeTransition(); - return; - } - var isTravelVertical = releaseGesture.direction === 'top-to-bottom' || releaseGesture.direction === 'bottom-to-top'; - var isTravelInverted = releaseGesture.direction === 'right-to-left' || releaseGesture.direction === 'bottom-to-top'; - var velocity, gestureDistance; - if (isTravelVertical) { - velocity = isTravelInverted ? -gestureState.vy : gestureState.vy; - gestureDistance = isTravelInverted ? -gestureState.dy : gestureState.dy; - } else { - velocity = isTravelInverted ? -gestureState.vx : gestureState.vx; - gestureDistance = isTravelInverted ? -gestureState.dx : gestureState.dx; - } - var transitionVelocity = clamp(-10, velocity, 10); - if (Math.abs(velocity) < releaseGesture.notMoving) { - // The gesture velocity is so slow, is "not moving" - var hasGesturedEnoughToComplete = gestureDistance > releaseGesture.fullDistance * releaseGesture.stillCompletionRatio; - transitionVelocity = hasGesturedEnoughToComplete ? releaseGesture.snapVelocity : -releaseGesture.snapVelocity; - } - if (transitionVelocity < 0 || this._doesGestureOverswipe(releaseGestureAction)) { - // This gesture is to an overswiped region or does not have enough velocity to complete - // If we are currently mid-transition, then this gesture was a pending gesture. Because this gesture takes no action, we can stop here - if (this.state.transitionFromIndex == null) { - // There is no current transition, so we need to transition back to the presented index - var transitionBackToPresentedIndex = this.state.presentedIndex; - // slight hack: change the presented index for a moment in order to transitionTo correctly - this.state.presentedIndex = destIndex; - this._transitionTo( - transitionBackToPresentedIndex, - -transitionVelocity, - 1 - this.spring.getCurrentValue() - ); - } - } else { - // The gesture has enough velocity to complete, so we transition to the gesture's destination - this._emitWillFocus(this.state.routeStack[destIndex]); - this._transitionTo( - destIndex, - transitionVelocity, - null, - () => { - if (releaseGestureAction === 'pop') { - this._cleanScenesPastIndex(destIndex); - } - } - ); - } - this._detachGesture(); - }, - - _handlePanResponderTerminate: function(e, gestureState) { - if (this.state.activeGesture == null) { - return; - } - var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); - this._detachGesture(); - var transitionBackToPresentedIndex = this.state.presentedIndex; - // slight hack: change the presented index for a moment in order to transitionTo correctly - this.state.presentedIndex = destIndex; - this._transitionTo( - transitionBackToPresentedIndex, - null, - 1 - this.spring.getCurrentValue() - ); - }, - - _attachGesture: function(gestureId) { - this.state.activeGesture = gestureId; - var gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); - this._enableScene(gesturingToIndex); - }, - - _detachGesture: function() { - this.state.activeGesture = null; - this.state.pendingGestureProgress = null; - this._hideScenes(); - }, - - _handlePanResponderMove: function(e, gestureState) { - if (this._isMoveGestureAttached !== undefined) { - invariant( - this._expectingGestureGrant, - 'Responder granted unexpectedly.' - ); - this._attachGesture(this._expectingGestureGrant); - this._onAnimationStart(); - this._expectingGestureGrant = undefined; - } - - var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - if (this.state.activeGesture) { - var gesture = sceneConfig.gestures[this.state.activeGesture]; - return this._moveAttachedGesture(gesture, gestureState); - } - var matchedGesture = this._matchGestureAction(GESTURE_ACTIONS, sceneConfig.gestures, gestureState); - if (matchedGesture) { - this._attachGesture(matchedGesture); - } - }, - - _moveAttachedGesture: function(gesture, gestureState) { - var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top'; - var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top'; - var distance = isTravelVertical ? gestureState.dy : gestureState.dx; - distance = isTravelInverted ? -distance : distance; - var gestureDetectMovement = gesture.gestureDetectMovement; - var nextProgress = (distance - gestureDetectMovement) / - (gesture.fullDistance - gestureDetectMovement); - if (nextProgress < 0 && gesture.isDetachable) { - var gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture); - this._transitionBetween(this.state.presentedIndex, gesturingToIndex, 0); - this._detachGesture(); - if (this.state.pendingGestureProgress != null) { - this.spring.setCurrentValue(0); - } - return; - } - if (gesture.overswipe && this._doesGestureOverswipe(this.state.activeGesture)) { - var frictionConstant = gesture.overswipe.frictionConstant; - var frictionByDistance = gesture.overswipe.frictionByDistance; - var frictionRatio = 1 / ((frictionConstant) + (Math.abs(nextProgress) * frictionByDistance)); - nextProgress *= frictionRatio; - } - nextProgress = clamp(0, nextProgress, 1); - if (this.state.transitionFromIndex != null) { - this.state.pendingGestureProgress = nextProgress; - } else if (this.state.pendingGestureProgress) { - this.spring.setEndValue(nextProgress); - } else { - this.spring.setCurrentValue(nextProgress); - } - }, - - _matchGestureAction: function(eligibleGestures, gestures, gestureState) { - if (!gestures || !eligibleGestures || !eligibleGestures.some) { - return null; - } - var matchedGesture = null; - eligibleGestures.some((gestureName, gestureIndex) => { - var gesture = gestures[gestureName]; - if (!gesture) { - return; - } - if (gesture.overswipe == null && this._doesGestureOverswipe(gestureName)) { - // cannot swipe past first or last scene without overswiping - return false; - } - var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top'; - var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top'; - var startedLoc = isTravelVertical ? gestureState.y0 : gestureState.x0; - var currentLoc = isTravelVertical ? gestureState.moveY : gestureState.moveX; - var travelDist = isTravelVertical ? gestureState.dy : gestureState.dx; - var oppositeAxisTravelDist = - isTravelVertical ? gestureState.dx : gestureState.dy; - var edgeHitWidth = gesture.edgeHitWidth; - if (isTravelInverted) { - startedLoc = -startedLoc; - currentLoc = -currentLoc; - travelDist = -travelDist; - oppositeAxisTravelDist = -oppositeAxisTravelDist; - edgeHitWidth = isTravelVertical ? - -(SCREEN_HEIGHT - edgeHitWidth) : - -(SCREEN_WIDTH - edgeHitWidth); - } - if (startedLoc === 0) { - startedLoc = currentLoc; - } - var moveStartedInRegion = gesture.edgeHitWidth == null || - startedLoc < edgeHitWidth; - if (!moveStartedInRegion) { - return false; - } - var moveTravelledFarEnough = travelDist >= gesture.gestureDetectMovement; - if (!moveTravelledFarEnough) { - return false; - } - var directionIsCorrect = Math.abs(travelDist) > Math.abs(oppositeAxisTravelDist) * gesture.directionRatio; - if (directionIsCorrect) { - matchedGesture = gestureName; - return true; - } else { - this._eligibleGestures = this._eligibleGestures.slice().splice(gestureIndex, 1); - } - }); - return matchedGesture || null; - }, - - _transitionSceneStyle: function(fromIndex, toIndex, progress, index) { - var viewAtIndex = this._sceneRefs[index]; - if (viewAtIndex === null || viewAtIndex === undefined) { - return; - } - // Use toIndex animation when we move forwards. Use fromIndex when we move back - var sceneConfigIndex = fromIndex < toIndex ? toIndex : fromIndex; - var sceneConfig = this.state.sceneConfigStack[sceneConfigIndex]; - // this happens for overswiping when there is no scene at toIndex - if (!sceneConfig) { - sceneConfig = this.state.sceneConfigStack[sceneConfigIndex - 1]; - } - var styleToUse = {}; - var useFn = index < fromIndex || index < toIndex ? - sceneConfig.animationInterpolators.out : - sceneConfig.animationInterpolators.into; - var directionAdjustedProgress = fromIndex < toIndex ? progress : 1 - progress; - var didChange = useFn(styleToUse, directionAdjustedProgress); - if (didChange) { - viewAtIndex.setNativeProps({style: styleToUse}); - } - }, - - _transitionBetween: function(fromIndex, toIndex, progress) { - this._transitionSceneStyle(fromIndex, toIndex, progress, fromIndex); - this._transitionSceneStyle(fromIndex, toIndex, progress, toIndex); - var navBar = this._navBar; - if (navBar && navBar.updateProgress && toIndex >= 0 && fromIndex >= 0) { - navBar.updateProgress(progress, fromIndex, toIndex); - } - }, - - _handleResponderTerminationRequest: function() { - return false; - }, - - _getDestIndexWithinBounds: function(n) { - var currentIndex = this.state.presentedIndex; - var destIndex = currentIndex + n; - invariant( - destIndex >= 0, - 'Cannot jump before the first route.' - ); - var maxIndex = this.state.routeStack.length - 1; - invariant( - maxIndex >= destIndex, - 'Cannot jump past the last route.' - ); - return destIndex; - }, - - _jumpN: function(n) { - var destIndex = this._getDestIndexWithinBounds(n); - this._enableScene(destIndex); - this._emitWillFocus(this.state.routeStack[destIndex]); - this._transitionTo(destIndex); - }, - - /** - * Transition to an existing scene without unmounting. - * @param {object} route Route to transition to. The specified route must - * be in the currently mounted set of routes defined in `routeStack`. - */ - jumpTo: function(route) { - var destIndex = this.state.routeStack.indexOf(route); - invariant( - destIndex !== -1, - 'Cannot jump to route that is not in the route stack' - ); - this._jumpN(destIndex - this.state.presentedIndex); - }, - - /** - * Jump forward to the next scene in the route stack. - */ - jumpForward: function() { - this._jumpN(1); - }, - - /** - * Jump backward without unmounting the current scene. - */ - jumpBack: function() { - this._jumpN(-1); - }, - - /** - * Navigate forward to a new scene, squashing any scenes that you could - * jump forward to. - * @param {object} route Route to push into the navigator stack. - */ - push: function(route) { - invariant(!!route, 'Must supply route to push'); - var activeLength = this.state.presentedIndex + 1; - var activeStack = this.state.routeStack.slice(0, activeLength); - var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength); - var nextStack = activeStack.concat([route]); - var destIndex = nextStack.length - 1; - var nextSceneConfig = this.props.configureScene(route, nextStack); - var nextAnimationConfigStack = activeAnimationConfigStack.concat([nextSceneConfig]); - this._emitWillFocus(nextStack[destIndex]); - this.setState({ - routeStack: nextStack, - sceneConfigStack: nextAnimationConfigStack, - }, () => { - this._enableScene(destIndex); - this._transitionTo(destIndex, nextSceneConfig.defaultTransitionVelocity); - }); - }, - - /** - * Go back N scenes at once. When N=1, behavior matches `pop()`. - * When N is invalid(negative or bigger than current routes count), do nothing. - * @param {number} n The number of scenes to pop. Should be an integer. - */ - popN: function(n) { - invariant(typeof n === 'number', 'Must supply a number to popN'); - n = parseInt(n, 10); - if (n <= 0 || this.state.presentedIndex - n < 0) { - return; - } - var popIndex = this.state.presentedIndex - n; - var presentedRoute = this.state.routeStack[this.state.presentedIndex]; - var popSceneConfig = this.props.configureScene(presentedRoute); // using the scene config of the currently presented view - this._enableScene(popIndex); - // This is needed because scene at the pop index may be transformed - // with a configuration different from the configuration on the presented - // route. - this._clearTransformations(popIndex); - this._emitWillFocus(this.state.routeStack[popIndex]); - this._transitionTo( - popIndex, - popSceneConfig.defaultTransitionVelocity, - null, // no spring jumping - () => { - this._cleanScenesPastIndex(popIndex); - } - ); - }, - - /** - * Transition back and unmount the current scene. - */ - pop: function() { - if (this.state.transitionQueue.length) { - // This is the workaround to prevent user from firing multiple `pop()` - // calls that may pop the routes beyond the limit. - // Because `this.state.presentedIndex` does not update until the - // transition starts, we can't reliably use `this.state.presentedIndex` - // to know whether we can safely keep popping the routes or not at this - // moment. - return; - } - - this.popN(1); - }, - - /** - * Replace a scene as specified by an index. - * @param {object} route Route representing the new scene to render. - * @param {number} index The route in the stack that should be replaced. - * If negative, it counts from the back of the stack. - * @param {Function} cb Callback function when the scene has been replaced. - */ - replaceAtIndex: function(route, index, cb) { - invariant(!!route, 'Must supply route to replace'); - if (index < 0) { - index += this.state.routeStack.length; - } - - if (this.state.routeStack.length <= index) { - return; - } - - var nextRouteStack = this.state.routeStack.slice(); - var nextAnimationModeStack = this.state.sceneConfigStack.slice(); - nextRouteStack[index] = route; - nextAnimationModeStack[index] = this.props.configureScene(route, nextRouteStack); - - if (index === this.state.presentedIndex) { - this._emitWillFocus(route); - } - this.setState({ - routeStack: nextRouteStack, - sceneConfigStack: nextAnimationModeStack, - }, () => { - if (index === this.state.presentedIndex) { - this._emitDidFocus(route); - } - cb && cb(); - }); - }, - - /** - * Replace the current scene with a new route. - * @param {object} route Route that replaces the current scene. - */ - replace: function(route) { - this.replaceAtIndex(route, this.state.presentedIndex); - }, - - /** - * Replace the previous scene. - * @param {object} route Route that replaces the previous scene. - */ - replacePrevious: function(route) { - this.replaceAtIndex(route, this.state.presentedIndex - 1); - }, - - /** - * Pop to the first scene in the stack, unmounting every other scene. - */ - popToTop: function() { - this.popToRoute(this.state.routeStack[0]); - }, - - /** - * Pop to a particular scene, as specified by its route. - * All scenes after it will be unmounted. - * @param {object} route Route to pop to. - */ - popToRoute: function(route) { - var indexOfRoute = this.state.routeStack.indexOf(route); - invariant( - indexOfRoute !== -1, - 'Calling popToRoute for a route that doesn\'t exist!' - ); - var numToPop = this.state.presentedIndex - indexOfRoute; - this.popN(numToPop); - }, - - /** - * Replace the previous scene and pop to it. - * @param {object} route Route that replaces the previous scene. - */ - replacePreviousAndPop: function(route) { - if (this.state.routeStack.length < 2) { - return; - } - this.replacePrevious(route); - this.pop(); - }, - - /** - * Navigate to a new scene and reset route stack. - * @param {object} route Route to navigate to. - */ - resetTo: function(route) { - invariant(!!route, 'Must supply route to push'); - this.replaceAtIndex(route, 0, () => { - // Do not use popToRoute here, because race conditions could prevent the - // route from existing at this time. Instead, just go to index 0 - this.popN(this.state.presentedIndex); - }); - }, - - /** - * Returns the current list of routes. - */ - getCurrentRoutes: function() { - // Clone before returning to avoid caller mutating the stack - return this.state.routeStack.slice(); - }, - - _cleanScenesPastIndex: function(index) { - var newStackLength = index + 1; - // Remove any unneeded rendered routes. - if (newStackLength < this.state.routeStack.length) { - this.setState({ - sceneConfigStack: this.state.sceneConfigStack.slice(0, newStackLength), - routeStack: this.state.routeStack.slice(0, newStackLength), - }); - } - }, - - _renderScene: function(route, i) { - var disabledSceneStyle = null; - var disabledScenePointerEvents = 'auto'; - if (i !== this.state.presentedIndex) { - disabledSceneStyle = styles.disabledScene; - disabledScenePointerEvents = 'none'; - } - return ( - { - this._sceneRefs[i] = scene; - }} - onStartShouldSetResponderCapture={() => { - return (this.state.transitionFromIndex != null); - }} - pointerEvents={disabledScenePointerEvents} - style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}> - {this.props.renderScene( - route, - this - )} - - ); - }, - - _renderNavigationBar: function() { - const { navigationBar } = this.props; - if (!navigationBar) { - return null; - } - return React.cloneElement(navigationBar, { - ref: (navBar) => { - this._navBar = navBar; - if (navigationBar && typeof navigationBar.ref === 'function') { - navigationBar.ref(navBar); - } - }, - navigator: this._navigationBarNavigator, - navState: this.state, - }); - }, - - _tvEventHandler: TVEventHandler, - - _enableTVEventHandler: function() { - this._tvEventHandler = new TVEventHandler(); - this._tvEventHandler.enable(this, function(cmp, evt) { - if (evt && evt.eventType === 'menu') { - cmp.pop(); - } - }); - }, - - _disableTVEventHandler: function() { - if (this._tvEventHandler) { - this._tvEventHandler.disable(); - delete this._tvEventHandler; - } - }, - - render: function() { - var newRenderedSceneMap = new Map(); - var scenes = this.state.routeStack.map((route, index) => { - var renderedScene; - if (this._renderedSceneMap.has(route) && - index !== this.state.presentedIndex) { - renderedScene = this._renderedSceneMap.get(route); - } else { - renderedScene = this._renderScene(route, index); - } - newRenderedSceneMap.set(route, renderedScene); - return renderedScene; - }); - this._renderedSceneMap = newRenderedSceneMap; - return ( - - - {scenes} - - {this._renderNavigationBar()} - - ); - }, - - _getNavigationContext: function() { - if (!this._navigationContext) { - this._navigationContext = new NavigationContext(); - } - return this._navigationContext; - } -}); - -module.exports = Navigator; diff --git a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js deleted file mode 100644 index c75f3f7b3f3857..00000000000000 --- a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBar.js +++ /dev/null @@ -1,316 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigatorBreadcrumbNavigationBar - */ -'use strict'; - -const NavigatorBreadcrumbNavigationBarStyles = require('NavigatorBreadcrumbNavigationBarStyles'); -const NavigatorNavigationBarStylesAndroid = require('NavigatorNavigationBarStylesAndroid'); -const NavigatorNavigationBarStylesIOS = require('NavigatorNavigationBarStylesIOS'); -const Platform = require('Platform'); -const React = require('React'); -const StyleSheet = require('StyleSheet'); -const View = require('View'); - -const guid = require('guid'); -const invariant = require('fbjs/lib/invariant'); - -const { Map } = require('immutable'); - -const Interpolators = NavigatorBreadcrumbNavigationBarStyles.Interpolators; -const NavigatorNavigationBarStyles = Platform.OS === 'android' ? - NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS; -const PropTypes = React.PropTypes; - -/** - * Reusable props objects. - */ -const CRUMB_PROPS = Interpolators.map(() => ({style: {}})); -const ICON_PROPS = Interpolators.map(() => ({style: {}})); -const SEPARATOR_PROPS = Interpolators.map(() => ({style: {}})); -const TITLE_PROPS = Interpolators.map(() => ({style: {}})); -const RIGHT_BUTTON_PROPS = Interpolators.map(() => ({style: {}})); - - -function navStatePresentedIndex(navState) { - if (navState.presentedIndex !== undefined) { - return navState.presentedIndex; - } - // TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS` - return navState.observedTopOfStack; -} - - -/** - * The first route is initially rendered using a different style than all - * future routes. - * - * @param {number} index Index of breadcrumb. - * @return {object} Style config for initial rendering of index. - */ -function initStyle(index, presentedIndex) { - return index === presentedIndex ? NavigatorBreadcrumbNavigationBarStyles.Center[index] : - index < presentedIndex ? NavigatorBreadcrumbNavigationBarStyles.Left[index] : - NavigatorBreadcrumbNavigationBarStyles.Right[index]; -} - -class NavigatorBreadcrumbNavigationBar extends React.Component { - static propTypes = { - navigator: PropTypes.shape({ - push: PropTypes.func, - pop: PropTypes.func, - replace: PropTypes.func, - popToRoute: PropTypes.func, - popToTop: PropTypes.func, - }), - routeMapper: PropTypes.shape({ - rightContentForRoute: PropTypes.func, - titleContentForRoute: PropTypes.func, - iconForRoute: PropTypes.func, - }), - navState: React.PropTypes.shape({ - routeStack: React.PropTypes.arrayOf(React.PropTypes.object), - presentedIndex: React.PropTypes.number, - }), - style: View.propTypes.style, - }; - - static Styles = NavigatorBreadcrumbNavigationBarStyles; - - _updateIndexProgress(progress, index, fromIndex, toIndex) { - var amount = toIndex > fromIndex ? progress : (1 - progress); - var oldDistToCenter = index - fromIndex; - var newDistToCenter = index - toIndex; - var interpolate; - invariant( - Interpolators[index], - 'Cannot find breadcrumb interpolators for ' + index - ); - if (oldDistToCenter > 0 && newDistToCenter === 0 || - newDistToCenter > 0 && oldDistToCenter === 0) { - interpolate = Interpolators[index].RightToCenter; - } else if (oldDistToCenter < 0 && newDistToCenter === 0 || - newDistToCenter < 0 && oldDistToCenter === 0) { - interpolate = Interpolators[index].CenterToLeft; - } else if (oldDistToCenter === newDistToCenter) { - interpolate = Interpolators[index].RightToCenter; - } else { - interpolate = Interpolators[index].RightToLeft; - } - - if (interpolate.Crumb(CRUMB_PROPS[index].style, amount)) { - this._setPropsIfExists('crumb_' + index, CRUMB_PROPS[index]); - } - if (interpolate.Icon(ICON_PROPS[index].style, amount)) { - this._setPropsIfExists('icon_' + index, ICON_PROPS[index]); - } - if (interpolate.Separator(SEPARATOR_PROPS[index].style, amount)) { - this._setPropsIfExists('separator_' + index, SEPARATOR_PROPS[index]); - } - if (interpolate.Title(TITLE_PROPS[index].style, amount)) { - this._setPropsIfExists('title_' + index, TITLE_PROPS[index]); - } - var right = this.refs['right_' + index]; - - const rightButtonStyle = RIGHT_BUTTON_PROPS[index].style; - if (right && interpolate.RightItem(rightButtonStyle, amount)) { - right.setNativeProps({ - style: rightButtonStyle, - pointerEvents: rightButtonStyle.opacity === 0 ? 'none' : 'auto', - }); - } - } - - updateProgress(progress, fromIndex, toIndex) { - var max = Math.max(fromIndex, toIndex); - var min = Math.min(fromIndex, toIndex); - for (var index = min; index <= max; index++) { - this._updateIndexProgress(progress, index, fromIndex, toIndex); - } - } - - onAnimationStart(fromIndex, toIndex) { - var max = Math.max(fromIndex, toIndex); - var min = Math.min(fromIndex, toIndex); - for (var index = min; index <= max; index++) { - this._setRenderViewsToHardwareTextureAndroid(index, true); - } - } - - onAnimationEnd() { - var max = this.props.navState.routeStack.length - 1; - for (var index = 0; index <= max; index++) { - this._setRenderViewsToHardwareTextureAndroid(index, false); - } - } - - _setRenderViewsToHardwareTextureAndroid(index, renderToHardwareTexture) { - var props = { - renderToHardwareTextureAndroid: renderToHardwareTexture, - }; - - this._setPropsIfExists('icon_' + index, props); - this._setPropsIfExists('separator_' + index, props); - this._setPropsIfExists('title_' + index, props); - this._setPropsIfExists('right_' + index, props); - } - - componentWillMount() { - this._reset(); - } - - render() { - var navState = this.props.navState; - var icons = navState && navState.routeStack.map(this._getBreadcrumb); - var titles = navState.routeStack.map(this._getTitle); - var buttons = navState.routeStack.map(this._getRightButton); - - return ( - - {titles} - {icons} - {buttons} - - ); - } - - immediatelyRefresh() { - this._reset(); - this.forceUpdate(); - } - - _reset() { - this._key = guid(); - this._descriptors = { - title: new Map(), - right: new Map(), - }; - } - - _getBreadcrumb = (route, index) => { - /** - * To prevent the case where titles on an empty navigation stack covers the first icon and - * becomes partially unpressable, we set the first breadcrumb to be unpressable by default, and - * make it pressable when there are multiple items in the stack. - */ - const pointerEvents = ( - (this.props.navState.routeStack.length <= 1 && index === 0) ? - 'none' : - 'auto' - ); - const navBarRouteMapper = this.props.routeMapper; - const firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); - - var breadcrumbDescriptor = ( - - - {navBarRouteMapper.iconForRoute(route, this.props.navigator)} - - - {navBarRouteMapper.separatorForRoute(route, this.props.navigator)} - - - ); - - return breadcrumbDescriptor; - }; - - _getTitle = (route, index) => { - if (this._descriptors.title.has(route)) { - return this._descriptors.title.get(route); - } - - var titleContent = this.props.routeMapper.titleContentForRoute( - this.props.navState.routeStack[index], - this.props.navigator - ); - var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); - - var titleDescriptor = ( - - {titleContent} - - ); - this._descriptors.title = this._descriptors.title.set(route, titleDescriptor); - return titleDescriptor; - }; - - _getRightButton = (route, index) => { - if (this._descriptors.right.has(route)) { - return this._descriptors.right.get(route); - } - var rightContent = this.props.routeMapper.rightContentForRoute( - this.props.navState.routeStack[index], - this.props.navigator - ); - if (!rightContent) { - this._descriptors.right = this._descriptors.right.set(route, null); - return null; - } - var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); - var rightButtonDescriptor = ( - - {rightContent} - - ); - this._descriptors.right = this._descriptors.right.set(route, rightButtonDescriptor); - return rightButtonDescriptor; - }; - - _setPropsIfExists(ref, props) { - var ref = this.refs[ref]; - ref && ref.setNativeProps(props); - } -} - -const styles = StyleSheet.create({ - breadCrumbContainer: { - overflow: 'hidden', - position: 'absolute', - height: NavigatorNavigationBarStyles.General.TotalNavHeight, - top: 0, - left: 0, - right: 0, - }, -}); - -module.exports = NavigatorBreadcrumbNavigationBar; diff --git a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.android.js b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.android.js deleted file mode 100644 index 7ff687b49da6e6..00000000000000 --- a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.android.js +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigatorBreadcrumbNavigationBarStyles - */ -'use strict'; - -var NavigatorNavigationBarStylesAndroid = require('NavigatorNavigationBarStylesAndroid'); - -var buildStyleInterpolator = require('buildStyleInterpolator'); -var merge = require('merge'); - -var NAV_BAR_HEIGHT = NavigatorNavigationBarStylesAndroid.General.NavBarHeight; - -var SPACING = 8; -var ICON_WIDTH = 40; -var SEPARATOR_WIDTH = 9; -var CRUMB_WIDTH = ICON_WIDTH + SEPARATOR_WIDTH; -var NAV_ELEMENT_HEIGHT = NAV_BAR_HEIGHT; - -var OPACITY_RATIO = 100; -var ICON_INACTIVE_OPACITY = 0.6; -var MAX_BREADCRUMBS = 10; - -var CRUMB_BASE = { - position: 'absolute', - flexDirection: 'row', - top: 0, - width: CRUMB_WIDTH, - height: NAV_ELEMENT_HEIGHT, - backgroundColor: 'transparent', -}; - -var ICON_BASE = { - width: ICON_WIDTH, - height: NAV_ELEMENT_HEIGHT, -}; - -var SEPARATOR_BASE = { - width: SEPARATOR_WIDTH, - height: NAV_ELEMENT_HEIGHT, -}; - -var TITLE_BASE = { - position: 'absolute', - top: 0, - height: NAV_ELEMENT_HEIGHT, - backgroundColor: 'transparent', - alignItems: 'flex-start', -}; - -var FIRST_TITLE_BASE = merge(TITLE_BASE, { - left: 0, - right: 0, -}); - -var RIGHT_BUTTON_BASE = { - position: 'absolute', - top: 0, - right: 0, - overflow: 'hidden', - opacity: 1, - height: NAV_ELEMENT_HEIGHT, - backgroundColor: 'transparent', -}; - -/** - * Precompute crumb styles so that they don't need to be recomputed on every - * interaction. - */ -var LEFT = []; -var CENTER = []; -var RIGHT = []; -for (var i = 0; i < MAX_BREADCRUMBS; i++) { - var crumbLeft = CRUMB_WIDTH * i + SPACING; - LEFT[i] = { - Crumb: merge(CRUMB_BASE, { left: crumbLeft }), - Icon: merge(ICON_BASE, { opacity: ICON_INACTIVE_OPACITY }), - Separator: merge(SEPARATOR_BASE, { opacity: 1 }), - Title: merge(TITLE_BASE, { left: crumbLeft, opacity: 0 }), - RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }), - }; - CENTER[i] = { - Crumb: merge(CRUMB_BASE, { left: crumbLeft }), - Icon: merge(ICON_BASE, { opacity: 1 }), - Separator: merge(SEPARATOR_BASE, { opacity: 0 }), - Title: merge(TITLE_BASE, { - left: crumbLeft + ICON_WIDTH, - opacity: 1, - }), - RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 1 }), - }; - var crumbRight = crumbLeft + 50; - RIGHT[i] = { - Crumb: merge(CRUMB_BASE, { left: crumbRight}), - Icon: merge(ICON_BASE, { opacity: 0 }), - Separator: merge(SEPARATOR_BASE, { opacity: 0 }), - Title: merge(TITLE_BASE, { - left: crumbRight + ICON_WIDTH, - opacity: 0, - }), - RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }), - }; -} - -// Special case the CENTER state of the first scene. -CENTER[0] = { - Crumb: merge(CRUMB_BASE, {left: SPACING + CRUMB_WIDTH}), - Icon: merge(ICON_BASE, {opacity: 0}), - Separator: merge(SEPARATOR_BASE, {opacity: 0}), - Title: merge(FIRST_TITLE_BASE, {opacity: 1}), - RightItem: CENTER[0].RightItem, -}; -LEFT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0}); -RIGHT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0}); - - -var buildIndexSceneInterpolator = function(startStyles, endStyles) { - return { - Crumb: buildStyleInterpolator({ - left: { - type: 'linear', - from: startStyles.Crumb.left, - to: endStyles.Crumb.left, - min: 0, - max: 1, - extrapolate: true, - }, - }), - Icon: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.Icon.opacity, - to: endStyles.Icon.opacity, - min: 0, - max: 1, - }, - }), - Separator: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.Separator.opacity, - to: endStyles.Separator.opacity, - min: 0, - max: 1, - }, - }), - Title: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.Title.opacity, - to: endStyles.Title.opacity, - min: 0, - max: 1, - }, - left: { - type: 'linear', - from: startStyles.Title.left, - to: endStyles.Title.left, - min: 0, - max: 1, - extrapolate: true, - }, - }), - RightItem: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.RightItem.opacity, - to: endStyles.RightItem.opacity, - min: 0, - max: 1, - round: OPACITY_RATIO, - }, - }), - }; -}; - -var Interpolators = CENTER.map(function(_, ii) { - return { - // Animating *into* the center stage from the right - RightToCenter: buildIndexSceneInterpolator(RIGHT[ii], CENTER[ii]), - // Animating out of the center stage, to the left - CenterToLeft: buildIndexSceneInterpolator(CENTER[ii], LEFT[ii]), - // Both stages (animating *past* the center stage) - RightToLeft: buildIndexSceneInterpolator(RIGHT[ii], LEFT[ii]), - }; -}); - -/** - * Contains constants that are used in constructing both `StyleSheet`s and - * inline styles during transitions. - */ -module.exports = { - Interpolators, - Left: LEFT, - Center: CENTER, - Right: RIGHT, - IconWidth: ICON_WIDTH, - IconHeight: NAV_BAR_HEIGHT, - SeparatorWidth: SEPARATOR_WIDTH, - SeparatorHeight: NAV_BAR_HEIGHT, -}; diff --git a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.ios.js b/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.ios.js deleted file mode 100644 index eea15eee9bec52..00000000000000 --- a/Libraries/CustomComponents/Navigator/NavigatorBreadcrumbNavigationBarStyles.ios.js +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigatorBreadcrumbNavigationBarStyles - */ -'use strict'; - -var Dimensions = require('Dimensions'); -var NavigatorNavigationBarStylesIOS = require('NavigatorNavigationBarStylesIOS'); - -var buildStyleInterpolator = require('buildStyleInterpolator'); -var merge = require('merge'); - -var SCREEN_WIDTH = Dimensions.get('window').width; -var STATUS_BAR_HEIGHT = NavigatorNavigationBarStylesIOS.General.StatusBarHeight; -var NAV_BAR_HEIGHT = NavigatorNavigationBarStylesIOS.General.NavBarHeight; - -var SPACING = 4; -var ICON_WIDTH = 40; -var SEPARATOR_WIDTH = 9; -var CRUMB_WIDTH = ICON_WIDTH + SEPARATOR_WIDTH; - -var OPACITY_RATIO = 100; -var ICON_INACTIVE_OPACITY = 0.6; -var MAX_BREADCRUMBS = 10; - -var CRUMB_BASE = { - position: 'absolute', - flexDirection: 'row', - top: STATUS_BAR_HEIGHT, - width: CRUMB_WIDTH, - height: NAV_BAR_HEIGHT, - backgroundColor: 'transparent', -}; - -var ICON_BASE = { - width: ICON_WIDTH, - height: NAV_BAR_HEIGHT, -}; - -var SEPARATOR_BASE = { - width: SEPARATOR_WIDTH, - height: NAV_BAR_HEIGHT, -}; - -var TITLE_BASE = { - position: 'absolute', - top: STATUS_BAR_HEIGHT, - height: NAV_BAR_HEIGHT, - backgroundColor: 'transparent', -}; - -// For first title styles, make sure first title is centered -var FIRST_TITLE_BASE = merge(TITLE_BASE, { - left: 0, - right: 0, - alignItems: 'center', - height: NAV_BAR_HEIGHT, -}); - -var RIGHT_BUTTON_BASE = { - position: 'absolute', - top: STATUS_BAR_HEIGHT, - right: SPACING, - overflow: 'hidden', - opacity: 1, - height: NAV_BAR_HEIGHT, - backgroundColor: 'transparent', -}; - -/** - * Precompute crumb styles so that they don't need to be recomputed on every - * interaction. - */ -var LEFT = []; -var CENTER = []; -var RIGHT = []; -for (var i = 0; i < MAX_BREADCRUMBS; i++) { - var crumbLeft = CRUMB_WIDTH * i + SPACING; - LEFT[i] = { - Crumb: merge(CRUMB_BASE, { left: crumbLeft }), - Icon: merge(ICON_BASE, { opacity: ICON_INACTIVE_OPACITY }), - Separator: merge(SEPARATOR_BASE, { opacity: 1 }), - Title: merge(TITLE_BASE, { left: crumbLeft, opacity: 0 }), - RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }), - }; - CENTER[i] = { - Crumb: merge(CRUMB_BASE, { left: crumbLeft }), - Icon: merge(ICON_BASE, { opacity: 1 }), - Separator: merge(SEPARATOR_BASE, { opacity: 0 }), - Title: merge(TITLE_BASE, { - left: crumbLeft + ICON_WIDTH, - opacity: 1, - }), - RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 1 }), - }; - var crumbRight = SCREEN_WIDTH - 100; - RIGHT[i] = { - Crumb: merge(CRUMB_BASE, { left: crumbRight}), - Icon: merge(ICON_BASE, { opacity: 0 }), - Separator: merge(SEPARATOR_BASE, { opacity: 0 }), - Title: merge(TITLE_BASE, { - left: crumbRight + ICON_WIDTH, - opacity: 0, - }), - RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }), - }; -} - -// Special case the CENTER state of the first scene. -CENTER[0] = { - Crumb: merge(CRUMB_BASE, {left: SCREEN_WIDTH / 4}), - Icon: merge(ICON_BASE, {opacity: 0}), - Separator: merge(SEPARATOR_BASE, {opacity: 0}), - Title: merge(FIRST_TITLE_BASE, {opacity: 1}), - RightItem: CENTER[0].RightItem, -}; -LEFT[0].Title = merge(FIRST_TITLE_BASE, {left: -SCREEN_WIDTH / 4, opacity: 0}); -RIGHT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0}); - - -var buildIndexSceneInterpolator = function(startStyles, endStyles) { - return { - Crumb: buildStyleInterpolator({ - left: { - type: 'linear', - from: startStyles.Crumb.left, - to: endStyles.Crumb.left, - min: 0, - max: 1, - extrapolate: true, - }, - }), - Icon: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.Icon.opacity, - to: endStyles.Icon.opacity, - min: 0, - max: 1, - }, - }), - Separator: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.Separator.opacity, - to: endStyles.Separator.opacity, - min: 0, - max: 1, - }, - }), - Title: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.Title.opacity, - to: endStyles.Title.opacity, - min: 0, - max: 1, - }, - left: { - type: 'linear', - from: startStyles.Title.left, - to: endStyles.Title.left, - min: 0, - max: 1, - extrapolate: true, - }, - }), - RightItem: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.RightItem.opacity, - to: endStyles.RightItem.opacity, - min: 0, - max: 1, - round: OPACITY_RATIO, - }, - }), - }; -}; - -var Interpolators = CENTER.map(function(_, ii) { - return { - // Animating *into* the center stage from the right - RightToCenter: buildIndexSceneInterpolator(RIGHT[ii], CENTER[ii]), - // Animating out of the center stage, to the left - CenterToLeft: buildIndexSceneInterpolator(CENTER[ii], LEFT[ii]), - // Both stages (animating *past* the center stage) - RightToLeft: buildIndexSceneInterpolator(RIGHT[ii], LEFT[ii]), - }; -}); - -/** - * Contains constants that are used in constructing both `StyleSheet`s and - * inline styles during transitions. - */ -module.exports = { - Interpolators, - Left: LEFT, - Center: CENTER, - Right: RIGHT, - IconWidth: ICON_WIDTH, - IconHeight: NAV_BAR_HEIGHT, - SeparatorWidth: SEPARATOR_WIDTH, - SeparatorHeight: NAV_BAR_HEIGHT, -}; diff --git a/Libraries/CustomComponents/Navigator/NavigatorNavigationBar.js b/Libraries/CustomComponents/Navigator/NavigatorNavigationBar.js deleted file mode 100644 index 7a39b18213f323..00000000000000 --- a/Libraries/CustomComponents/Navigator/NavigatorNavigationBar.js +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigatorNavigationBar - */ -'use strict'; - -var React = require('React'); -var NavigatorNavigationBarStylesAndroid = require('NavigatorNavigationBarStylesAndroid'); -var NavigatorNavigationBarStylesIOS = require('NavigatorNavigationBarStylesIOS'); -var Platform = require('Platform'); -var StyleSheet = require('StyleSheet'); -var View = require('View'); - -var guid = require('guid'); - -var { Map } = require('immutable'); - -var COMPONENT_NAMES = ['Title', 'LeftButton', 'RightButton']; - -var NavigatorNavigationBarStyles = Platform.OS === 'android' ? - NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS; - -var navStatePresentedIndex = function(navState) { - if (navState.presentedIndex !== undefined) { - return navState.presentedIndex; - } - // TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS` - return navState.observedTopOfStack; -}; - -class NavigatorNavigationBar extends React.Component { - static propTypes = { - navigator: React.PropTypes.object, - routeMapper: React.PropTypes.shape({ - Title: React.PropTypes.func.isRequired, - LeftButton: React.PropTypes.func.isRequired, - RightButton: React.PropTypes.func.isRequired, - }).isRequired, - navState: React.PropTypes.shape({ - routeStack: React.PropTypes.arrayOf(React.PropTypes.object), - presentedIndex: React.PropTypes.number, - }), - navigationStyles: React.PropTypes.object, - style: View.propTypes.style, - }; - - static Styles = NavigatorNavigationBarStyles; - static StylesAndroid = NavigatorNavigationBarStylesAndroid; - static StylesIOS = NavigatorNavigationBarStylesIOS; - - static defaultProps = { - navigationStyles: NavigatorNavigationBarStyles, - }; - - componentWillMount() { - this._reset(); - } - - /** - * Stop transtion, immediately resets the cached state and re-render the - * whole view. - */ - immediatelyRefresh = () => { - this._reset(); - this.forceUpdate(); - }; - - _reset = () => { - this._key = guid(); - this._reusableProps = {}; - this._components = {}; - this._descriptors = {}; - - COMPONENT_NAMES.forEach(componentName => { - this._components[componentName] = new Map(); - this._descriptors[componentName] = new Map(); - }); - }; - - _getReusableProps = (/*string*/componentName, /*number*/index) => /*object*/ { - var propStack = this._reusableProps[componentName]; - if (!propStack) { - propStack = this._reusableProps[componentName] = []; - } - var props = propStack[index]; - if (!props) { - props = propStack[index] = {style:{}}; - } - return props; - }; - - _updateIndexProgress = ( - /*number*/progress, - /*number*/index, - /*number*/fromIndex, - /*number*/toIndex, - ) => { - var amount = toIndex > fromIndex ? progress : (1 - progress); - var oldDistToCenter = index - fromIndex; - var newDistToCenter = index - toIndex; - var interpolate; - if (oldDistToCenter > 0 && newDistToCenter === 0 || - newDistToCenter > 0 && oldDistToCenter === 0) { - interpolate = this.props.navigationStyles.Interpolators.RightToCenter; - } else if (oldDistToCenter < 0 && newDistToCenter === 0 || - newDistToCenter < 0 && oldDistToCenter === 0) { - interpolate = this.props.navigationStyles.Interpolators.CenterToLeft; - } else if (oldDistToCenter === newDistToCenter) { - interpolate = this.props.navigationStyles.Interpolators.RightToCenter; - } else { - interpolate = this.props.navigationStyles.Interpolators.RightToLeft; - } - - COMPONENT_NAMES.forEach(function (componentName) { - var component = this._components[componentName].get(this.props.navState.routeStack[index]); - var props = this._getReusableProps(componentName, index); - if (component && interpolate[componentName](props.style, amount)) { - props.pointerEvents = props.style.opacity === 0 ? 'none' : 'box-none'; - component.setNativeProps(props); - } - }, this); - }; - - updateProgress = (/*number*/progress, /*number*/fromIndex, /*number*/toIndex) => { - var max = Math.max(fromIndex, toIndex); - var min = Math.min(fromIndex, toIndex); - for (var index = min; index <= max; index++) { - this._updateIndexProgress(progress, index, fromIndex, toIndex); - } - }; - - render() { - var navBarStyle = { - height: this.props.navigationStyles.General.TotalNavHeight, - }; - var navState = this.props.navState; - var components = navState.routeStack.map((route, index) => - COMPONENT_NAMES.map(componentName => - this._getComponent(componentName, route, index) - ) - ); - - return ( - - {components} - - ); - } - - _getComponent = (/*string*/componentName, /*object*/route, /*number*/index) => /*?Object*/ { - if (this._descriptors[componentName].includes(route)) { - return this._descriptors[componentName].get(route); - } - - var rendered = null; - - var content = this.props.routeMapper[componentName]( - this.props.navState.routeStack[index], - this.props.navigator, - index, - this.props.navState - ); - if (!content) { - return null; - } - - var componentIsActive = index === navStatePresentedIndex(this.props.navState); - var initialStage = componentIsActive ? - this.props.navigationStyles.Stages.Center : - this.props.navigationStyles.Stages.Left; - rendered = ( - { - this._components[componentName] = this._components[componentName].set(route, ref); - }} - pointerEvents={componentIsActive ? 'box-none' : 'none'} - style={initialStage[componentName]}> - {content} - - ); - - this._descriptors[componentName] = this._descriptors[componentName].set(route, rendered); - return rendered; - }; -} - - -var styles = StyleSheet.create({ - navBarContainer: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - backgroundColor: 'transparent', - }, -}); - -module.exports = NavigatorNavigationBar; diff --git a/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStylesAndroid.js b/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStylesAndroid.js deleted file mode 100644 index 42db9a9d926848..00000000000000 --- a/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStylesAndroid.js +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigatorNavigationBarStylesAndroid - */ -'use strict'; - -var buildStyleInterpolator = require('buildStyleInterpolator'); -var merge = require('merge'); - -// Android Material Design -var NAV_BAR_HEIGHT = 56; -var TITLE_LEFT = 72; -var BUTTON_SIZE = 24; -var TOUCH_TARGT_SIZE = 48; -var BUTTON_HORIZONTAL_MARGIN = 16; - -var BUTTON_EFFECTIVE_MARGIN = BUTTON_HORIZONTAL_MARGIN - (TOUCH_TARGT_SIZE - BUTTON_SIZE) / 2; -var NAV_ELEMENT_HEIGHT = NAV_BAR_HEIGHT; - -var BASE_STYLES = { - Title: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - alignItems: 'flex-start', - height: NAV_ELEMENT_HEIGHT, - backgroundColor: 'transparent', - marginLeft: TITLE_LEFT, - }, - LeftButton: { - position: 'absolute', - top: 0, - left: BUTTON_EFFECTIVE_MARGIN, - overflow: 'hidden', - height: NAV_ELEMENT_HEIGHT, - backgroundColor: 'transparent', - }, - RightButton: { - position: 'absolute', - top: 0, - right: BUTTON_EFFECTIVE_MARGIN, - overflow: 'hidden', - alignItems: 'flex-end', - height: NAV_ELEMENT_HEIGHT, - backgroundColor: 'transparent', - }, -}; - -// There are 3 stages: left, center, right. All previous navigation -// items are in the left stage. The current navigation item is in the -// center stage. All upcoming navigation items are in the right stage. -// Another way to think of the stages is in terms of transitions. When -// we move forward in the navigation stack, we perform a -// right-to-center transition on the new navigation item and a -// center-to-left transition on the current navigation item. -var Stages = { - Left: { - Title: merge(BASE_STYLES.Title, { opacity: 0 }), - LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 0 }), - RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }), - }, - Center: { - Title: merge(BASE_STYLES.Title, { opacity: 1 }), - LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 1 }), - RightButton: merge(BASE_STYLES.RightButton, { opacity: 1 }), - }, - Right: { - Title: merge(BASE_STYLES.Title, { opacity: 0 }), - LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 0 }), - RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }), - }, -}; - - -var opacityRatio = 100; - -function buildSceneInterpolators(startStyles, endStyles) { - return { - Title: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.Title.opacity, - to: endStyles.Title.opacity, - min: 0, - max: 1, - }, - left: { - type: 'linear', - from: startStyles.Title.left, - to: endStyles.Title.left, - min: 0, - max: 1, - extrapolate: true, - }, - }), - LeftButton: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.LeftButton.opacity, - to: endStyles.LeftButton.opacity, - min: 0, - max: 1, - round: opacityRatio, - }, - left: { - type: 'linear', - from: startStyles.LeftButton.left, - to: endStyles.LeftButton.left, - min: 0, - max: 1, - }, - }), - RightButton: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.RightButton.opacity, - to: endStyles.RightButton.opacity, - min: 0, - max: 1, - round: opacityRatio, - }, - left: { - type: 'linear', - from: startStyles.RightButton.left, - to: endStyles.RightButton.left, - min: 0, - max: 1, - extrapolate: true, - }, - }), - }; -} - -var Interpolators = { - // Animating *into* the center stage from the right - RightToCenter: buildSceneInterpolators(Stages.Right, Stages.Center), - // Animating out of the center stage, to the left - CenterToLeft: buildSceneInterpolators(Stages.Center, Stages.Left), - // Both stages (animating *past* the center stage) - RightToLeft: buildSceneInterpolators(Stages.Right, Stages.Left), -}; - - -module.exports = { - General: { - NavBarHeight: NAV_BAR_HEIGHT, - StatusBarHeight: 0, - TotalNavHeight: NAV_BAR_HEIGHT, - }, - Interpolators, - Stages, -}; diff --git a/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStylesIOS.js b/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStylesIOS.js deleted file mode 100644 index 8163d49674a7f0..00000000000000 --- a/Libraries/CustomComponents/Navigator/NavigatorNavigationBarStylesIOS.js +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigatorNavigationBarStylesIOS - */ -'use strict'; - -var Dimensions = require('Dimensions'); - -var buildStyleInterpolator = require('buildStyleInterpolator'); -var merge = require('merge'); - -var SCREEN_WIDTH = Dimensions.get('window').width; -var NAV_BAR_HEIGHT = 44; -var STATUS_BAR_HEIGHT = 20; -var NAV_HEIGHT = NAV_BAR_HEIGHT + STATUS_BAR_HEIGHT; - -var BASE_STYLES = { - Title: { - position: 'absolute', - top: STATUS_BAR_HEIGHT, - left: 0, - right: 0, - alignItems: 'center', - height: NAV_BAR_HEIGHT, - backgroundColor: 'transparent', - }, - LeftButton: { - position: 'absolute', - top: STATUS_BAR_HEIGHT, - left: 0, - overflow: 'hidden', - opacity: 1, - height: NAV_BAR_HEIGHT, - backgroundColor: 'transparent', - }, - RightButton: { - position: 'absolute', - top: STATUS_BAR_HEIGHT, - right: 0, - overflow: 'hidden', - opacity: 1, - alignItems: 'flex-end', - height: NAV_BAR_HEIGHT, - backgroundColor: 'transparent', - }, -}; - -// There are 3 stages: left, center, right. All previous navigation -// items are in the left stage. The current navigation item is in the -// center stage. All upcoming navigation items are in the right stage. -// Another way to think of the stages is in terms of transitions. When -// we move forward in the navigation stack, we perform a -// right-to-center transition on the new navigation item and a -// center-to-left transition on the current navigation item. -var Stages = { - Left: { - Title: merge(BASE_STYLES.Title, { left: -SCREEN_WIDTH / 2, opacity: 0 }), - LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 0 }), - RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }), - }, - Center: { - Title: merge(BASE_STYLES.Title, { left: 0, opacity: 1 }), - LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 1 }), - RightButton: merge(BASE_STYLES.RightButton, { opacity: 1 }), - }, - Right: { - Title: merge(BASE_STYLES.Title, { left: SCREEN_WIDTH / 2, opacity: 0 }), - LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 0 }), - RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }), - }, -}; - - -var opacityRatio = 100; - -function buildSceneInterpolators(startStyles, endStyles) { - return { - Title: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.Title.opacity, - to: endStyles.Title.opacity, - min: 0, - max: 1, - }, - left: { - type: 'linear', - from: startStyles.Title.left, - to: endStyles.Title.left, - min: 0, - max: 1, - extrapolate: true, - }, - }), - LeftButton: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.LeftButton.opacity, - to: endStyles.LeftButton.opacity, - min: 0, - max: 1, - round: opacityRatio, - }, - left: { - type: 'linear', - from: startStyles.LeftButton.left, - to: endStyles.LeftButton.left, - min: 0, - max: 1, - }, - }), - RightButton: buildStyleInterpolator({ - opacity: { - type: 'linear', - from: startStyles.RightButton.opacity, - to: endStyles.RightButton.opacity, - min: 0, - max: 1, - round: opacityRatio, - }, - left: { - type: 'linear', - from: startStyles.RightButton.left, - to: endStyles.RightButton.left, - min: 0, - max: 1, - extrapolate: true, - }, - }), - }; -} - -var Interpolators = { - // Animating *into* the center stage from the right - RightToCenter: buildSceneInterpolators(Stages.Right, Stages.Center), - // Animating out of the center stage, to the left - CenterToLeft: buildSceneInterpolators(Stages.Center, Stages.Left), - // Both stages (animating *past* the center stage) - RightToLeft: buildSceneInterpolators(Stages.Right, Stages.Left), -}; - - -module.exports = { - General: { - NavBarHeight: NAV_BAR_HEIGHT, - StatusBarHeight: STATUS_BAR_HEIGHT, - TotalNavHeight: NAV_HEIGHT, - }, - Interpolators, - Stages, -}; diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js deleted file mode 100644 index eeacd4993a05f2..00000000000000 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ /dev/null @@ -1,788 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @providesModule NavigatorSceneConfigs - */ -'use strict'; - -var Dimensions = require('Dimensions'); -var I18nManager = require('I18nManager'); -var PixelRatio = require('PixelRatio'); - -var buildStyleInterpolator = require('buildStyleInterpolator'); - -var IS_RTL = I18nManager.isRTL; - -var SCREEN_WIDTH = Dimensions.get('window').width; -var SCREEN_HEIGHT = Dimensions.get('window').height; -var PIXEL_RATIO = PixelRatio.get(); - -var ToTheLeftIOS = { - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: -SCREEN_WIDTH * 0.3, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - opacity: { - value: 1.0, - type: 'constant', - }, -}; - -var ToTheRightIOS = { - ...ToTheLeftIOS, - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: SCREEN_WIDTH * 0.3, y: 0, z: 0}, - }, -}; - -var FadeToTheLeft = { - // Rotate *requires* you to break out each individual component of - // rotation (x, y, z, w) - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: -Math.round(SCREEN_WIDTH * 0.3), y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - // Uncomment to try rotation: - // Quick guide to reasoning about rotations: - // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/#Quaternions - // transformRotateRadians: { - // from: {x: 0, y: 0, z: 0, w: 1}, - // to: {x: 0, y: 0, z: -0.47, w: 0.87}, - // min: 0, - // max: 1, - // type: 'linear', - // extrapolate: true - // }, - transformScale: { - from: {x: 1, y: 1, z: 1}, - to: {x: 0.95, y: 0.95, z: 1}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true - }, - opacity: { - from: 1, - to: 0.3, - min: 0, - max: 1, - type: 'linear', - extrapolate: false, - round: 100, - }, - translateX: { - from: 0, - to: -Math.round(SCREEN_WIDTH * 0.3), - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - scaleX: { - from: 1, - to: 0.95, - min: 0, - max: 1, - type: 'linear', - extrapolate: true - }, - scaleY: { - from: 1, - to: 0.95, - min: 0, - max: 1, - type: 'linear', - extrapolate: true - }, -}; - -var FadeToTheRight = { - ...FadeToTheLeft, - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: Math.round(SCREEN_WIDTH * 0.3), y: 0, z: 0}, - }, - translateX: { - from: 0, - to: Math.round(SCREEN_WIDTH * 0.3), - }, -}; - -var FadeIn = { - opacity: { - from: 0, - to: 1, - min: 0.5, - max: 1, - type: 'linear', - extrapolate: false, - round: 100, - }, -}; - -var FadeOut = { - opacity: { - from: 1, - to: 0, - min: 0, - max: 0.5, - type: 'linear', - extrapolate: false, - round: 100, - }, -}; - -var ToTheLeft = { - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: -SCREEN_WIDTH, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - opacity: { - value: 1.0, - type: 'constant', - }, - - translateX: { - from: 0, - to: -SCREEN_WIDTH, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, -}; - -var ToTheRight = { - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: SCREEN_WIDTH, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - opacity: { - value: 1.0, - type: 'constant', - }, - - translateX: { - from: 0, - to: SCREEN_WIDTH, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, -}; - -var ToTheUp = { - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: 0, y: -SCREEN_HEIGHT, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - opacity: { - value: 1.0, - type: 'constant', - }, - translateY: { - from: 0, - to: -SCREEN_HEIGHT, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, -}; - -var ToTheDown = { - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: 0, y: SCREEN_HEIGHT, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - opacity: { - value: 1.0, - type: 'constant', - }, - translateY: { - from: 0, - to: SCREEN_HEIGHT, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, -}; - -var FromTheRight = { - opacity: { - value: 1.0, - type: 'constant', - }, - - transformTranslate: { - from: {x: SCREEN_WIDTH, y: 0, z: 0}, - to: {x: 0, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - - translateX: { - from: SCREEN_WIDTH, - to: 0, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - - scaleX: { - value: 1, - type: 'constant', - }, - scaleY: { - value: 1, - type: 'constant', - }, -}; - -var FromTheLeft = { - ...FromTheRight, - transformTranslate: { - from: {x: -SCREEN_WIDTH, y: 0, z: 0}, - to: {x: 0, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - translateX: { - from: -SCREEN_WIDTH, - to: 0, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, -}; - -var FromTheDown = { - ...FromTheRight, - transformTranslate: { - from: {y: SCREEN_HEIGHT, x: 0, z: 0}, - to: {x: 0, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - translateY: { - from: SCREEN_HEIGHT, - to: 0, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, -}; - -var FromTheTop = { - ...FromTheRight, - transformTranslate: { - from: {y: -SCREEN_HEIGHT, x: 0, z: 0}, - to: {x: 0, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - translateY: { - from: -SCREEN_HEIGHT, - to: 0, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, -}; - -var ToTheBack = { - // Rotate *requires* you to break out each individual component of - // rotation (x, y, z, w) - transformTranslate: { - from: {x: 0, y: 0, z: 0}, - to: {x: 0, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - transformScale: { - from: {x: 1, y: 1, z: 1}, - to: {x: 0.95, y: 0.95, z: 1}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true - }, - opacity: { - from: 1, - to: 0.3, - min: 0, - max: 1, - type: 'linear', - extrapolate: false, - round: 100, - }, - scaleX: { - from: 1, - to: 0.95, - min: 0, - max: 1, - type: 'linear', - extrapolate: true - }, - scaleY: { - from: 1, - to: 0.95, - min: 0, - max: 1, - type: 'linear', - extrapolate: true - }, -}; - -var FromTheFront = { - opacity: { - value: 1.0, - type: 'constant', - }, - - transformTranslate: { - from: {x: 0, y: SCREEN_HEIGHT, z: 0}, - to: {x: 0, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - translateY: { - from: SCREEN_HEIGHT, - to: 0, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - scaleX: { - value: 1, - type: 'constant', - }, - scaleY: { - value: 1, - type: 'constant', - }, -}; - -var ToTheBackAndroid = { - opacity: { - value: 1, - type: 'constant', - }, -}; - -var FromTheFrontAndroid = { - opacity: { - from: 0, - to: 1, - min: 0.5, - max: 1, - type: 'linear', - extrapolate: false, - round: 100, - }, - transformTranslate: { - from: {x: 0, y: 100, z: 0}, - to: {x: 0, y: 0, z: 0}, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, - translateY: { - from: 100, - to: 0, - min: 0, - max: 1, - type: 'linear', - extrapolate: true, - round: PIXEL_RATIO, - }, -}; - -var BaseOverswipeConfig = { - frictionConstant: 1, - frictionByDistance: 1.5, -}; - -var BaseLeftToRightGesture = { - - // If the gesture can end and restart during one continuous touch - isDetachable: false, - - // How far the swipe must drag to start transitioning - gestureDetectMovement: 2, - - // Amplitude of release velocity that is considered still - notMoving: 0.3, - - // Fraction of directional move required. - directionRatio: 0.66, - - // Velocity to transition with when the gesture release was "not moving" - snapVelocity: 2, - - // Region that can trigger swipe. iOS default is 30px from the left edge - edgeHitWidth: 30, - - // Ratio of gesture completion when non-velocity release will cause action - stillCompletionRatio: 3 / 5, - - fullDistance: SCREEN_WIDTH, - - direction: 'left-to-right', - -}; - -var BaseRightToLeftGesture = { - ...BaseLeftToRightGesture, - direction: 'right-to-left', -}; - -var BaseDownUpGesture = { - ...BaseLeftToRightGesture, - fullDistance: SCREEN_HEIGHT, - direction: 'bottom-to-top', -}; - -var BaseUpDownGesture = { - ...BaseLeftToRightGesture, - fullDistance: SCREEN_HEIGHT, - direction: 'top-to-bottom', -}; - -// For RTL experiment, we need to swap all the Left and Right gesture and animation. -// So we create a direction mapping for both LTR and RTL, and change left/right to start/end. -let directionMapping = { - ToTheStartIOS: ToTheLeftIOS, - ToTheEndIOS: ToTheRightIOS, - FadeToTheStart: FadeToTheLeft, - FadeToTheEnd: FadeToTheRight, - ToTheStart: ToTheLeft, - ToTheEnd: ToTheRight, - FromTheStart: FromTheLeft, - FromTheEnd: FromTheRight, - BaseStartToEndGesture: BaseLeftToRightGesture, - BaseEndToStartGesture: BaseRightToLeftGesture, -}; - -if (IS_RTL) { - directionMapping = { - ToTheStartIOS: ToTheRightIOS, - ToTheEndIOS: ToTheLeftIOS, - FadeToTheStart: FadeToTheRight, - FadeToTheEnd: FadeToTheLeft, - ToTheStart: ToTheRight, - ToTheEnd: ToTheLeft, - FromTheStart: FromTheRight, - FromTheEnd: FromTheLeft, - BaseStartToEndGesture: BaseRightToLeftGesture, - BaseEndToStartGesture: BaseLeftToRightGesture, - }; -} - -var BaseConfig = { - // A list of all gestures that are enabled on this scene - gestures: { - pop: directionMapping.BaseStartToEndGesture, - }, - - // Rebound spring parameters when transitioning FROM this scene - springFriction: 26, - springTension: 200, - - // Velocity to start at when transitioning without gesture - defaultTransitionVelocity: 1.5, - - // Animation interpolators for horizontal transitioning: - animationInterpolators: { - into: buildStyleInterpolator(directionMapping.FromTheEnd), - out: buildStyleInterpolator(directionMapping.FadeToTheStart), - }, -}; - -var NavigatorSceneConfigs = { - PushFromRight: { - ...BaseConfig, - animationInterpolators: { - into: buildStyleInterpolator(directionMapping.FromTheEnd), - out: buildStyleInterpolator(directionMapping.ToTheStartIOS), - }, - }, - PushFromLeft: { - ...BaseConfig, - animationInterpolators: { - into: buildStyleInterpolator(directionMapping.FromTheStart), - out: buildStyleInterpolator(directionMapping.ToTheEndIOS), - }, - }, - FloatFromRight: { - ...BaseConfig, - // We will want to customize this soon - }, - FloatFromLeft: { - ...BaseConfig, - gestures: { - pop: directionMapping.BaseEndToStartGesture, - }, - animationInterpolators: { - into: buildStyleInterpolator(directionMapping.FromTheStart), - out: buildStyleInterpolator(directionMapping.FadeToTheEnd), - }, - }, - FloatFromBottom: { - ...BaseConfig, - gestures: { - pop: { - ...directionMapping.BaseStartToEndGesture, - edgeHitWidth: 150, - direction: 'top-to-bottom', - fullDistance: SCREEN_HEIGHT, - } - }, - animationInterpolators: { - into: buildStyleInterpolator(FromTheFront), - out: buildStyleInterpolator(ToTheBack), - }, - }, - FloatFromBottomAndroid: { - ...BaseConfig, - gestures: null, - defaultTransitionVelocity: 3, - springFriction: 20, - animationInterpolators: { - into: buildStyleInterpolator(FromTheFrontAndroid), - out: buildStyleInterpolator(ToTheBackAndroid), - }, - }, - FadeAndroid: { - ...BaseConfig, - gestures: null, - animationInterpolators: { - into: buildStyleInterpolator(FadeIn), - out: buildStyleInterpolator(FadeOut), - }, - }, - SwipeFromLeft: { - ...BaseConfig, - gestures: { - jumpBack: { - ...directionMapping.BaseEndToStartGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - jumpForward: { - ...directionMapping.BaseStartToEndGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - }, - animationInterpolators: { - into: buildStyleInterpolator(directionMapping.FromTheStart), - out: buildStyleInterpolator(directionMapping.ToTheEnd), - }, - }, - HorizontalSwipeJump: { - ...BaseConfig, - gestures: { - jumpBack: { - ...directionMapping.BaseStartToEndGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - jumpForward: { - ...directionMapping.BaseEndToStartGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - }, - animationInterpolators: { - into: buildStyleInterpolator(directionMapping.FromTheEnd), - out: buildStyleInterpolator(directionMapping.ToTheStart), - }, - }, - HorizontalSwipeJumpFromRight: { - ...BaseConfig, - gestures: { - jumpBack: { - ...directionMapping.BaseEndToStartGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - jumpForward: { - ...directionMapping.BaseStartToEndGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - pop: directionMapping.BaseEndToStartGesture, - }, - animationInterpolators: { - into: buildStyleInterpolator(directionMapping.FromTheStart), - out: buildStyleInterpolator(directionMapping.FadeToTheEnd), - }, - }, - HorizontalSwipeJumpFromLeft: { - ...BaseConfig, - gestures: { - jumpBack: { - ...directionMapping.BaseEndToStartGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - jumpForward: { - ...directionMapping.BaseStartToEndGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - pop: directionMapping.BaseEndToStartGesture, - }, - animationInterpolators: { - into: buildStyleInterpolator(directionMapping.FromTheStart), - out: buildStyleInterpolator(directionMapping.ToTheEnd), - }, - }, - VerticalUpSwipeJump: { - ...BaseConfig, - gestures: { - jumpBack: { - ...BaseUpDownGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - jumpForward: { - ...BaseDownUpGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - }, - animationInterpolators: { - into: buildStyleInterpolator(FromTheDown), - out: buildStyleInterpolator(ToTheUp), - }, - }, - VerticalDownSwipeJump: { - ...BaseConfig, - gestures: { - jumpBack: { - ...BaseDownUpGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - jumpForward: { - ...BaseUpDownGesture, - overswipe: BaseOverswipeConfig, - edgeHitWidth: null, - isDetachable: true, - }, - }, - animationInterpolators: { - into: buildStyleInterpolator(FromTheTop), - out: buildStyleInterpolator(ToTheDown), - }, - }, -}; - -module.exports = NavigatorSceneConfigs; diff --git a/Libraries/EventEmitter/EventValidator.js b/Libraries/EventEmitter/EventValidator.js index 26fda76dfab7f5..5fc234b2263ab4 100644 --- a/Libraries/EventEmitter/EventValidator.js +++ b/Libraries/EventEmitter/EventValidator.js @@ -11,8 +11,6 @@ */ 'use strict'; -const copyProperties = require('copyProperties'); - /** * EventValidator is designed to validate event types to make it easier to catch * common mistakes. It accepts a map of all of the different types of events @@ -37,7 +35,7 @@ const EventValidator = { const eventTypes = Object.keys(types); const emitterWithValidation = Object.create(emitter); - copyProperties(emitterWithValidation, { + Object.assign(emitterWithValidation, { emit: function emit(type, a, b, c, d, e, _) { assertAllowsEventType(type, eventTypes); return emitter.emit.call(this, type, a, b, c, d, e, _); diff --git a/Libraries/EventEmitter/__mocks__/NativeEventEmitter.js b/Libraries/EventEmitter/__mocks__/NativeEventEmitter.js new file mode 100644 index 00000000000000..e2aa9526571b63 --- /dev/null +++ b/Libraries/EventEmitter/__mocks__/NativeEventEmitter.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ +'use strict'; + +const EventEmitter = require('EventEmitter'); +const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); + +/** + * Mock the NativeEventEmitter as a normal JS EventEmitter. + */ +class NativeEventEmitter extends EventEmitter { + constructor() { + super(RCTDeviceEventEmitter.sharedSubscriber); + } +} + +module.exports = NativeEventEmitter; diff --git a/Libraries/EventEmitter/mixInEventEmitter.js b/Libraries/EventEmitter/mixInEventEmitter.js index 2e2f47e025488f..d5db027bb630bc 100644 --- a/Libraries/EventEmitter/mixInEventEmitter.js +++ b/Libraries/EventEmitter/mixInEventEmitter.js @@ -14,9 +14,7 @@ const EventEmitter = require('EventEmitter'); const EventEmitterWithHolding = require('EventEmitterWithHolding'); const EventHolder = require('EventHolder'); -const EventValidator = require('EventValidator'); -const copyProperties = require('copyProperties'); const invariant = require('fbjs/lib/invariant'); const keyOf = require('fbjs/lib/keyOf'); @@ -63,13 +61,13 @@ function mixInEventEmitter(cls: Function | Object, types: Object) { // Keep track of the provided types, union the types if they already exist, // which allows for prototype subclasses to provide more types. if (target.hasOwnProperty(TYPES_KEY)) { - copyProperties(target.__types, types); + Object.assign(target.__types, types); } else if (target.__types) { - target.__types = copyProperties({}, target.__types, types); + target.__types = Object.assign({}, target.__types, types); } else { target.__types = types; } - copyProperties(target, EventEmitterMixin); + Object.assign(target, EventEmitterMixin); } const EventEmitterMixin = { @@ -120,7 +118,10 @@ const EventEmitterMixin = { __getEventEmitter: function() { if (!this.__eventEmitter) { let emitter = new EventEmitter(); - emitter = EventValidator.addValidation(emitter, this.__types); + if (__DEV__) { + const EventValidator = require('EventValidator'); + emitter = EventValidator.addValidation(emitter, this.__types); + } const holder = new EventHolder(); this.__eventEmitter = new EventEmitterWithHolding(emitter, holder); diff --git a/Libraries/Experimental/IncrementalPresenter.js b/Libraries/Experimental/IncrementalPresenter.js index 50d4d56b0dc1b0..90c31fda2333eb 100644 --- a/Libraries/Experimental/IncrementalPresenter.js +++ b/Libraries/Experimental/IncrementalPresenter.js @@ -15,6 +15,8 @@ const IncrementalGroup = require('IncrementalGroup'); const React = require('React'); const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + import type {Context} from 'Incremental'; /** @@ -47,7 +49,7 @@ class IncrementalPresenter extends React.Component { disabled: React.PropTypes.bool, onDone: React.PropTypes.func, onLayout: React.PropTypes.func, - style: View.propTypes.style, + style: ViewPropTypes.style, }; static contextTypes = { incrementalGroup: React.PropTypes.object, diff --git a/Libraries/Experimental/SwipeableRow/SwipeableListView.js b/Libraries/Experimental/SwipeableRow/SwipeableListView.js index 0f9af8cbead296..95381044771544 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableListView.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableListView.js @@ -1,23 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * * @providesModule SwipeableListView * @flow */ @@ -30,6 +18,11 @@ const SwipeableRow = require('SwipeableRow'); const {PropTypes} = React; +type DefaultProps = { + bounceFirstRowOnMount: boolean, + renderQuickActions: Function, +}; + type Props = { bounceFirstRowOnMount: boolean, dataSource: SwipeableListViewDataSource, @@ -61,7 +54,7 @@ type State = { * - It can bounce the 1st row of the list so users know it's swipeable * - More to come */ -class SwipeableListView extends React.Component { +class SwipeableListView extends React.Component { props: Props; state: State; diff --git a/Libraries/Experimental/SwipeableRow/SwipeableListViewDataSource.js b/Libraries/Experimental/SwipeableRow/SwipeableListViewDataSource.js index 4c01bf1a0f9a4d..506b434160d98e 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableListViewDataSource.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableListViewDataSource.js @@ -1,23 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * * @providesModule SwipeableListViewDataSource */ 'use strict'; diff --git a/Libraries/Experimental/SwipeableRow/SwipeableQuickActionButton.js b/Libraries/Experimental/SwipeableRow/SwipeableQuickActionButton.js index b1d9ff2b21978a..64195eb7b3f169 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableQuickActionButton.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableQuickActionButton.js @@ -1,23 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * * @providesModule SwipeableQuickActionButton * @flow */ @@ -29,6 +17,8 @@ const Text = require('Text'); const TouchableHighlight = require('TouchableHighlight'); const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + const {PropTypes} = React; import type {ImageSource} from 'ImageSource'; @@ -55,7 +45,7 @@ class SwipeableQuickActionButton extends React.Component { imageSource: Image.propTypes.source.isRequired, imageStyle: Image.propTypes.style, onPress: PropTypes.func, - style: View.propTypes.style, + style: ViewPropTypes.style, testID: PropTypes.string, text: PropTypes.string, textStyle: Text.propTypes.style, diff --git a/Libraries/Experimental/SwipeableRow/SwipeableQuickActions.js b/Libraries/Experimental/SwipeableRow/SwipeableQuickActions.js index d13914cb5051f3..48b90cea4c4996 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableQuickActions.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableQuickActions.js @@ -1,23 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * * @providesModule SwipeableQuickActions * @flow */ @@ -27,6 +15,8 @@ const React = require('React'); const StyleSheet = require('StyleSheet'); const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + /** * A thin wrapper around standard quick action buttons that can, if the user * chooses, be used with SwipeableListView. Sample usage is as follows, in the @@ -41,7 +31,7 @@ class SwipeableQuickActions extends React.Component { props: {style?: $FlowFixMe}; static propTypes = { - style: View.propTypes.style, + style: ViewPropTypes.style, }; render(): React.Element { diff --git a/Libraries/Experimental/SwipeableRow/SwipeableRow.js b/Libraries/Experimental/SwipeableRow/SwipeableRow.js index eead0ee3805428..0e105c2526b8f8 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableRow.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableRow.js @@ -1,23 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * * @providesModule SwipeableRow * @flow */ diff --git a/Libraries/Experimental/WindowedListView.js b/Libraries/Experimental/WindowedListView.js index 818801696de207..c2e73c68dce83c 100644 --- a/Libraries/Experimental/WindowedListView.js +++ b/Libraries/Experimental/WindowedListView.js @@ -1,32 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule WindowedListView * @flow */ diff --git a/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj b/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj index 916ec00c2529ea..eabbb8b862370a 100644 --- a/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj +++ b/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj @@ -111,6 +111,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -158,6 +159,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index 7d4a7752d3b56f..4820d75702eb85 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -22,6 +22,7 @@ var Set = require('Set'); var StyleSheet = require('StyleSheet'); var StyleSheetPropType = require('StyleSheetPropType'); var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); var ViewStylePropTypes = require('ViewStylePropTypes'); var filterObject = require('fbjs/lib/filterObject'); @@ -78,7 +79,7 @@ var ImageSpecificStyleKeys = new Set(Object.keys(ImageStylePropTypes).filter(x = var Image = React.createClass({ propTypes: { - ...View.propTypes, + ...ViewPropTypes, style: StyleSheetPropType(ImageStylePropTypes), /** * `uri` is a string representing the resource identifier for the image, which @@ -193,7 +194,7 @@ var Image = React.createClass({ getSize( url: string, success: (width: number, height: number) => void, - failure: (error: any) => void, + failure?: (error: any) => void, ) { return ImageLoader.getSize(url) .then(function(sizes) { diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index 3177b1f5e2fcb4..bba7a8999c7523 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -304,7 +304,7 @@ const Image = React.createClass({ getSize: function( uri: string, success: (width: number, height: number) => void, - failure: (error: any) => void, + failure?: (error: any) => void, ) { ImageViewManager.getSize(uri, success, failure || function() { console.warn('Failed to get size for image: ' + uri); diff --git a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj index 200008e1db2991..5369d035b71a32 100644 --- a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj +++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj @@ -361,6 +361,7 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -412,6 +413,7 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; diff --git a/Libraries/Image/RCTLocalAssetImageLoader.m b/Libraries/Image/RCTLocalAssetImageLoader.m index b1bf78c2ddaaaa..17acd15b88c29c 100644 --- a/Libraries/Image/RCTLocalAssetImageLoader.m +++ b/Libraries/Image/RCTLocalAssetImageLoader.m @@ -36,36 +36,6 @@ - (BOOL)shouldCacheLoadedImages return NO; } -static NSString *bundleName(NSBundle *bundle) -{ - NSString *name = bundle.infoDictionary[@"CFBundleName"]; - if (!name) { - name = [[bundle.bundlePath lastPathComponent] stringByDeletingPathExtension]; - } - return name; -} - -static NSBundle *bundleForPath(NSString *key) -{ - static NSMutableDictionary *bundleCache; - if (!bundleCache) { - bundleCache = [NSMutableDictionary new]; - bundleCache[@"main"] = [NSBundle mainBundle]; - - // Initialize every bundle in the array - for (NSString *path in [[NSBundle mainBundle] pathsForResourcesOfType:@"bundle" inDirectory:nil]) { - [NSBundle bundleWithPath:path]; - } - - // The bundles initialized above will now also be in `allBundles` - for (NSBundle *bundle in [NSBundle allBundles]) { - bundleCache[bundleName(bundle)] = bundle; - } - } - - return bundleCache[key]; -} - - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale @@ -80,31 +50,14 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL return; } - NSString *imageName = RCTBundlePathForURL(imageURL); - - NSBundle *bundle; - NSArray *imagePathComponents = [imageName pathComponents]; - if ([imagePathComponents count] > 1 && - [[[imagePathComponents firstObject] pathExtension] isEqualToString:@"bundle"]) { - NSString *bundlePath = [imagePathComponents firstObject]; - bundle = bundleForPath([bundlePath stringByDeletingPathExtension]); - imageName = [imageName substringFromIndex:(bundlePath.length + 1)]; - } - - UIImage *image; - if (bundle) { - image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil]; - } else { - image = [UIImage imageNamed:imageName]; - } - + UIImage *image = RCTImageFromLocalAssetURL(imageURL); if (image) { if (progressHandler) { progressHandler(1, 1); } completionHandler(nil, image); } else { - NSString *message = [NSString stringWithFormat:@"Could not find image named %@", imageName]; + NSString *message = [NSString stringWithFormat:@"Could not find image %@", imageURL]; RCTLogWarn(@"%@", message); completionHandler(RCTErrorWithMessage(message), nil); } diff --git a/Libraries/Interaction/FrameRateLogger.js b/Libraries/Interaction/FrameRateLogger.js new file mode 100644 index 00000000000000..3f1265b4140b03 --- /dev/null +++ b/Libraries/Interaction/FrameRateLogger.js @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule FrameRateLogger + * @flow + */ +'use strict'; + +const NativeModules = require('NativeModules'); + +const invariant = require('fbjs/lib/invariant'); + +/** + * Flow API for native FrameRateLogger module. If the native module is not installed, function calls + * are just no-ops. + * + * Typical behavior is that `setContext` is called when a new screen is loaded (e.g. via a + * navigation integration), and then `beginScroll` is called by `ScrollResponder` at which point the + * native module then begins tracking frame drops. When `ScrollResponder` calls `endScroll`, the + * native module gathers up all it's frame drop data and reports it via an analytics pipeline for + * analysis. + * + * Note that `beginScroll` may be called multiple times by `ScrollResponder` - unclear if that's a + * bug, but the native module should be robust to that. + * + * In the future we may add support for tracking frame drops in other types of interactions beyond + * scrolling. + */ +const FrameRateLogger = { + /** + * Enable `debug` to see local logs of what's going on. `reportStackTraces` will grab stack traces + * during UI thread stalls and upload them if the native module supports it. + */ + setGlobalOptions: function(options: {debug?: boolean, reportStackTraces?: boolean}) { + if (options.debug !== undefined) { + invariant( + NativeModules.FrameRateLogger, + 'Trying to debug FrameRateLogger without the native module!', + ); + } + NativeModules.FrameRateLogger && NativeModules.FrameRateLogger.setGlobalOptions(options); + }, + + /** + * Must call `setContext` before any events can be properly tracked, which is done automatically + * in `AppRegistry`, but navigation is also a common place to hook in. + */ + setContext: function(context: string) { + NativeModules.FrameRateLogger && NativeModules.FrameRateLogger.setContext(context); + }, + + /** + * Called in `ScrollResponder` so any component that uses that module will handle this + * automatically. + */ + beginScroll() { + NativeModules.FrameRateLogger && NativeModules.FrameRateLogger.beginScroll(); + }, + + /** + * Called in `ScrollResponder` so any component that uses that module will handle this + * automatically. + */ + endScroll() { + NativeModules.FrameRateLogger && NativeModules.FrameRateLogger.endScroll(); + }, +}; + +module.exports = FrameRateLogger; diff --git a/Libraries/Interaction/PanResponder.js b/Libraries/Interaction/PanResponder.js index ee5c0edb08282b..fe217b8b74f914 100644 --- a/Libraries/Interaction/PanResponder.js +++ b/Libraries/Interaction/PanResponder.js @@ -77,7 +77,7 @@ const currentCentroidY = TouchHistoryMath.currentCentroidY; * onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, * * onPanResponderGrant: (evt, gestureState) => { - * // The guesture has started. Show visual feedback so the user knows + * // The gesture has started. Show visual feedback so the user knows * // what is happening! * * // gestureState.d{x,y} will be set to zero now diff --git a/Libraries/LayoutAnimation/LayoutAnimation.js b/Libraries/LayoutAnimation/LayoutAnimation.js index 61d62601d71789..ac2525a887067c 100644 --- a/Libraries/LayoutAnimation/LayoutAnimation.js +++ b/Libraries/LayoutAnimation/LayoutAnimation.js @@ -11,12 +11,12 @@ */ 'use strict'; -var {PropTypes} = require('React'); var UIManager = require('UIManager'); -var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker'); var keyMirror = require('fbjs/lib/keyMirror'); +var {checkPropTypes, PropTypes} = require('react'); + var TypesEnum = { spring: true, linear: true, @@ -33,7 +33,7 @@ var PropertiesEnum = { }; var Properties = keyMirror(PropertiesEnum); -var animChecker = createStrictShapeTypeChecker({ +var animType = PropTypes.shape({ duration: PropTypes.number, delay: PropTypes.number, springDamping: PropTypes.number, @@ -55,11 +55,11 @@ type Anim = { property?: $Enum, } -var configChecker = createStrictShapeTypeChecker({ +var configType = PropTypes.shape({ duration: PropTypes.number.isRequired, - create: animChecker, - update: animChecker, - delete: animChecker, + create: animType, + update: animType, + delete: animType, }); type Config = { @@ -69,9 +69,13 @@ type Config = { delete?: Anim, } +function checkConfig(config: Config, location: string, name: string) { + checkPropTypes({config: configType}, {config}, location, name); +} + function configureNext(config: Config, onAnimationDidEnd?: Function) { if (__DEV__) { - configChecker({config}, 'config', 'LayoutAnimation.configureNext'); + checkConfig(config, 'config', 'LayoutAnimation.configureNext'); } UIManager.configureNextLayoutAnimation( config, onAnimationDidEnd || function() {}, function() { /* unused */ } @@ -151,7 +155,7 @@ var LayoutAnimation = { create, Types, Properties, - configChecker: configChecker, + checkConfig, Presets, easeInEaseOut: configureNext.bind( null, Presets.easeInEaseOut diff --git a/Libraries/Linking/Linking.js b/Libraries/Linking/Linking.js index 1902bfb2a793ef..46318a0eed7f72 100644 --- a/Libraries/Linking/Linking.js +++ b/Libraries/Linking/Linking.js @@ -33,7 +33,7 @@ const LinkingManager = Platform.OS === 'android' ? * * ``` * componentDidMount() { - * var url = Linking.getInitialURL().then((url) => { + * Linking.getInitialURL().then((url) => { * if (url) { * console.log('Initial url is: ' + url); * } @@ -138,7 +138,8 @@ class Linking extends NativeEventEmitter { /** * Try to open the given `url` with any of the installed apps. * - * You can use other URLs, like a location (e.g. "geo:37.484847,-122.148386"), a contact, + * You can use other URLs, like a location (e.g. "geo:37.484847,-122.148386" on Android + * or "http://maps.apple.com/?ll=37.484847,-122.148386" on iOS), a contact, * or any other URL that can be opened with the installed apps. * * NOTE: This method will fail if the system doesn't know how to open the specified URL. diff --git a/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj b/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj index d7916e085f7c82..233265be113fb1 100644 --- a/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj +++ b/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj @@ -174,6 +174,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -220,6 +221,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Libraries/LinkingIOS/RCTLinkingManager.m b/Libraries/LinkingIOS/RCTLinkingManager.m index dd1ef0381072dc..d5eb10be1fc441 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.m +++ b/Libraries/LinkingIOS/RCTLinkingManager.m @@ -19,6 +19,11 @@ @implementation RCTLinkingManager RCT_EXPORT_MODULE() +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + - (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self diff --git a/Libraries/Lists/FillRateHelper.js b/Libraries/Lists/FillRateHelper.js new file mode 100644 index 00000000000000..d856fa9527c2b4 --- /dev/null +++ b/Libraries/Lists/FillRateHelper.js @@ -0,0 +1,179 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule FillRateHelper + * @flow + */ + +'use strict'; + +const performanceNow = require('fbjs/lib/performanceNow'); +const warning = require('fbjs/lib/warning'); + +export type FillRateExceededInfo = { + event: { + sample_type: string, + blankness: number, + blank_pixels_top: number, + blank_pixels_bottom: number, + scroll_offset: number, + visible_length: number, + scroll_speed: number, + first_frame: Object, + last_frame: Object, + }, + aggregate: { + avg_blankness: number, + min_speed_when_blank: number, + avg_speed_when_blank: number, + avg_blankness_when_any_blank: number, + fraction_any_blank: number, + all_samples_timespan_sec: number, + fill_rate_sample_counts: {[key: string]: number}, + }, +}; + +type FrameMetrics = {inLayout?: boolean, length: number, offset: number}; + +let _listeners: Array<(FillRateExceededInfo) => void> = []; +let _sampleRate = null; + +/** + * A helper class for detecting when the maximem fill rate of `VirtualizedList` is exceeded. + * By default the sampling rate is set to zero and this will do nothing. If you want to collect + * samples (e.g. to log them), make sure to call `FillRateHelper.setSampleRate(0.0-1.0)`. + * + * Listeners and sample rate are global for all `VirtualizedList`s - typical usage will combine with + * `SceneTracker.getActiveScene` to determine the context of the events. + */ +class FillRateHelper { + _getFrameMetrics: (index: number) => ?FrameMetrics; + _anyBlankCount = 0; + _anyBlankMinSpeed = Infinity; + _anyBlankSpeedSum = 0; + _sampleCounts = {}; + _fractionBlankSum = 0; + _samplesStartTime = 0; + + static addFillRateExceededListener( + callback: (FillRateExceededInfo) => void + ): {remove: () => void} { + warning( + _sampleRate !== null, + 'Call `FillRateHelper.setSampleRate` before `addFillRateExceededListener`.' + ); + _listeners.push(callback); + return { + remove: () => { + _listeners = _listeners.filter((listener) => callback !== listener); + }, + }; + } + + static setSampleRate(sampleRate: number) { + _sampleRate = sampleRate; + } + + static enabled(): boolean { + return (_sampleRate || 0) > 0.0; + } + + constructor(getFrameMetrics: (index: number) => ?FrameMetrics) { + this._getFrameMetrics = getFrameMetrics; + } + + computeInfoSampled( + sampleType: string, + props: { + data: Array, + getItemCount: (data: Array) => number, + initialNumToRender: number, + }, + state: { + first: number, + last: number, + }, + scrollMetrics: { + offset: number, + velocity: number, + visibleLength: number, + }, + ): ?FillRateExceededInfo { + if (!FillRateHelper.enabled() || (_sampleRate || 0) <= Math.random()) { + return null; + } + const start = performanceNow(); + if (props.getItemCount(props.data) === 0) { + return null; + } + if (!this._samplesStartTime) { + this._samplesStartTime = start; + } + const {offset, velocity, visibleLength} = scrollMetrics; + let blankTop = 0; + let first = state.first; + let firstFrame = this._getFrameMetrics(first); + while (first <= state.last && (!firstFrame || !firstFrame.inLayout)) { + firstFrame = this._getFrameMetrics(first); + first++; + } + if (firstFrame) { + blankTop = Math.min(visibleLength, Math.max(0, firstFrame.offset - offset)); + } + let blankBottom = 0; + let last = state.last; + let lastFrame = this._getFrameMetrics(last); + while (last >= state.first && (!lastFrame || !lastFrame.inLayout)) { + lastFrame = this._getFrameMetrics(last); + last--; + } + if (lastFrame) { + const bottomEdge = lastFrame.offset + lastFrame.length; + blankBottom = Math.min(visibleLength, Math.max(0, offset + visibleLength - bottomEdge)); + } + this._sampleCounts.all = (this._sampleCounts.all || 0) + 1; + this._sampleCounts[sampleType] = (this._sampleCounts[sampleType] || 0) + 1; + const blankness = (blankTop + blankBottom) / visibleLength; + if (blankness > 0) { + const scrollSpeed = Math.abs(velocity); + if (scrollSpeed && sampleType === 'onScroll') { + this._anyBlankMinSpeed = Math.min(this._anyBlankMinSpeed, scrollSpeed); + } + this._anyBlankSpeedSum += scrollSpeed; + this._anyBlankCount++; + this._fractionBlankSum += blankness; + const event = { + sample_type: sampleType, + blankness: blankness, + blank_pixels_top: blankTop, + blank_pixels_bottom: blankBottom, + scroll_offset: offset, + visible_length: visibleLength, + scroll_speed: scrollSpeed, + first_frame: {...firstFrame}, + last_frame: {...lastFrame}, + }; + const aggregate = { + avg_blankness: this._fractionBlankSum / this._sampleCounts.all, + min_speed_when_blank: this._anyBlankMinSpeed, + avg_speed_when_blank: this._anyBlankSpeedSum / this._anyBlankCount, + avg_blankness_when_any_blank: this._fractionBlankSum / this._anyBlankCount, + fraction_any_blank: this._anyBlankCount / this._sampleCounts.all, + all_samples_timespan_sec: (performanceNow() - this._samplesStartTime) / 1000.0, + fill_rate_sample_counts: {...this._sampleCounts}, + compute_time: performanceNow() - start, + }; + const info = {event, aggregate}; + _listeners.forEach((listener) => listener(info)); + return info; + } + return null; + } +} + +module.exports = FillRateHelper; diff --git a/Libraries/CustomComponents/Lists/FlatList.js b/Libraries/Lists/FlatList.js similarity index 66% rename from Libraries/CustomComponents/Lists/FlatList.js rename to Libraries/Lists/FlatList.js index 951f8f78278a09..17ccf2ce845e54 100644 --- a/Libraries/CustomComponents/Lists/FlatList.js +++ b/Libraries/Lists/FlatList.js @@ -1,32 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule FlatList * @flow */ @@ -34,7 +13,6 @@ const MetroListView = require('MetroListView'); // Used as a fallback legacy option const React = require('React'); -const ReactNative = require('ReactNative'); const View = require('View'); const VirtualizedList = require('VirtualizedList'); @@ -51,7 +29,7 @@ type RequiredProps = { * _renderItem = ({item}) => ( * this._onPress(item)}> * {item.title}} - * + * * ); * ... * @@ -67,17 +45,29 @@ type RequiredProps = { }; type OptionalProps = { /** - * Rendered at the bottom of all the items. + * Rendered in between each item, but not at the top or bottom. */ - FooterComponent?: ?ReactClass, + ItemSeparatorComponent?: ?ReactClass, /** - * Rendered at the top of all the items. + * Rendered at the bottom of all the items. Can be a React Component Class, a render function, or + * a rendered element. */ - HeaderComponent?: ?ReactClass, + ListFooterComponent?: ?(ReactClass | React.Element), /** - * Rendered in between each item, but not at the top or bottom. + * Rendered at the top of all the items. Can be a React Component Class, a render function, or + * a rendered element. */ - SeparatorComponent?: ?ReactClass, + ListHeaderComponent?: ?(ReactClass | React.Element), + /** + * Optional custom style for multi-item rows generated when numColumns > 1. + */ + columnWrapperStyle?: StyleObj, + /** + * A marker property for telling the list to re-render (since it implements `PureComponent`). If + * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the + * `data` prop, stick it here and treat it immutably. + */ + extraData?: any, /** * `getItemLayout` is an optional optimizations that let us skip measurement of dynamic content if * you know the height of items a priori. `getItemLayout` is the most efficient, and is easy to @@ -88,7 +78,7 @@ type OptionalProps = { * )} * * Remember to include separator length (height or width) in your offset calculation if you - * specify `SeparatorComponent`. + * specify `ItemSeparatorComponent`. */ getItemLayout?: (data: ?Array, index: number) => {length: number, offset: number, index: number}, @@ -96,6 +86,12 @@ type OptionalProps = { * If true, renders items next to each other horizontally instead of stacked vertically. */ horizontal?: ?boolean, + /** + * How many items to render in the initial batch. This should be enough to fill the screen but not + * much more. Note these items will never be unmounted as part of the windowed rendering in order + * to improve perceived performance of scroll-to-top actions. + */ + initialNumToRender: number, /** * Used to extract a unique key for a given item at the specified index. Key is used for caching * and as the react key to track item re-ordering. The default extractor checks `item.key`, then @@ -103,7 +99,7 @@ type OptionalProps = { */ keyExtractor: (item: ItemT, index: number) => string, /** - * Multiple columns can only be rendered with `horizontal={false}`` and will zig-zag like a + * Multiple columns can only be rendered with `horizontal={false}` and will zig-zag like a * `flexWrap` layout. Items should all be the same height - masonry layouts are not supported. */ numColumns: number, @@ -112,6 +108,12 @@ type OptionalProps = { * content. */ onEndReached?: ?(info: {distanceFromEnd: number}) => void, + /** + * How far from the end (in units of visible length of the list) the bottom edge of the + * list must be from the end of the content to trigger the `onEndReached` callback. + * Thus a value of 0.5 will trigger `onEndReached` when the end of the content is + * within half the visible length of the list. + */ onEndReachedThreshold?: ?number, /** * If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make @@ -119,26 +121,17 @@ type OptionalProps = { */ onRefresh?: ?() => void, /** - * Called when the viewability of rows changes, as defined by the - * `viewablePercentThreshold` prop. + * Called when the viewability of rows changes, as defined by the `viewabilityConfig` prop. */ - onViewableItemsChanged?: ?(info: {viewableItems: Array, changed: Array}) => void, + onViewableItemsChanged?: ?(info: { + viewableItems: Array, + changed: Array, + }) => void, legacyImplementation?: ?boolean, /** * Set this true while waiting for new data from a refresh. */ refreshing?: ?boolean, - /** - * Optional custom style for multi-item rows generated when numColumns > 1 - */ - columnWrapperStyle?: StyleObj, - /** - * Optional optimization to minimize re-rendering items. - */ - shouldItemUpdate: ( - prevInfo: {item: ItemT, index: number}, - nextInfo: {item: ItemT, index: number} - ) => boolean, /** * See `ViewabilityHelper` for flow type and further documentation. */ @@ -165,8 +158,9 @@ type DefaultProps = typeof defaultProps; * - Separator support. * - Pull to Refresh. * - Scroll loading. + * - ScrollToIndex support. * - * If you need section support, use [``](/react-native/docs/sectionlist.html). + * If you need section support, use [``](docs/sectionlist.html). * * Minimal Example: * @@ -175,21 +169,90 @@ type DefaultProps = typeof defaultProps; * renderItem={({item}) => {item.key}} * /> * - * This is a convenience wrapper around [``](/react-native/docs/virtualizedlist.html), - * and thus inherits the following caveats: + * More complex example demonstrating `PureComponent` usage for perf optimization and avoiding bugs. + * + * - By binding the `onPressItem` handler, the props will remain `===` and `PureComponent` will + * prevent wasteful re-renders unless the actual `id`, `selected`, or `title` props change, even + * if the inner `SomeOtherWidget` has no such optimizations. + * - By passing `extraData={this.state}` to `FlatList` we make sure `FlatList` itself will re-render + * when the `state.selected` changes. Without setting this prop, `FlatList` would not know it + * needs to re-render any items because it is also a `PureComponent` and the prop comparison will + * not show any changes. + * - `keyExtractor` tells the list to use the `id`s for the react keys. + * + * + * class MyListItem extends React.PureComponent { + * _onPress = () => { + * this.props.onPressItem(this.props.id); + * }; + * + * render() { + * return ( + * + * ) + * } + * } + * + * class MyList extends React.PureComponent { + * state = {selected: (new Map(): Map)}; + * + * _keyExtractor = (item, index) => item.id; + * + * _onPressItem = (id: string) => { + * // updater functions are preferred for transactional updates + * this.setState((state) => { + * // copy the map rather than modifying state. + * const selected = new Map(state.selected); + * selected.set(id, !state.get(id)); // toggle + * return {selected}; + * }); + * }; + * + * _renderItem = ({item}) => ( + * + * ); + * + * render() { + * return ( + * + * ); + * } + * } + * + * This is a convenience wrapper around [``](docs/virtualizedlist.html), + * and thus inherits it's props (as well as those of `ScrollView`) that aren't explicitly listed + * here, along with the following caveats: * * - Internal state is not preserved when content scrolls out of the render window. Make sure all * your data is captured in the item data or external stores like Flux, Redux, or Relay. * - This is a `PureComponent` which means that it will not re-render if `props` remain shallow- - * equal. Make sure that everything your `renderItem` function depends on is passed as a prop that - * is not `===` after updates, otherwise your UI may not update on changes. This includes the - * `data` prop and parent component state. + * equal. Make sure that everything your `renderItem` function depends on is passed as a prop + * (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on + * changes. This includes the `data` prop and parent component state. * - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously * offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see * blank content. This is a tradeoff that can be adjusted to suit the needs of each application, * and we are working on improving it behind the scenes. * - By default, the list looks for a `key` prop on each item and uses that for the React key. * Alternatively, you can provide a custom `keyExtractor` prop. + * + * NOTE: `removeClippedSubviews` might not be necessary and may cause bugs. If you see issues with + * content not rendering, e.g when using `LayoutAnimation`, try setting + * `removeClippedSubviews={false}`, and we may change the default in the future after more + * experimentation in production apps. */ class FlatList extends React.PureComponent, void> { static defaultProps: DefaultProps = defaultProps; @@ -204,17 +267,22 @@ class FlatList extends React.PureComponent, vo /** * Scrolls to the item at a the specified index such that it is positioned in the viewable area * such that `viewPosition` 0 places it at the top, 1 at the bottom, and 0.5 centered in the - * middle. + * middle. `viewOffset` is a fixed number of pixels to offset the final target position. * - * May be janky without `getItemLayout` prop. + * Note: cannot scroll to locations outside the render window without specifying the + * `getItemLayout` prop. */ - scrollToIndex(params: {animated?: ?boolean, index: number, viewPosition?: number}) { + scrollToIndex(params: { + animated?: ?boolean, index: number, viewOffset?: number, viewPosition?: number, + }) { this._listRef.scrollToIndex(params); } /** - * Requires linear scan through data - use `scrollToIndex` instead if possible. May be janky - * without `getItemLayout` prop. + * Requires linear scan through data - use `scrollToIndex` instead if possible. + * + * Note: cannot scroll to locations outside the render window without specifying the + * `getItemLayout` prop. */ scrollToItem(params: {animated?: ?boolean, item: ItemT, viewPosition?: number}) { this._listRef.scrollToItem(params); @@ -236,11 +304,18 @@ class FlatList extends React.PureComponent, vo this._listRef.recordInteraction(); } + /** + * Provides a handle to the underlying scroll responder. + */ + getScrollResponder() { + if (this._listRef) { + return this._listRef.getScrollResponder(); + } + } + getScrollableNode() { - if (this._listRef && this._listRef.getScrollableNode) { + if (this._listRef) { return this._listRef.getScrollableNode(); - } else { - return ReactNative.findNodeHandle(this._listRef); } } @@ -249,6 +324,11 @@ class FlatList extends React.PureComponent, vo } componentWillReceiveProps(nextProps: Props) { + invariant( + nextProps.numColumns === this.props.numColumns, + 'Changing numColumns on the fly is not supported. Change the key prop on FlatList when ' + + 'changing the number of columns to force a fresh render of the component.' + ); this._checkProps(nextProps); } @@ -327,6 +407,7 @@ class FlatList extends React.PureComponent, vo arr.push({...v, item, key: keyExtractor(item, index), index}); }); } + _onViewableItemsChanged = (info) => { const {numColumns, onViewableItemsChanged} = this.props; if (!onViewableItemsChanged) { @@ -361,19 +442,6 @@ class FlatList extends React.PureComponent, vo } }; - _shouldItemUpdate = (prev, next) => { - const {numColumns, shouldItemUpdate} = this.props; - if (numColumns > 1) { - return prev.item.length !== next.item.length || - prev.item.some((prevItem, ii) => shouldItemUpdate( - {item: prevItem, index: prev.index + ii}, - {item: next.item[ii], index: next.index + ii}, - )); - } else { - return shouldItemUpdate(prev, next); - } - }; - render() { if (this.props.legacyImplementation) { return ; @@ -386,7 +454,6 @@ class FlatList extends React.PureComponent, vo getItemCount={this._getItemCount} keyExtractor={this._keyExtractor} ref={this._captureRef} - shouldItemUpdate={this._shouldItemUpdate} onViewableItemsChanged={this.props.onViewableItemsChanged && this._onViewableItemsChanged} /> ); diff --git a/Libraries/CustomComponents/ListView/ListView.js b/Libraries/Lists/ListView/ListView.js similarity index 94% rename from Libraries/CustomComponents/ListView/ListView.js rename to Libraries/Lists/ListView/ListView.js index 066691f54bbc73..afc38070cabc5c 100644 --- a/Libraries/CustomComponents/ListView/ListView.js +++ b/Libraries/Lists/ListView/ListView.js @@ -1,32 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule ListView * @flow */ diff --git a/Libraries/CustomComponents/ListView/ListViewDataSource.js b/Libraries/Lists/ListView/ListViewDataSource.js similarity index 89% rename from Libraries/CustomComponents/ListView/ListViewDataSource.js rename to Libraries/Lists/ListView/ListViewDataSource.js index 54f1227767a129..70bc43d1a2729b 100644 --- a/Libraries/CustomComponents/ListView/ListViewDataSource.js +++ b/Libraries/Lists/ListView/ListViewDataSource.js @@ -1,26 +1,10 @@ /** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ListViewDataSource * @flow diff --git a/Libraries/CustomComponents/ListView/__mocks__/ListViewMock.js b/Libraries/Lists/ListView/__mocks__/ListViewMock.js similarity index 97% rename from Libraries/CustomComponents/ListView/__mocks__/ListViewMock.js rename to Libraries/Lists/ListView/__mocks__/ListViewMock.js index a3e5e1245373a8..6e86c8fe6cc965 100644 --- a/Libraries/CustomComponents/ListView/__mocks__/ListViewMock.js +++ b/Libraries/Lists/ListView/__mocks__/ListViewMock.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/Libraries/CustomComponents/Lists/MetroListView.js b/Libraries/Lists/MetroListView.js similarity index 75% rename from Libraries/CustomComponents/Lists/MetroListView.js rename to Libraries/Lists/MetroListView.js index 93524121456c0b..eae4a6f3263959 100644 --- a/Libraries/CustomComponents/Lists/MetroListView.js +++ b/Libraries/Lists/MetroListView.js @@ -1,32 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule MetroListView * @flow */ @@ -62,7 +41,6 @@ type NormalProps = { refreshing?: boolean, }; type DefaultProps = { - shouldItemUpdate: (curr: {item: Item}, next: {item: Item}) => boolean, keyExtractor: (item: Item) => string, }; type Props = NormalProps & DefaultProps; @@ -83,14 +61,19 @@ class MetroListView extends React.Component { scrollToItem(params: {animated?: ?boolean, item: Item, viewPosition?: number}) { throw new Error('scrollToItem not supported in legacy ListView.'); } + scrollToLocation() { + throw new Error('scrollToLocation not supported in legacy ListView.'); + } scrollToOffset(params: {animated?: ?boolean, offset: number}) { const {animated, offset} = params; this._listRef.scrollTo( this.props.horizontal ? {x: offset, animated} : {y: offset, animated} ); } + getListRef() { + return this._listRef; + } static defaultProps: DefaultProps = { - shouldItemUpdate: () => true, keyExtractor: (item, index) => item.key || index, renderScrollComponent: (props: Props) => { if (props.onRefresh) { @@ -114,7 +97,7 @@ class MetroListView extends React.Component { this.props, { ds: new ListView.DataSource({ - rowHasChanged: (itemA, itemB) => this.props.shouldItemUpdate({item: itemA}, {item: itemB}), + rowHasChanged: (itemA, itemB) => true, sectionHeaderHasChanged: () => true, getSectionHeaderData: (dataBlob, sectionID) => this.state.sectionHeaderData[sectionID], }), diff --git a/Libraries/CustomComponents/Lists/SectionList.js b/Libraries/Lists/SectionList.js similarity index 51% rename from Libraries/CustomComponents/Lists/SectionList.js rename to Libraries/Lists/SectionList.js index 2b6daefe77eb6a..8b7409d9b1ddd0 100644 --- a/Libraries/CustomComponents/Lists/SectionList.js +++ b/Libraries/Lists/SectionList.js @@ -1,38 +1,18 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule SectionList * @flow */ 'use strict'; const MetroListView = require('MetroListView'); +const Platform = require('Platform'); const React = require('React'); const VirtualizedSectionList = require('VirtualizedSectionList'); @@ -48,13 +28,11 @@ type SectionBase = { // Optional props will override list-wide props just for this section. renderItem?: ?(info: {item: SectionItemT, index: number}) => ?React.Element, - SeparatorComponent?: ?ReactClass, + ItemSeparatorComponent?: ?ReactClass, keyExtractor?: (item: SectionItemT) => string, // TODO: support more optional/override props - // FooterComponent?: ?ReactClass<*>, - // HeaderComponent?: ?ReactClass<*>, - // onViewableItemsChanged?: ({viewableItems: Array, changed: Array}) => void, + // onViewableItemsChanged?: ... }; type RequiredProps> = { @@ -73,26 +51,46 @@ type OptionalProps> = { /** * Rendered at the very beginning of the list. */ - ListHeaderComponent?: ?ReactClass, + ListHeaderComponent?: ?(ReactClass | React.Element), /** * Rendered at the very end of the list. */ - ListFooterComponent?: ?ReactClass, - /** - * Rendered at the top of each section. Sticky headers are not yet supported. - */ - renderSectionHeader?: ?(info: {section: SectionT}) => ?React.Element, + ListFooterComponent?: ?(ReactClass | React.Element), /** * Rendered in between each section. */ SectionSeparatorComponent?: ?ReactClass, + /** + * A marker property for telling the list to re-render (since it implements `PureComponent`). If + * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the + * `data` prop, stick it here and treat it immutably. + */ + extraData?: any, + /** + * How many items to render in the initial batch. This should be enough to fill the screen but not + * much more. Note these items will never be unmounted as part of the windowed rendering in order + * to improve perceived performance of scroll-to-top actions. + */ + initialNumToRender: number, /** * Used to extract a unique key for a given item at the specified index. Key is used for caching * and as the react key to track item re-ordering. The default extractor checks item.key, then * falls back to using the index, like react does. */ + keyExtractor: (item: Item, index: number) => string, + /** + * Called once when the scroll position gets within `onEndReachedThreshold` of the rendered + * content. + */ onEndReached?: ?(info: {distanceFromEnd: number}) => void, + /** + * How far from the end (in units of visible length of the list) the bottom edge of the + * list must be from the end of the content to trigger the `onEndReached` callback. + * Thus a value of 0.5 will trigger `onEndReached` when the end of the content is + * within half the visible length of the list. + */ + onEndReachedThreshold?: ?number, /** * If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make * sure to also set the `refreshing` prop correctly. @@ -102,25 +100,36 @@ type OptionalProps> = { * Called when the viewability of rows changes, as defined by the * `viewabilityConfig` prop. */ - onViewableItemsChanged?: ?(info: {viewableItems: Array, changed: Array}) => void, + onViewableItemsChanged?: ?(info: { + viewableItems: Array, + changed: Array, + }) => void, /** * Set this true while waiting for new data from a refresh. */ refreshing?: ?boolean, /** - * This is an optional optimization to minimize re-rendering items. + * Rendered at the top of each section. These stick to the top of the `ScrollView` by default on + * iOS. See `stickySectionHeadersEnabled`. */ - shouldItemUpdate: ( - prevProps: {item: Item, index: number}, - nextProps: {item: Item, index: number} - ) => boolean, + renderSectionHeader?: ?(info: {section: SectionT}) => ?React.Element, + /** + * Makes section headers stick to the top of the screen until the next one pushes it off. Only + * enabled by default on iOS because that is the platform standard there. + */ + stickySectionHeadersEnabled?: boolean, }; type Props = RequiredProps & OptionalProps & VirtualizedSectionListProps; -type DefaultProps = typeof VirtualizedSectionList.defaultProps; +const defaultProps = { + ...VirtualizedSectionList.defaultProps, + stickySectionHeadersEnabled: Platform.OS === 'ios', +}; + +type DefaultProps = typeof defaultProps; /** * A performant interface for rendering sectioned lists, supporting the most handy features: @@ -136,9 +145,8 @@ type DefaultProps = typeof VirtualizedSectionList.defaultProps; * - Pull to Refresh. * - Scroll loading. * - * If you don't need section support and want a simpler interface, use [``](/react-native/docs/flatlist.html). - * - * If you need _sticky_ section header support, use `ListView` for now. + * If you don't need section support and want a simpler interface, use + * [``](/react-native/docs/flatlist.html). * * Simple Examples: * @@ -160,40 +168,88 @@ type DefaultProps = typeof VirtualizedSectionList.defaultProps; * ]} * /> * - * This is a convenience wrapper around [``](/react-native/docs/virtualizedlist.html), - * and thus inherits the following caveats: + * This is a convenience wrapper around [``](docs/virtualizedlist.html), + * and thus inherits it's props (as well as those of `ScrollView`) that aren't explicitly listed + * here, along with the following caveats: * * - Internal state is not preserved when content scrolls out of the render window. Make sure all * your data is captured in the item data or external stores like Flux, Redux, or Relay. * - This is a `PureComponent` which means that it will not re-render if `props` remain shallow- - * equal. Make sure that everything your `renderItem` function depends on is passed as a prop that - * is not `===` after updates, otherwise your UI may not update on changes. This includes the - * `data` prop and parent component state. + * equal. Make sure that everything your `renderItem` function depends on is passed as a prop + * (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on + * changes. This includes the `data` prop and parent component state. * - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously * offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see * blank content. This is a tradeoff that can be adjusted to suit the needs of each application, * and we are working on improving it behind the scenes. * - By default, the list looks for a `key` prop on each item and uses that for the React key. * Alternatively, you can provide a custom `keyExtractor` prop. + * + * NOTE: `removeClippedSubviews` might not be necessary and may cause bugs. If you see issues with + * content not rendering, e.g when using `LayoutAnimation`, try setting + * `removeClippedSubviews={false}`, and we may change the default in the future after more + * experimentation in production apps. */ class SectionList> extends React.PureComponent, void> { props: Props; - static defaultProps: DefaultProps = VirtualizedSectionList.defaultProps; + static defaultProps: DefaultProps = defaultProps; + + /** + * Scrolls to the item at the specified `sectionIndex` and `itemIndex` (within the section) + * positioned in the viewable area such that `viewPosition` 0 places it at the top (and may be + * covered by a sticky header), 1 at the bottom, and 0.5 centered in the middle. `viewOffset` is a + * fixed number of pixels to offset the final target position, e.g. to compensate for sticky + * headers. + * + * Note: cannot scroll to locations outside the render window without specifying the + * `getItemLayout` prop. + */ + scrollToLocation(params: { + animated?: ?boolean, + itemIndex: number, + sectionIndex: number, + viewOffset?: number, + viewPosition?: number, + }) { + this._wrapperListRef.scrollToLocation(params); + } + + /** + * Tells the list an interaction has occured, which should trigger viewability calculations, e.g. + * if `waitForInteractions` is true and the user has not scrolled. This is typically called by + * taps on items or by navigation actions. + */ + recordInteraction() { + const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); + listRef && listRef.recordInteraction(); + } + + /** + * Provides a handle to the underlying scroll responder. + */ + getScrollResponder() { + const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); + if (listRef) { + return listRef.getScrollResponder(); + } + } + + getScrollableNode() { + const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); + if (listRef) { + return listRef.getScrollableNode(); + } + } render() { - const {ListFooterComponent, ListHeaderComponent, ItemSeparatorComponent} = this.props; const List = this.props.legacyImplementation ? MetroListView : VirtualizedSectionList; - return ( - - ); + return ; } + + _wrapperListRef: MetroListView | VirtualizedSectionList; + _captureRef = (ref) => { this._wrapperListRef = ref; }; } module.exports = SectionList; diff --git a/Libraries/CustomComponents/Lists/ViewabilityHelper.js b/Libraries/Lists/ViewabilityHelper.js similarity index 95% rename from Libraries/CustomComponents/Lists/ViewabilityHelper.js rename to Libraries/Lists/ViewabilityHelper.js index f3cc0e584b34d4..ee6f353115a4e0 100644 --- a/Libraries/CustomComponents/Lists/ViewabilityHelper.js +++ b/Libraries/Lists/ViewabilityHelper.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the @@ -49,7 +49,7 @@ export type ViewabilityConfig = {| * layout. * * An item is said to be in a "viewable" state when any of the following -* is true for longer than `minViewTime` milliseconds (after an interaction if `waitForInteraction` +* is true for longer than `minimumViewTime` milliseconds (after an interaction if `waitForInteraction` * is true): * * - Occupying >= `viewAreaCoveragePercentThreshold` of the view area XOR fraction of the item @@ -145,7 +145,7 @@ class ViewabilityHelper { renderRange?: {first: number, last: number}, // Optional optimization to reduce the scan size ): void { const updateTime = Date.now(); - if (this._lastUpdateTime === 0 && getFrameMetrics(0)) { + if (this._lastUpdateTime === 0 && itemCount > 0 && getFrameMetrics(0)) { // Only count updates after the first item is rendered and has a frame. this._lastUpdateTime = updateTime; } @@ -171,13 +171,13 @@ class ViewabilityHelper { } this._viewableIndices = viewableIndices; this._lastUpdateTime = updateTime; - if (this._config.minViewTime && updateElapsed < this._config.minViewTime) { + if (this._config.minimumViewTime && updateElapsed < this._config.minimumViewTime) { const handle = setTimeout( () => { this._timers.delete(handle); this._onUpdateSync(viewableIndices, onViewableItemsChanged, createViewToken); }, - this._config.minViewTime, + this._config.minimumViewTime, ); this._timers.add(handle); } else { diff --git a/Libraries/CustomComponents/Lists/VirtualizeUtils.js b/Libraries/Lists/VirtualizeUtils.js similarity index 99% rename from Libraries/CustomComponents/Lists/VirtualizeUtils.js rename to Libraries/Lists/VirtualizeUtils.js index e03fa4dd725662..9342d41bad6765 100644 --- a/Libraries/CustomComponents/Lists/VirtualizeUtils.js +++ b/Libraries/Lists/VirtualizeUtils.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/Libraries/CustomComponents/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js similarity index 75% rename from Libraries/CustomComponents/Lists/VirtualizedList.js rename to Libraries/Lists/VirtualizedList.js index 206a1f2d8cd3f3..2fa3fd3c029a99 100644 --- a/Libraries/CustomComponents/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -1,38 +1,18 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule VirtualizedList * @flow */ 'use strict'; const Batchinator = require('Batchinator'); +const FillRateHelper = require('FillRateHelper'); const React = require('React'); const ReactNative = require('ReactNative'); const RefreshControl = require('RefreshControl'); @@ -48,6 +28,7 @@ const {computeWindowedRenderLimits} = require('VirtualizeUtils'); import type {ViewabilityConfig, ViewToken} from 'ViewabilityHelper'; type Item = any; + type renderItemType = (info: {item: Item, index: number}) => ?React.Element; type RequiredProps = { @@ -59,9 +40,6 @@ type RequiredProps = { data?: any, }; type OptionalProps = { - FooterComponent?: ?ReactClass, - HeaderComponent?: ?ReactClass, - SeparatorComponent?: ?ReactClass, /** * `debug` will turn on extra logging and visual overlays to aid with debugging both usage and * implementation, but with a significant perf hit. @@ -73,6 +51,12 @@ type OptionalProps = { * this for debugging purposes. */ disableVirtualization: boolean, + /** + * A marker property for telling the list to re-render (since it implements `PureComponent`). If + * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the + * `data` prop, stick it here and treat it immutably. + */ + extraData?: any, /** * A generic accessor for extracting an item from any sort of data blob. */ @@ -86,7 +70,8 @@ type OptionalProps = { horizontal?: ?boolean, /** * How many items to render in the initial batch. This should be enough to fill the screen but not - * much more. + * much more. Note these items will never be unmounted as part of the windowed rendering in order + * to improve perceived performance of scroll-to-top actions. */ initialNumToRender: number, keyExtractor: (item: Item, index: number) => string, @@ -108,7 +93,10 @@ type OptionalProps = { * Called when the viewability of rows changes, as defined by the * `viewabilityConfig` prop. */ - onViewableItemsChanged?: ?(info: {viewableItems: Array, changed: Array}) => void, + onViewableItemsChanged?: ?(info: { + viewableItems: Array, + changed: Array, + }) => void, /** * Set this true while waiting for new data from a refresh. */ @@ -123,10 +111,6 @@ type OptionalProps = { * Render a custom scroll component, e.g. with a differently styled `RefreshControl`. */ renderScrollComponent: (props: Object) => React.Element, - shouldItemUpdate: ( - props: {item: Item, index: number}, - nextProps: {item: Item, index: number} - ) => boolean, /** * Amount of time between low-pri item render batches, e.g. for rendering items quite a ways off * screen. Similar fill rate/responsiveness tradeoff as `maxToRenderPerBatch`. @@ -165,9 +149,9 @@ type State = {first: number, last: number}; * - Internal state is not preserved when content scrolls out of the render window. Make sure all * your data is captured in the item data or external stores like Flux, Redux, or Relay. * - This is a `PureComponent` which means that it will not re-render if `props` remain shallow- - * equal. Make sure that everything your `renderItem` function depends on is passed as a prop that - * is not `===` after updates, otherwise your UI may not update on changes. This includes the - * `data` prop and parent component state. + * equal. Make sure that everything your `renderItem` function depends on is passed as a prop + * (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on + * changes. This includes the `data` prop and parent component state. * - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously * offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see * blank content. This is a tradeoff that can be adjusted to suit the needs of each application, @@ -175,11 +159,10 @@ type State = {first: number, last: number}; * - By default, the list looks for a `key` prop on each item and uses that for the React key. * Alternatively, you can provide a custom `keyExtractor` prop. * - * NOTE: `LayoutAnimation` and sticky section headers both have bugs when used with this and are - * therefore not officially supported yet. - * * NOTE: `removeClippedSubviews` might not be necessary and may cause bugs. If you see issues with - * content not rendering, try disabling it, and we may change the default there. + * content not rendering, e.g when using `LayoutAnimation`, try setting + * `removeClippedSubviews={false}`, and we may change the default in the future after more + * experimentation in production apps. */ class VirtualizedList extends React.PureComponent { props: Props; @@ -197,18 +180,25 @@ class VirtualizedList extends React.PureComponent { } // scrollToIndex may be janky without getItemLayout prop - scrollToIndex(params: {animated?: ?boolean, index: number, viewPosition?: number}) { - const {data, horizontal, getItemCount} = this.props; - const {animated, index, viewPosition} = params; - if (!(index >= 0 && index < getItemCount(data))) { - console.warn('scrollToIndex out of range ' + index); - return; - } + scrollToIndex(params: { + animated?: ?boolean, index: number, viewOffset?: number, viewPosition?: number + }) { + const {data, horizontal, getItemCount, getItemLayout} = this.props; + const {animated, index, viewOffset, viewPosition} = params; + invariant( + index >= 0 && index < getItemCount(data), + `scrollToIndex out of range: ${index} vs ${getItemCount(data) - 1}`, + ); + invariant( + getItemLayout || index < this._highestMeasuredFrameIndex, + 'scrollToIndex should be used in conjunction with getItemLayout, ' + + 'otherwise there is no way to know the location of an arbitrary index.', + ); const frame = this._getFrameMetricsApprox(index); const offset = Math.max( 0, frame.offset - (viewPosition || 0) * (this._scrollMetrics.visibleLength - frame.length), - ); + ) - (viewOffset || 0); this._scrollRef.scrollTo(horizontal ? {x: offset, animated} : {y: offset, animated}); } @@ -238,6 +228,17 @@ class VirtualizedList extends React.PureComponent { this._updateViewableItems(this.props.data); } + /** + * Provides a handle to the underlying scroll responder. + * Note that `this._scrollRef` might not be a `ScrollView`, so we + * need to check that it responds to `getScrollResponder` before calling it. + */ + getScrollResponder() { + if (this._scrollRef && this._scrollRef.getScrollResponder) { + return this._scrollRef.getScrollResponder(); + } + } + getScrollableNode() { if (this._scrollRef && this._scrollRef.getScrollableNode) { return this._scrollRef.getScrollableNode(); @@ -260,7 +261,6 @@ class VirtualizedList extends React.PureComponent { return String(index); }, maxToRenderPerBatch: 10, - onEndReached: () => {}, onEndReachedThreshold: 2, // multiples of length removeClippedSubviews: true, renderScrollComponent: (props: Props) => { @@ -285,10 +285,7 @@ class VirtualizedList extends React.PureComponent { return ; } }, - shouldItemUpdate: ( - props: {item: Item, index: number}, - nextProps: {item: Item, index: number}, - ) => true, + scrollEventThrottle: 50, updateCellsBatchingPeriod: 50, windowSize: 21, // multiples of length }; @@ -305,6 +302,8 @@ class VirtualizedList extends React.PureComponent { 'Components based on VirtualizedList must be wrapped with Animated.createAnimatedComponent ' + 'to support native onScroll events with useNativeDriver', ); + + this._fillRateHelper = new FillRateHelper(this._getFrameMetrics); this._updateCellsToRenderBatcher = new Batchinator( this._updateCellsToRender, this.props.updateCellsBatchingPeriod, @@ -323,66 +322,112 @@ class VirtualizedList extends React.PureComponent { } componentWillReceiveProps(newProps: Props) { - const {data, getItemCount, maxToRenderPerBatch} = newProps; + const {data, extraData, getItemCount, maxToRenderPerBatch} = newProps; // first and last could be stale (e.g. if a new, shorter items props is passed in), so we make // sure we're rendering a reasonable range here. this.setState({ first: Math.max(0, Math.min(this.state.first, getItemCount(data) - 1 - maxToRenderPerBatch)), last: Math.max(0, Math.min(this.state.last, getItemCount(data) - 1)), }); + if (data !== this.props.data || extraData !== this.props.extraData) { + this._hasDataChangedSinceEndReached = true; + } this._updateCellsToRenderBatcher.schedule(); } - _pushCells(cells, first, last) { - const {SeparatorComponent, data, getItem, getItemCount, keyExtractor} = this.props; + _pushCells( + cells: Array, + stickyHeaderIndices: Array, + stickyIndicesFromProps: Set, + first: number, + last: number, + ) { + const {ItemSeparatorComponent, data, getItem, getItemCount, keyExtractor} = this.props; + const stickyOffset = this.props.ListHeaderComponent ? 1 : 0; const end = getItemCount(data) - 1; last = Math.min(end, last); for (let ii = first; ii <= last; ii++) { const item = getItem(data, ii); invariant(item, 'No item for index ' + ii); const key = keyExtractor(item, ii); + if (stickyIndicesFromProps.has(ii + stickyOffset)) { + stickyHeaderIndices.push(cells.length); + } cells.push( this._onCellLayout(e, key, ii)} onUnmount={this._onCellUnmount} parentProps={this.props} /> ); - if (SeparatorComponent && ii < end) { - cells.push(); + if (ItemSeparatorComponent && ii < end) { + cells.push(); } } } + render() { - const {FooterComponent, HeaderComponent} = this.props; + const {ListFooterComponent, ListHeaderComponent} = this.props; const {data, disableVirtualization, horizontal} = this.props; const cells = []; - if (HeaderComponent) { + const stickyIndicesFromProps = new Set(this.props.stickyHeaderIndices); + const stickyHeaderIndices = []; + if (ListHeaderComponent) { + const element = React.isValidElement(ListHeaderComponent) + ? ListHeaderComponent + : ; cells.push( - + {element} ); } const itemCount = this.props.getItemCount(data); if (itemCount > 0) { _usedIndexForKey = false; + const spacerKey = !horizontal ? 'height' : 'width'; const lastInitialIndex = this.props.initialNumToRender - 1; const {first, last} = this.state; - this._pushCells(cells, 0, lastInitialIndex); - if (!disableVirtualization && first > lastInitialIndex) { - const initBlock = this._getFrameMetricsApprox(lastInitialIndex); - const firstSpace = this._getFrameMetricsApprox(first).offset - - (initBlock.offset + initBlock.length); - cells.push( - - ); + this._pushCells(cells, stickyHeaderIndices, stickyIndicesFromProps, 0, lastInitialIndex); + const firstAfterInitial = Math.max(lastInitialIndex + 1, first); + if (!disableVirtualization && first > lastInitialIndex + 1) { + let insertedStickySpacer = false; + if (stickyIndicesFromProps.size > 0) { + const stickyOffset = ListHeaderComponent ? 1 : 0; + // See if there are any sticky headers in the virtualized space that we need to render. + for (let ii = firstAfterInitial - 1; ii > lastInitialIndex; ii--) { + if (stickyIndicesFromProps.has(ii + stickyOffset)) { + const initBlock = this._getFrameMetricsApprox(lastInitialIndex); + const stickyBlock = this._getFrameMetricsApprox(ii); + const leadSpace = stickyBlock.offset - (initBlock.offset + initBlock.length); + cells.push( + + ); + this._pushCells(cells, stickyHeaderIndices, stickyIndicesFromProps, ii, ii); + const trailSpace = this._getFrameMetricsApprox(first).offset - + (stickyBlock.offset + stickyBlock.length); + cells.push( + + ); + insertedStickySpacer = true; + break; + } + } + } + if (!insertedStickySpacer) { + const initBlock = this._getFrameMetricsApprox(lastInitialIndex); + const firstSpace = this._getFrameMetricsApprox(first).offset - + (initBlock.offset + initBlock.length); + cells.push( + + ); + } } - this._pushCells(cells, Math.max(lastInitialIndex + 1, first), last); + this._pushCells(cells, stickyHeaderIndices, stickyIndicesFromProps, firstAfterInitial, last); if (!this._hasWarned.keys && _usedIndexForKey) { console.warn( 'VirtualizedList: missing keys for items, make sure to specify a key property on each ' + @@ -392,6 +437,9 @@ class VirtualizedList extends React.PureComponent { } if (!disableVirtualization && last < itemCount - 1) { const lastFrame = this._getFrameMetricsApprox(last); + // Without getItemLayout, we limit our tail spacer to the _highestMeasuredFrameIndex to + // prevent the user for hyperscrolling into un-measured area because otherwise content will + // likely jump around as it renders in above the viewport. const end = this.props.getItemLayout ? itemCount - 1 : Math.min(itemCount - 1, this._highestMeasuredFrameIndex); @@ -400,14 +448,17 @@ class VirtualizedList extends React.PureComponent { (endFrame.offset + endFrame.length) - (lastFrame.offset + lastFrame.length); cells.push( - + ); } } - if (FooterComponent) { + if (ListFooterComponent) { + const element = React.isValidElement(ListFooterComponent) + ? ListFooterComponent + : ; cells.push( - + {element} ); } @@ -419,7 +470,8 @@ class VirtualizedList extends React.PureComponent { onScroll: this._onScroll, onScrollBeginDrag: this._onScrollBeginDrag, ref: this._captureScrollRef, - scrollEventThrottle: 50, // TODO: Android support + scrollEventThrottle: this.props.scrollEventThrottle, // TODO: Android support + stickyHeaderIndices, }, cells, ); @@ -435,9 +487,11 @@ class VirtualizedList extends React.PureComponent { } _averageCellLength = 0; + _hasDataChangedSinceEndReached = true; _hasWarned = {}; _highestMeasuredFrameIndex = 0; _headerLength = 0; + _fillRateHelper: FillRateHelper; _frames = {}; _footerLength = 0; _scrollMetrics = { @@ -454,7 +508,7 @@ class VirtualizedList extends React.PureComponent { this._scrollRef = ref; }; - _onCellLayout = (e, cellKey, index) => { + _onCellLayout(e, cellKey, index) { const layout = e.nativeEvent.layout; const next = { offset: this._selectOffset(layout), @@ -474,8 +528,11 @@ class VirtualizedList extends React.PureComponent { this._frames[cellKey] = next; this._highestMeasuredFrameIndex = Math.max(this._highestMeasuredFrameIndex, index); this._updateCellsToRenderBatcher.schedule(); + } else { + this._frames[cellKey].inLayout = true; } - }; + this._sampleFillRate('onCellLayout'); + } _onCellUnmount = (cellKey: string) => { const curr = this._frames[cellKey]; @@ -561,6 +618,15 @@ class VirtualizedList extends React.PureComponent { this._updateCellsToRenderBatcher.schedule(); }; + _sampleFillRate(sampleType: string) { + this._fillRateHelper.computeInfoSampled( + sampleType, + this.props, + this.state, + this._scrollMetrics, + ); + } + _onScroll = (e: Object) => { if (this.props.onScroll) { this.props.onScroll(e); @@ -573,9 +639,9 @@ class VirtualizedList extends React.PureComponent { if (dt > 500 && this._scrollMetrics.dt > 500 && (contentLength > (5 * visibleLength)) && !this._hasWarned.perf) { infoLog( - 'VirtualizedList: You have a large list that is slow to update - make sure ' + - 'shouldItemUpdate is implemented effectively and consider getItemLayout, PureComponent, ' + - 'etc.', + 'VirtualizedList: You have a large list that is slow to update - make sure your ' + + 'renderItem function renders components that follow React performance best practices ' + + 'like PureComponent, shouldComponentUpdate, etc.', {dt, prevDt: this._scrollMetrics.dt, contentLength}, ); this._hasWarned.perf = true; @@ -584,16 +650,22 @@ class VirtualizedList extends React.PureComponent { const velocity = dOffset / dt; this._scrollMetrics = {contentLength, dt, offset, timestamp, velocity, visibleLength}; const {data, getItemCount, onEndReached, onEndReachedThreshold, windowSize} = this.props; + + this._sampleFillRate('onScroll'); + this._updateViewableItems(data); if (!data) { return; } const distanceFromEnd = contentLength - visibleLength - offset; const itemCount = getItemCount(data); - if (distanceFromEnd < onEndReachedThreshold * visibleLength && - this._scrollMetrics.contentLength !== this._sentEndForContentLength && - this.state.last === itemCount - 1) { - // Only call onEndReached for a given content length once. + if (onEndReached && + this.state.last === itemCount - 1 && + distanceFromEnd < onEndReachedThreshold * visibleLength && + (this._hasDataChangedSinceEndReached || + this._scrollMetrics.contentLength !== this._sentEndForContentLength)) { + // Only call onEndReached once for a given dataset + content length. + this._hasDataChangedSinceEndReached = false; this._sentEndForContentLength = this._scrollMetrics.contentLength; onEndReached({distanceFromEnd}); } @@ -619,6 +691,7 @@ class VirtualizedList extends React.PureComponent { this._viewabilityHelper.recordInteraction(); this.props.onScrollBeginDrag && this.props.onScrollBeginDrag(e); }; + _updateCellsToRender = () => { const {data, disableVirtualization, getItemCount, onEndReachedThreshold} = this.props; this._updateViewableItems(data); @@ -669,7 +742,9 @@ class VirtualizedList extends React.PureComponent { } }; - _getFrameMetrics = (index: number): ?{length: number, offset: number, index: number} => { + _getFrameMetrics = (index: number): ?{ + length: number, offset: number, index: number, inLayout?: boolean, + } => { const {data, getItem, getItemCount, getItemLayout, keyExtractor} = this.props; invariant(getItemCount(data) > index, 'Tried to get frame for out of range index ' + index); const item = getItem(data, index); @@ -704,38 +779,30 @@ class CellRenderer extends React.Component { cellKey: string, index: number, item: Item, - onCellLayout: (event: Object, cellKey: string, index: number) => void, + onLayout: (event: Object) => void, // This is extracted by ScrollViewStickyHeader onUnmount: (cellKey: string) => void, parentProps: { renderItem: renderItemType, getItemLayout?: ?Function, - shouldItemUpdate: ( - props: {item: Item, index: number}, - nextProps: {item: Item, index: number} - ) => boolean, }, }; - _onLayout = (e) => { - this.props.onCellLayout(e, this.props.cellKey, this.props.index); - } componentWillUnmount() { this.props.onUnmount(this.props.cellKey); } - shouldComponentUpdate(nextProps, nextState) { - const curr = {item: this.props.item, index: this.props.index}; - const next = {item: nextProps.item, index: nextProps.index}; - return nextProps.parentProps.shouldItemUpdate(curr, next); - } render() { const {item, index, parentProps} = this.props; const {renderItem, getItemLayout} = parentProps; invariant(renderItem, 'no renderItem!'); const element = renderItem({item, index}); - if (getItemLayout && !parentProps.debug) { + if (getItemLayout && + !parentProps.debug && + !FillRateHelper.enabled()) { return element; } + // NOTE: that when this is a sticky header, `onLayout` will get automatically extracted and + // called explicitly by `ScrollViewStickyHeader`. return ( - + {element} ); diff --git a/Libraries/CustomComponents/Lists/VirtualizedSectionList.js b/Libraries/Lists/VirtualizedSectionList.js similarity index 72% rename from Libraries/CustomComponents/Lists/VirtualizedSectionList.js rename to Libraries/Lists/VirtualizedSectionList.js index 84ec2c2c37822e..d6de9f38d2cf1a 100644 --- a/Libraries/CustomComponents/Lists/VirtualizedSectionList.js +++ b/Libraries/Lists/VirtualizedSectionList.js @@ -1,32 +1,11 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * Facebook, Inc. ("Facebook") owns all right, title and interest, including - * all intellectual property and other proprietary rights, in and to the React - * Native CustomComponents software (the "Software"). Subject to your - * compliance with these terms, you are hereby granted a non-exclusive, - * worldwide, royalty-free copyright license to (1) use and copy the Software; - * and (2) reproduce and distribute the Software as part of your own software - * ("Your Software"). Facebook reserves all rights not expressly granted to - * you in this license agreement. - * - * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. - * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR - * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * @providesModule VirtualizedSectionList * @flow */ @@ -52,7 +31,7 @@ type SectionBase = { // Optional props will override list-wide props just for this section. renderItem?: ?({item: SectionItem, index: number}) => ?React.Element<*>, - SeparatorComponent?: ?ReactClass<*>, + ItemSeparatorComponent?: ?ReactClass<*>, keyExtractor?: (item: SectionItem) => string, // TODO: support more optional/override props @@ -69,22 +48,22 @@ type OptionalProps = { /** * Rendered after the last item in the last section. */ - ListFooterComponent?: ?ReactClass<*>, + ListFooterComponent?: ?(ReactClass<*> | React.Element<*>), /** * Rendered at the very beginning of the list. */ - ListHeaderComponent?: ?ReactClass<*>, + ListHeaderComponent?: ?(ReactClass<*> | React.Element<*>), /** * Default renderer for every item in every section. */ renderItem: ({item: Item, index: number}) => ?React.Element<*>, /** - * Rendered at the top of each section. In the future, a sticky option will be added. + * Rendered at the top of each section. */ renderSectionHeader?: ?({section: SectionT}) => ?React.Element<*>, /** * Rendered at the bottom of every Section, except the very last one, in place of the normal - * SeparatorComponent. + * ItemSeparatorComponent. */ SectionSeparatorComponent?: ?ReactClass<*>, /** @@ -113,13 +92,6 @@ type OptionalProps = { * Set this true while waiting for new data from a refresh. */ refreshing?: ?boolean, - /** - * This is an optional optimization to minimize re-rendering items. - */ - shouldItemUpdate: ( - prevProps: {item: Item, index: number}, - nextProps: {item: Item, index: number} - ) => boolean, }; export type Props = @@ -147,6 +119,24 @@ class VirtualizedSectionList data: [], }; + scrollToLocation(params: { + animated?: ?boolean, itemIndex: number, sectionIndex: number, viewPosition?: number + }) { + let index = params.itemIndex + 1; + for (let ii = 0; ii < params.sectionIndex; ii++) { + index += this.props.sections[ii].data.length + 1; + } + const toIndexParams = { + ...params, + index, + }; + this._listRef.scrollToIndex(toIndexParams); + } + + getListRef(): VirtualizedList { + return this._listRef; + } + _keyExtractor = (item: Item, index: number) => { const info = this._subExtractor(index); return (info && info.key) || String(index); @@ -210,11 +200,6 @@ class VirtualizedSectionList } } - _isItemSticky = (item, index) => { - const info = this._subExtractor(index); - return info && info.index == null; - }; - _renderItem = ({item, index}: {item: Item, index: number}) => { const info = this._subExtractor(index); if (!info) { @@ -223,8 +208,7 @@ class VirtualizedSectionList const {renderSectionHeader} = this.props; return renderSectionHeader ? renderSectionHeader({section: info.section}) : null; } else { - const renderItem = info.section.renderItem || - this.props.renderItem; + const renderItem = info.section.renderItem || this.props.renderItem; const SeparatorComponent = this._getSeparatorComponent(index, info); invariant(renderItem, 'no renderItem!'); return ( @@ -241,54 +225,47 @@ class VirtualizedSectionList if (!info) { return null; } - const SeparatorComponent = info.section.SeparatorComponent || this.props.ItemSeparatorComponent; + const ItemSeparatorComponent = info.section.ItemSeparatorComponent || this.props.ItemSeparatorComponent; const {SectionSeparatorComponent} = this.props; const isLastItemInList = index === this.state.childProps.getItemCount() - 1; const isLastItemInSection = info.index === info.section.data.length - 1; if (SectionSeparatorComponent && isLastItemInSection && !isLastItemInList) { return SectionSeparatorComponent; } - if (SeparatorComponent && !isLastItemInSection && !isLastItemInList) { - return SeparatorComponent; + if (ItemSeparatorComponent && !isLastItemInSection && !isLastItemInList) { + return ItemSeparatorComponent; } return null; } - _shouldItemUpdate = (prev, next) => { - const {shouldItemUpdate} = this.props; - if (!shouldItemUpdate || shouldItemUpdate(prev, next)) { - return true; - } - return this._getSeparatorComponent(prev.index) !== this._getSeparatorComponent(next.index); - } - _computeState(props: Props): State { - const itemCount = props.sections.reduce((v, section) => v + section.data.length + 1, 0); + const offset = props.ListHeaderComponent ? 1 : 0; + const stickyHeaderIndices = []; + const itemCount = props.sections.reduce( + (v, section) => { + stickyHeaderIndices.push(v + offset); + return v + section.data.length + 1; + }, + 0 + ); return { childProps: { ...props, - FooterComponent: this.props.ListFooterComponent, - HeaderComponent: this.props.ListHeaderComponent, renderItem: this._renderItem, - SeparatorComponent: undefined, // Rendered with renderItem + ItemSeparatorComponent: undefined, // Rendered with renderItem data: props.sections, getItemCount: () => itemCount, getItem, - isItemSticky: this._isItemSticky, keyExtractor: this._keyExtractor, onViewableItemsChanged: props.onViewableItemsChanged ? this._onViewableItemsChanged : undefined, - shouldItemUpdate: this._shouldItemUpdate, + stickyHeaderIndices: props.stickySectionHeadersEnabled ? stickyHeaderIndices : undefined, }, }; } constructor(props: Props, context: Object) { super(props, context); - warning( - !props.stickySectionHeadersEnabled, - 'VirtualizedSectionList: Sticky headers only supported with legacyImplementation for now.' - ); this.state = this._computeState(props); } @@ -297,8 +274,11 @@ class VirtualizedSectionList } render() { - return ; + return ; } + + _listRef: VirtualizedList; + _captureRef = (ref) => { this._listRef = ref; }; } function getItem(sections: ?Array, index: number): ?Item { diff --git a/Libraries/CustomComponents/Lists/__flowtests__/FlatList-flowtest.js b/Libraries/Lists/__flowtests__/FlatList-flowtest.js similarity index 98% rename from Libraries/CustomComponents/Lists/__flowtests__/FlatList-flowtest.js rename to Libraries/Lists/__flowtests__/FlatList-flowtest.js index 13fec8472a381f..b77b88b22a22b5 100644 --- a/Libraries/CustomComponents/Lists/__flowtests__/FlatList-flowtest.js +++ b/Libraries/Lists/__flowtests__/FlatList-flowtest.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/Libraries/CustomComponents/Lists/__flowtests__/SectionList-flowtest.js b/Libraries/Lists/__flowtests__/SectionList-flowtest.js similarity index 98% rename from Libraries/CustomComponents/Lists/__flowtests__/SectionList-flowtest.js rename to Libraries/Lists/__flowtests__/SectionList-flowtest.js index dc1941f2dda406..4f94e6dc2c3f58 100644 --- a/Libraries/CustomComponents/Lists/__flowtests__/SectionList-flowtest.js +++ b/Libraries/Lists/__flowtests__/SectionList-flowtest.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013-present, Facebook, Inc. + * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/Libraries/Lists/__tests__/FillRateHelper-test.js b/Libraries/Lists/__tests__/FillRateHelper-test.js new file mode 100644 index 00000000000000..9a0792367e7673 --- /dev/null +++ b/Libraries/Lists/__tests__/FillRateHelper-test.js @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +'use strict'; + +jest.unmock('FillRateHelper'); + +const FillRateHelper = require('FillRateHelper'); + +let rowFramesGlobal; +const dataGlobal = [{key: 'a'}, {key: 'b'}, {key: 'c'}, {key: 'd'}]; +function getFrameMetrics(index: number) { + const frame = rowFramesGlobal[dataGlobal[index].key]; + return {length: frame.height, offset: frame.y, inLayout: frame.inLayout}; +} + +function computeResult({helper, props, state, scroll}) { + return helper.computeInfoSampled( + 'test', + { + data: dataGlobal, + fillRateTrackingSampleRate: 1, + getItemCount: (data2) => data2.length, + initialNumToRender: 10, + ...(props || {}), + }, + {first: 0, last: 1, ...(state || {})}, + {offset: 0, visibleLength: 100, ...(scroll || {})}, + ); +} + +describe('computeInfoSampled', function() { + beforeEach(() => { + FillRateHelper.setSampleRate(1); + }); + + it('computes correct blankness of viewport', function() { + const helper = new FillRateHelper(getFrameMetrics); + rowFramesGlobal = { + a: {y: 0, height: 50, inLayout: true}, + b: {y: 50, height: 50, inLayout: true}, + }; + let result = computeResult({helper}); + expect(result).toBeNull(); + result = computeResult({helper, state: {last: 0}}); + expect(result.event.blankness).toBe(0.5); + result = computeResult({helper, scroll: {offset: 25}}); + expect(result.event.blankness).toBe(0.25); + result = computeResult({helper, scroll: {visibleLength: 400}}); + expect(result.event.blankness).toBe(0.75); + result = computeResult({helper, scroll: {offset: 100}}); + expect(result.event.blankness).toBe(1); + expect(result.aggregate.avg_blankness).toBe(0.5); + }); + + it('skips frames that are not in layout', function() { + const helper = new FillRateHelper(getFrameMetrics); + rowFramesGlobal = { + a: {y: 0, height: 10, inLayout: false}, + b: {y: 10, height: 30, inLayout: true}, + c: {y: 40, height: 40, inLayout: true}, + d: {y: 80, height: 20, inLayout: false}, + }; + const result = computeResult({helper, state: {last: 3}}); + expect(result.event.blankness).toBe(0.3); + }); + + it('sampling rate can disable', function() { + const helper = new FillRateHelper(getFrameMetrics); + rowFramesGlobal = { + a: {y: 0, height: 40, inLayout: true}, + b: {y: 40, height: 40, inLayout: true}, + }; + let result = computeResult({helper}); + expect(result.event.blankness).toBe(0.2); + + FillRateHelper.setSampleRate(0); + + result = computeResult({helper}); + expect(result).toBeNull(); + }); + + it('can handle multiple listeners and unsubscribe', function() { + const listeners = [jest.fn(), jest.fn(), jest.fn()]; + const subscriptions = listeners.map( + (listener) => FillRateHelper.addFillRateExceededListener(listener) + ); + subscriptions[1].remove(); + const helper = new FillRateHelper(getFrameMetrics); + rowFramesGlobal = { + a: {y: 0, height: 40, inLayout: true}, + b: {y: 40, height: 40, inLayout: true}, + }; + const result = computeResult({helper}); + expect(result.event.blankness).toBe(0.2); + expect(listeners[0]).toBeCalledWith(result); + expect(listeners[1]).not.toBeCalled(); + expect(listeners[2]).toBeCalledWith(result); + }); +}); diff --git a/Libraries/Lists/__tests__/FlatList-test.js b/Libraries/Lists/__tests__/FlatList-test.js new file mode 100644 index 00000000000000..e0d6fe56c8b1e5 --- /dev/null +++ b/Libraries/Lists/__tests__/FlatList-test.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +'use strict'; + +jest.disableAutomock(); + +const React = require('React'); +const ReactTestRenderer = require('react-test-renderer'); + +const FlatList = require('FlatList'); + +describe('FlatList', () => { + it('renders simple list', () => { + const component = ReactTestRenderer.create( + } + /> + ); + expect(component).toMatchSnapshot(); + }); + it('renders empty list', () => { + const component = ReactTestRenderer.create( + } + /> + ); + expect(component).toMatchSnapshot(); + }); + it('renders null list', () => { + const component = ReactTestRenderer.create( + } + /> + ); + expect(component).toMatchSnapshot(); + }); + it('renders all the bells and whistles', () => { + const component = ReactTestRenderer.create( + } + ListFooterComponent={() =>