diff --git a/.circleci/config.yml b/.circleci/config.yml index ace95d437146ac..7ee106ab67709b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,39 +1,42 @@ aliases: - - &restore-cache + - &restore-node-cache keys: - - v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }} + - v1-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }} # Fallback in case checksum fails - - v1-dependencies-{{ .Branch }}- - - &save-cache + - v1-dependencies-{{ arch }}-{{ .Branch }}- + + - &save-node-cache paths: - node_modules - key: v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }} + key: v1-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }} - &restore-cache-website keys: - - v1-website-dependencies-{{ .Branch }}-{{ checksum "website/package.json" }} + - v1-website-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "website/package.json" }} # Fallback in case checksum fails - - v1-website-dependencies-{{ .Branch }}- + - v1-website-dependencies-{{ arch }}-{{ .Branch }}- + - &save-cache-website paths: - website/node_modules - key: v1-website-dependencies-{{ .Branch }}-{{ checksum "website/package.json" }} + key: v1-website-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "website/package.json" }} - - &restore-cache-danger + - &restore-cache-analysis keys: - - v1-danger-dependencies-{{ .Branch }}-{{ checksum "danger/package.json" }} + - v1-analysis-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }}{{ checksum "danger/package.json" }} # Fallback in case checksum fails - - v1-danger-dependencies-{{ .Branch }}- - - &save-cache-danger + - v1-analysis-dependencies-{{ arch }}-{{ .Branch }}- + - &save-cache-analysis paths: - danger/node_modules - key: v1-danger-dependencies-{{ .Branch }}-{{ checksum "danger/package.json" }} + - node_modules + key: v1-analysis-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }}{{ checksum "danger/package.json" }} - &restore-cache-android-packages keys: - - v1-android-sdkmanager-packages-{{ checksum "scripts/circle-ci-android-setup.sh" }} + - v1-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }} # Fallback in case checksum fails - - v1-android-sdkmanager-packages- + - v1-android-sdkmanager-packages-{{ arch }}- - &save-cache-android-packages paths: - /opt/android/sdk/system-images/android-23 @@ -43,89 +46,168 @@ aliases: - /opt/android/sdk/platforms/android-19 - /opt/android/sdk/build-tools/23.0.1 - /opt/android/sdk/add-ons/addon-google_apis-google-23 - key: v1-android-sdkmanager-packages-{{ checksum "scripts/circle-ci-android-setup.sh" }} + key: v1-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }} - &restore-cache-ndk keys: - - v1-android-ndk-r10e-32-64 + - v1-android-ndk-{{ arch }}-r10e-32-64 + - &save-cache-ndk paths: - /opt/ndk - key: v1-android-ndk-r10e-32-64 + key: v1-android-ndk-{{ arch }}-r10e-32-64 - &restore-cache-buck-downloads keys: - - v1-buck-downloads-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }} + - v1-buck-downloads-{{ arch }}-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }} # Fallback in case checksum fails - - v1-buck-downloads-{{ .Branch }}- + - v1-buck-downloads-{{ arch }}-{{ .Branch }}- - &save-cache-buck-downloads paths: - "ReactAndroid/build/downloads" - key: v1-buck-downloads-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }} + key: v1-buck-downloads-{{ arch }}-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }} - &restore-cache-buck keys: - - v1-buck-v2017.09.04.02 + - v1-buck-{{ arch }}-v2017.09.04.02 - &save-cache-buck paths: - ~/buck - key: v1-buck-v2017.09.04.02 + key: v1-buck-{{ arch }}-v2017.09.04.02 - &restore-cache-watchman keys: - - v1-watchman-v4.9.0 + - v1-watchman-{{ arch }}-v4.9.0 - &save-cache-watchman paths: - ~/watchman - key: v1-watchman-v4.9.0 + key: v1-watchman-{{ arch }}-v4.9.0 + + - &install-node-dependencies + | + npm install --no-package-lock --no-spin --no-progress + + - &run-node-tests + | + npm test -- --maxWorkers=2 + npm run lint + npm run flow -- check + + - &filter-only-master-stable + branches: + only: + - /.*-stable/ + - master + + - &filter-ignore-gh-pages + branches: + ignore: gh-pages + + - &filter-ignore-master-stable + branches: + ignore: + - master + - /.*-stable/ + - gh-pages defaults: &defaults working_directory: ~/react-native version: 2 jobs: - test-node-8: + # Runs JavaScript tests on Node 8 + test-js-node-8: <<: *defaults docker: - image: circleci/node:8 steps: - checkout - - run: npm install --no-package-lock - - run: | - npm run lint - npm run flow -- check - npm test -- --maxWorkers=2 -# eslint - - run: - name: Analyze Code - command: | - npm install github@0.2.4 - cat <(echo eslint; npm run lint --silent -- --format=json; echo flow; npm run flow --silent -- check --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" CI_USER=$CIRCLE_PROJECT_USERNAME CI_REPO=$CIRCLE_PROJECT_REPONAME PULL_REQUEST_NUMBER=$CIRCLE_PR_NUMBER node bots/code-analysis-bot.js + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache + - run: *run-node-tests - test-node-6: + # Runs JavaScript tests on Node 6 + test-js-node-6: <<: *defaults docker: - image: circleci/node:6.11.0 steps: - checkout - - run: npm install - - run: | - npm run lint - npm run flow -- check - npm test -- --maxWorkers=2 + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache + - run: *run-node-tests - test-node-4: + # Runs JavaScript tests on Node 4 + test-js-node-4: <<: *defaults docker: - image: circleci/node:4.8.4 steps: - checkout - - run: npm install - - run: | - npm run lint - npm run flow -- check - npm test -- --maxWorkers=2 + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache + - run: *run-node-tests + + # Runs unit tests on iOS devices + test-objc-ios: + <<: *defaults + macos: + xcode: "9.0" + dependencies: + pre: + - xcrun instruments -w "iPhone 5s (10.3.1)" || true + steps: + - checkout + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache + - run: ./scripts/objc-test-ios.sh + + # Runs unit tests on tvOS devices + test-objc-tvos: + <<: *defaults + macos: + xcode: "9.0" + dependencies: + pre: + - xcrun instruments -w "Apple TV 1080p (10.0)" || true + steps: + - checkout + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache + - run: ./scripts/objc-test-tvos.sh + # Runs end to end tests + test-objc-e2e: + <<: *defaults + macos: + xcode: "9.0" + dependencies: + pre: + - xcrun instruments -w "iPhone 5s (10.3.1)" || true + steps: + - checkout + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache + - run: node ./scripts/run-ci-e2e-tests.js --ios --js --retries 3; + + # Checks podspec + test-podspec: + <<: *defaults + macos: + xcode: "9.0" + steps: + - checkout + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache + - run: ./scripts/process-podspecs.sh + test-website: <<: *defaults docker: @@ -136,7 +218,7 @@ jobs: name: Install Dependencies command: | cd website - npm install --no-package-lock + npm install --no-package-lock --no-spin --no-progress - run: name: Test Website command: | @@ -156,7 +238,7 @@ jobs: name: Install Dependencies command: | cd website - npm install --no-package-lock + npm install --no-package-lock --no-spin --no-progress - run: name: Build and Deploy Static Website command: | @@ -164,20 +246,22 @@ jobs: git config --global user.email "reactjs-bot@users.noreply.github.com" git config --global user.name "Website Deployment Script" echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc - echo "Deploying website..." cd website && GIT_USER=reactjs-bot npm run gh-pages else echo "Skipping deploy." fi + # Build JavaScript bundle for Android tests build-js-bundle: <<: *defaults docker: - image: circleci/node:8 steps: - checkout - - run: npm install --no-package-lock + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache - run: name: Build JavaScript Bundle command: node local-cli/cli.js bundle --max-workers 2 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js @@ -188,6 +272,7 @@ jobs: - store_artifacts: path: ReactAndroid/src/androidTest/assets/AndroidTestBundle.js + # Runs unit tests tests on Android test-android: <<: *defaults docker: @@ -254,7 +339,10 @@ jobs: command: | curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - sudo apt-get install -y nodejs - - run: npm install + - restore-cache: *restore-node-cache + - run: *install-node-dependencies + - save-cache: *save-node-cache +# TODO: Install and use watchman to speed up builds # - restore-cache: *restore-cache-watchman # - run: # name: Install Watchman Dependencies @@ -318,11 +406,11 @@ jobs: - run: name: Build and Install Test APK command: source scripts/circle-ci-android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS -# Failing test is expected +# TODO: Uncomment, test was already failing on Circle 1.0 # - run: # name: Run Installed APK with Tests # command: node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests -# Should be disabled pending on https://our.intern.facebook.com/intern/tasks?t=16912142 +# TODO: Should be disabled, pending on https://our.intern.facebook.com/intern/tasks?t=16912142 # - run: # name: Run Android End to End Tests # command: source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2 @@ -334,7 +422,7 @@ jobs: mkdir -p ~/junit/ find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ~/junit/ \; find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ~/junit/ \; -# TODO circle does not understand Buck's report, maybe need to transform xml slightly +# TODO: Circle does not understand Buck's report, maybe need to transform xml slightly # find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} ~/junit/ \; when: always - store_test_results: @@ -342,32 +430,120 @@ jobs: - store_artifacts: path: ~/junit + analyze-pull-request: + <<: *defaults + docker: + - image: circleci/node:8 + steps: + - checkout + - restore-cache: *restore-cache-analysis + - run: *install-node-dependencies + - run: + name: Install Dependencies + command: | + if [ -n "$CIRCLE_PULL_REQUEST" ]; then + npm install github@0.2.4 + cd danger + npm install --no-package-lock --no-spin --no-progress + else + echo "Skipping dependency installation." + fi + - save-cache: *save-cache-analysis +# Run Danger + - run: + name: Analyze Pull Request + command: | + if [ -n "$CIRCLE_PULL_REQUEST" ]; then + cd danger && DANGER_GITHUB_API_TOKEN="e622517d9f1136ea8900""07c6373666312cdfaa69" npm run danger + else + echo "Skipping pull request analysis." + fi + when: always +# Run eslint + - run: + name: Analyze Code + command: | + if [ -n "$CIRCLE_PULL_REQUEST" ]; then + cat <(echo eslint; npm run lint --silent -- --format=json; echo flow; npm run flow --silent -- check --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" CI_USER=$CIRCLE_PROJECT_USERNAME CI_REPO=$CIRCLE_PROJECT_REPONAME PULL_REQUEST_NUMBER=$CIRCLE_PR_NUMBER node bots/code-analysis-bot.js + else + echo "Skipping code analysis." + fi + + publish-npm: + <<: *defaults + docker: + - image: circleci/node:8 + steps: + - checkout + - run: *install-node-dependencies + - run: + name: Publish React Native Package + command: | + if [ -z "$CIRCLE_PULL_REQUEST" ]; then + echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc + git config --global user.email "reactjs-bot@users.noreply.github.com" + git config --global user.name "Website Deployment Script" + echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc + node ./scripts/publish-npm.js + else + echo "Skipping publication." + fi + # Workflows enables us to run multiple jobs in parallel workflows: version: 2 + test_node: jobs: - - test-node-8 - - test-node-6 -# Node 4 tests are already failing on Circle 1.0 -# - test-node-4 - website: - jobs: - - test-website - - deploy-website: - requires: - - test-website - filters: - branches: - only: - - /.*-stable/ - - master + - test-js-node-8: + filters: *filter-ignore-gh-pages + - test-js-node-6: + filters: *filter-ignore-gh-pages + +# TODO: Re-enable. Node 4 tests are already failing on Circle 1.0 +# - test-js-node-4: +# filters: *filter-ignore-gh-pages + + test_android: jobs: - build-js-bundle: - filters: - branches: - ignore: gh-pages + filters: *filter-ignore-gh-pages - test-android: requires: - build-js-bundle + + test_ios: + jobs: + - test-objc-ios: + filters: *filter-ignore-gh-pages + - test-objc-tvos: + filters: *filter-ignore-gh-pages + - test-objc-e2e: + filters: *filter-ignore-gh-pages +# TODO: Re-enable. Podspec tests are already failing on Travis +# - test-podspec: +# filters: *filter-ignore-gh-pages + + website: + jobs: + - test-website: + filters: *filter-ignore-gh-pages + - deploy-website: + requires: + - test-website + filters: *filter-only-master-stable + + analyze: + jobs: + - analyze-pull-request: + filters: *filter-ignore-master-stable + + deploy: + jobs: + - hold: + type: approval + - publish-npm: + requires: + - hold + filters: *filter-only-master-stable diff --git a/.flowconfig b/.flowconfig index 683702d269f9c7..15ea5d95a5ea41 100644 --- a/.flowconfig +++ b/.flowconfig @@ -46,12 +46,12 @@ suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeState suppress_type=$FixMe -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-4]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-4]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-7]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-7]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError unsafe.enable_getters_and_setters=true [version] -^0.54.0 +^0.57.0 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 05161b55a05169..126b18a9854ac3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,8 +9,38 @@ Happy contributing! --> +## Motivation + (Write your motivation here.) ## Test Plan (Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!) + +## Release Notes + diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 00000000000000..24203633432e73 --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,9 @@ +# Configuration for probot-no-response - https://github.com/probot/no-response + +# Number of days of inactivity before an Issue is closed for lack of response +daysUntilClose: 14 +# Label requiring a response +responseRequiredLabel: Needs more information +# Comment to post when closing an Issue for lack of response. Set to `false` to disable +closeComment: > + Closing this issue as more information is needed to debug this and we have not heard back from the author. diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000000000..dc018b58604d55 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,20 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - Good first issue + - For Discussion + - Core Team +# Label to use when marking an issue as stale +staleLabel: Stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. + Maybe the issue has been fixed in a recent release, or perhaps it is not affecting a lot of people. + If you think this issue should definitely remain open, please let us know why. + Thank you for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 00abb8680236db..00000000000000 --- a/.travis.yml +++ /dev/null @@ -1,49 +0,0 @@ -language: objective-c - -osx_image: xcode8.3 - -install: - - nvm install 8 - - rm -Rf "${TMPDIR}/jest_preprocess_cache" - - brew install yarn --ignore-dependencies - - brew install watchman - - yarn install - -script: - - if [[ "$TEST_TYPE" = objc-ios ]]; then travis_retry travis_wait 30 ./scripts/objc-test-ios.sh test; fi - - if [[ "$TEST_TYPE" = objc-tvos ]]; then travis_retry travis_wait 30 ./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 && travis_wait 30 ./scripts/process-podspecs.sh; fi - -cache: - - cocoapods - - yarn - -matrix: - - fast_finish: true # Fail the whole build as soon as one test type fails. Should help with Travis capacity issues (very long queues). - -# The order of these tests says which are more likely to run first and fail the whole build fast. -env: - - TEST_TYPE=objc-ios - - TEST_TYPE=podspecs - - TEST_TYPE=e2e-objc - - TEST_TYPE=objc-tvos - -branches: - only: - - master - - /^.*-stable$/ - -notifications: - email: - recipients: - - douglowder@mac.com # Doug Lowder built and maintains Apple TV specific code and wants to be notified about tvOS failures. - - eloy@artsy.net # Eloy Durán maintains the podspecs test and wants to be notified about failures. - on_failure: change - on_success: change - slack: - secure: oQL2C966v7/DtxNqfM7WowjY0R5mgLHR2qHkoucwK5iVrmaptnHr8fq01xlj7VT0kDwNLqT3n4+gtCviGw89lq71m3W76c8Pms/10jpjw+LwAfQPVizNw/Bx8MFNNmjDauK/auFxaybiLZupi7zd4xFGOZvScmFdfD4CAAp2OOA= - on_pull_requests: false - on_failure: change - on_success: change - webhooks: https://code.facebook.com/travis/webhook/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dde3cae8b537dd..0d9e9d7eccf64d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ There are many ways to contribute to React Native, and many of them do not invol * Simply start using React Native. Go through the [Getting Started](http://facebook.github.io/react-native/docs/getting-started.html) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](http://facebook.github.io/react-native/docs/contributing.html#reporting-new-issues). * Look through the [open issues](https://github.com/facebook/react-native/issues). Provide workarounds, ask for clarification, or suggest labels. Help [triage issues](http://facebook.github.io/react-native/docs/contributing.html#triaging-issues-and-pull-requests). -* If you find an issue you would like to fix, [open a pull request](http://facebook.github.io/react-native/docs/contributing.html#your-first-pull-request). Issues tagged as [_Good First Task_](https://github.com/facebook/react-native/labels/Good%20First%20Task) are a good place to get started. +* If you find an issue you would like to fix, [open a pull request](http://facebook.github.io/react-native/docs/contributing.html#your-first-pull-request). Issues tagged as [_Good first issue_](https://github.com/facebook/react-native/labels/Good%20first%20issue) are a good place to get started. * Read through the [React Native docs](http://facebook.github.io/react-native/docs). If you find anything that is confusing or can be improved, you can make edits by clicking "Improve this page" at the bottom of most docs. * Browse [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native) and answer questions. This will help you get familiarized with common pitfalls or misunderstandings, which can be useful when contributing updates to the documentation. * Take a look at the [features requested](https://react-native.canny.io/feature-requests) by others in the community and consider opening a pull request if you see something you want to work on. @@ -92,16 +92,15 @@ If you're only fixing a bug, it's fine to submit a pull request right away but w 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. -**Before submitting a pull request**, please make sure the following is done: +Please make sure the following is done when submitting a pull request: 1. Fork [the repository](https://github.com/facebook/react-native) and create your branch from `master`. 2. Add the copyright notice to the top of any new files you've added. -3. Describe your [**test plan**](https://facebook.github.io/react-native/docs/contributing.html#test-plan) in your commit. -4. Ensure [**tests pass**](https://facebook.github.io/react-native/docs/contributing.html#contrinuous-integration-tests) on both Travis and Circle CI. -5. Make sure your code lints (`npm run lint`). -6. If you haven't already, [sign the CLA](https://code.facebook.com/cla). +3. Describe your [**test plan**](/react-native/docs/contributing.html#test-plan) in your pull request description. Make sure to [test your changes](/react-native/docs/testing.html)! +4. Make sure your code lints (`npm run lint`). +5. If you haven't already, [sign the CLA](https://code.facebook.com/cla). -All pull requests should be opened against the `master` branch. +All pull requests should be opened against the `master` branch. After opening your pull request, ensure [**all tests pass**](/react-native/docs/contributing.html#contrinuous-integration-tests) on Circle CI. If a test fails and you believe it is unrelated to your change, leave a comment on the pull request explaining why. > **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. @@ -117,9 +116,8 @@ See [What is a Test Plan?](https://medium.com/@martinkonicek/what-is-a-test-plan #### Continuous integration tests -Make sure all **tests pass** on both [Travis][travis] and [Circle CI][circle]. PRs that break tests are unlikely to be merged. Learn more about [testing your changes here](https://facebook.github.io/react-native/docs/testing.html). +Make sure all **tests pass** on [Circle CI][circle]. PRs that break tests are unlikely to be merged. Learn more about [testing your changes here](/react-native/docs/testing.html). -[travis]: https://travis-ci.org/facebook/react-native [circle]: http://circleci.com/gh/facebook/react-native #### Breaking changes @@ -180,7 +178,7 @@ However, there are still some styles that the linter cannot pick up. #### JavaScript * Use semicolons; -* `'use strict';` +* ES6 standards * Prefer `'` over `"` * Do not use the optional parameters of `setTimeout` and `setInterval` * 80 character line length diff --git a/ContainerShip/scripts/run-android-ci-instrumentation-tests.js b/ContainerShip/scripts/run-android-ci-instrumentation-tests.js index e7e89ea8371d60..00a9f4955814db 100644 --- a/ContainerShip/scripts/run-android-ci-instrumentation-tests.js +++ b/ContainerShip/scripts/run-android-ci-instrumentation-tests.js @@ -45,7 +45,7 @@ const test_opts = { OFFSET: argv.offset, COUNT: argv.count -} +}; let max_test_class_length = Number.NEGATIVE_INFINITY; @@ -85,14 +85,14 @@ if (test_opts.COUNT != null && test_opts.OFFSET != null) { } return async.mapSeries(testClasses, (clazz, callback) => { - if(clazz.length > max_test_class_length) { + if (clazz.length > max_test_class_length) { max_test_class_length = clazz.length; } return async.retry(test_opts.RETRIES, (retryCb) => { const test_process = child_process.spawn('./ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh', [test_opts.PACKAGE, clazz], { stdio: 'inherit' - }) + }); const timeout = setTimeout(() => { test_process.kill(); @@ -106,7 +106,7 @@ return async.mapSeries(testClasses, (clazz, callback) => { test_process.on('exit', (code) => { clearTimeout(timeout); - if(code !== 0) { + if (code !== 0) { return retryCb(new Error(`Process exited with code: ${code}`)); } @@ -138,16 +138,16 @@ function print_test_suite_results(results) { function pad_output(num_chars) { let i = 0; - while(i < num_chars) { + while (i < num_chars) { process.stdout.write(' '); i++; } } results.forEach((test) => { - if(test.status === 'success') { + if (test.status === 'success') { color = colors.GREEN; passing_suites++; - } else if(test.status === 'failure') { + } else if (test.status === 'failure') { color = colors.RED; failing_suites++; } diff --git a/ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh b/ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh index ea5255052eee3c..5a9a976a97da4e 100755 --- a/ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh +++ b/ContainerShip/scripts/run-instrumentation-tests-via-adb-shell.sh @@ -29,7 +29,7 @@ def update(): # prevent CircleCI from killing the process for inactivity while not done: time.sleep(5) - print "Running in background. Waiting for 'adb' command reponse..." + print "Running in background. Waiting for 'adb' command response..." t = threading.Thread(target=update) t.dameon = True diff --git a/IntegrationTests/AsyncStorageTest.js b/IntegrationTests/AsyncStorageTest.js index 044392eb61cce6..ef7df1f1f365e5 100644 --- a/IntegrationTests/AsyncStorageTest.js +++ b/IntegrationTests/AsyncStorageTest.js @@ -155,7 +155,7 @@ function testOptimizedMultiGet() { let keys = batch.map(([key, value]) => key); AsyncStorage.multiSet(batch, (err1) => { // yes, twice on purpose - ;[1, 2].forEach((i) => { + [1, 2].forEach((i) => { expectAsyncNoError(`${i} testOptimizedMultiGet/multiSet`, err1); AsyncStorage.multiGet(keys, (err2, result) => { expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2); diff --git a/IntegrationTests/ImageCachePolicyTest.js b/IntegrationTests/ImageCachePolicyTest.js index 4d621dfc546c34..9732600712a88b 100644 --- a/IntegrationTests/ImageCachePolicyTest.js +++ b/IntegrationTests/ImageCachePolicyTest.js @@ -48,7 +48,7 @@ class ImageCachePolicyTest extends React.Component { const results: Array = TESTS.map(x => nextState[x]); if (!results.includes(undefined)) { - const result: boolean = results.reduce((x,y) => x === y === true, true) + const result: boolean = results.reduce((x,y) => x === y === true, true); TestModule.markTestPassed(result); } diff --git a/Libraries/ActionSheetIOS/ActionSheetIOS.js b/Libraries/ActionSheetIOS/ActionSheetIOS.js index ce4dbcd304e917..8c3dbe55a1dc7a 100644 --- a/Libraries/ActionSheetIOS/ActionSheetIOS.js +++ b/Libraries/ActionSheetIOS/ActionSheetIOS.js @@ -29,6 +29,20 @@ var ActionSheetIOS = { * * The 'callback' function takes one parameter, the zero-based index * of the selected item. + * + * Minimal example: + * + * ``` + * ActionSheetIOS.showActionSheetWithOptions({ + * options: ['Remove', 'Cancel'], + * destructiveButtonIndex: 1, + * cancelButtonIndex: 0, + * }, + * (buttonIndex) => { + * if (buttonIndex === 1) { // destructive action } + * }); + * ``` + * */ showActionSheetWithOptions(options: Object, callback: Function) { invariant( diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index 3e32591f7c9e89..71d616026f0b57 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -827,8 +827,11 @@ module.exports = { /** * 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'. + * 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: true`. + * In addition, loops can prevent `VirtualizedList`-based components from rendering + * more rows while the animation is running. You can pass `isInteraction: false` in the + * child animation config to fix this. */ loop, diff --git a/Libraries/Animated/src/Easing.js b/Libraries/Animated/src/Easing.js index 03c40917597baa..7b396ea4c0564c 100644 --- a/Libraries/Animated/src/Easing.js +++ b/Libraries/Animated/src/Easing.js @@ -162,11 +162,6 @@ class Easing { * times. * * http://easings.net/#easeInElastic - * - * Wolfram Plots: - * - * - http://tiny.cc/elastic_b_1 (bounciness = 1, default) - * - http://tiny.cc/elastic_b_3 (bounciness = 3) */ static elastic(bounciness: number = 1): (t: number) => number { const p = bounciness * Math.PI; diff --git a/Libraries/BatchedBridge/MessageQueue.js b/Libraries/BatchedBridge/MessageQueue.js index f7c296bc6d8331..0f017bfb481981 100644 --- a/Libraries/BatchedBridge/MessageQueue.js +++ b/Libraries/BatchedBridge/MessageQueue.js @@ -11,8 +11,6 @@ * @format */ -/*eslint no-bitwise: 0*/ - 'use strict'; const ErrorUtils = require('ErrorUtils'); @@ -26,7 +24,7 @@ export type SpyData = { type: number, module: ?string, method: string | number, - args: any, + args: any[], }; const TO_JS = 0; @@ -37,6 +35,7 @@ const METHOD_IDS = 1; const PARAMS = 2; const MIN_TIME_BETWEEN_FLUSHES_MS = 5; +// eslint-disable-next-line no-bitwise const TRACE_TAG_REACT_APPS = 1 << 17; const DEBUG_INFO_LIMIT = 32; @@ -46,17 +45,17 @@ let JSTimers = null; class MessageQueue { _lazyCallableModules: {[key: string]: (void) => Object}; - _queue: [Array, Array, Array, number]; - _successCallbacks: Array; - _failureCallbacks: Array; + _queue: [number[], number[], any[], number]; + _successCallbacks: (?Function)[]; + _failureCallbacks: (?Function)[]; _callID: number; _inCall: number; _lastFlush: number; _eventLoopStartTime: number; - _debugInfo: Object; - _remoteModuleTable: Object; - _remoteMethodTable: Object; + _debugInfo: {[number]: [number, number]}; + _remoteModuleTable: {[number]: string}; + _remoteMethodTable: {[number]: string[]}; __spy: ?(data: SpyData) => void; @@ -107,11 +106,7 @@ class MessageQueue { } } - callFunctionReturnFlushedQueue( - module: string, - method: string, - args: Array, - ) { + callFunctionReturnFlushedQueue(module: string, method: string, args: any[]) { this.__guard(() => { this.__callFunction(module, method, args); }); @@ -122,7 +117,7 @@ class MessageQueue { callFunctionReturnResultAndFlushedQueue( module: string, method: string, - args: Array, + args: any[], ) { let result; this.__guard(() => { @@ -132,7 +127,7 @@ class MessageQueue { return [result, this.flushedQueue()]; } - invokeCallbackAndReturnFlushedQueue(cbID: number, args: Array) { + invokeCallbackAndReturnFlushedQueue(cbID: number, args: any[]) { this.__guard(() => { this.__invokeCallback(cbID, args); }); @@ -178,7 +173,7 @@ class MessageQueue { enqueueNativeCall( moduleID: number, methodID: number, - params: Array, + params: any[], onFail: ?Function, onSucc: ?Function, ) { @@ -191,7 +186,9 @@ class MessageQueue { } // Encode callIDs into pairs of callback identifiers by shifting left and using the rightmost bit // to indicate fail (0) or success (1) + // eslint-disable-next-line no-bitwise onFail && params.push(this._callID << 1); + // eslint-disable-next-line no-bitwise onSucc && params.push((this._callID << 1) | 1); this._successCallbacks[this._callID] = onSucc; this._failureCallbacks[this._callID] = onFail; @@ -248,7 +245,7 @@ class MessageQueue { } } - createDebugLookup(moduleID: number, name: string, methods: Array) { + createDebugLookup(moduleID: number, name: string, methods: string[]) { if (__DEV__) { this._remoteModuleTable[moduleID] = name; this._remoteMethodTable[moduleID] = methods; @@ -279,7 +276,7 @@ class MessageQueue { Systrace.endEvent(); } - __callFunction(module: string, method: string, args: Array) { + __callFunction(module: string, method: string, args: any[]): any { this._lastFlush = new Date().getTime(); this._eventLoopStartTime = this._lastFlush; Systrace.beginEvent(`${module}.${method}()`); @@ -304,16 +301,18 @@ class MessageQueue { return result; } - __invokeCallback(cbID: number, args: Array) { + __invokeCallback(cbID: number, args: any[]) { this._lastFlush = new Date().getTime(); this._eventLoopStartTime = this._lastFlush; // The rightmost bit of cbID indicates fail (0) or success (1), the other bits are the callID shifted left. + // eslint-disable-next-line no-bitwise const callID = cbID >>> 1; - const callback = - cbID & 1 - ? this._successCallbacks[callID] - : this._failureCallbacks[callID]; + // eslint-disable-next-line no-bitwise + const isSuccess = cbID & 1; + const callback = isSuccess + ? this._successCallbacks[callID] + : this._failureCallbacks[callID]; if (__DEV__) { const debug = this._debugInfo[callID]; @@ -344,7 +343,7 @@ class MessageQueue { } this._successCallbacks[callID] = this._failureCallbacks[callID] = null; - callback.apply(null, args); + callback(...args); if (__DEV__) { Systrace.endEvent(); diff --git a/Libraries/BatchedBridge/__tests__/MessageQueue-test.js b/Libraries/BatchedBridge/__tests__/MessageQueue-test.js index d9cd5a4e358a88..d48c77313e0254 100644 --- a/Libraries/BatchedBridge/__tests__/MessageQueue-test.js +++ b/Libraries/BatchedBridge/__tests__/MessageQueue-test.js @@ -65,20 +65,20 @@ describe('MessageQueue', function() { it('should call the stored callback', () => { let done = false; queue.enqueueNativeCall(0, 1, [], () => {}, () => { done = true; }); - queue.__invokeCallback(1); + queue.__invokeCallback(1, []); expect(done).toEqual(true); }); it('should throw when calling the same callback twice', () => { queue.enqueueNativeCall(0, 1, [], () => {}, () => {}); - queue.__invokeCallback(1); - expect(() => queue.__invokeCallback(1)).toThrow(); + queue.__invokeCallback(1, []); + expect(() => queue.__invokeCallback(1, [])).toThrow(); }); it('should throw when calling both success and failure callback', () => { queue.enqueueNativeCall(0, 1, [], () => {}, () => {}); - queue.__invokeCallback(1); - expect(() => queue.__invokeCallback(0)).toThrow(); + queue.__invokeCallback(1, []); + expect(() => queue.__invokeCallback(0, [])).toThrow(); }); it('should throw when calling with unknown module', () => { diff --git a/Libraries/Components/ActivityIndicator/ActivityIndicator.js b/Libraries/Components/ActivityIndicator/ActivityIndicator.js index 1ed1a7ebcc3e2d..3a47a3d8f4f9b3 100644 --- a/Libraries/Components/ActivityIndicator/ActivityIndicator.js +++ b/Libraries/Components/ActivityIndicator/ActivityIndicator.js @@ -14,8 +14,9 @@ const ColorPropType = require('ColorPropType'); const NativeMethodsMixin = require('NativeMethodsMixin'); const Platform = require('Platform'); -const React = require('React'); +const ProgressBarAndroid = require('ProgressBarAndroid'); const PropTypes = require('prop-types'); +const React = require('React'); const StyleSheet = require('StyleSheet'); const View = require('View'); const ViewPropTypes = require('ViewPropTypes'); @@ -135,16 +136,20 @@ const ActivityIndicator = createReactClass({ break; } + const nativeProps = { + ...props, + style: sizeStyle, + styleAttr: 'Normal', + indeterminate: true, + }; + return ( - - + + {Platform.OS === 'ios' ? ( + + ) : ( + + )} ); } @@ -169,18 +174,7 @@ if (Platform.OS === 'ios') { var RCTActivityIndicator = requireNativeComponent( 'RCTActivityIndicatorView', ActivityIndicator, - {nativeOnly: {activityIndicatorViewStyle: true}}, - ); -} else if (Platform.OS === 'android') { - var RCTActivityIndicator = requireNativeComponent( - 'AndroidProgressBar', - ActivityIndicator, - // Ignore props that are specific to non inderterminate ProgressBar. - {nativeOnly: { - indeterminate: true, - progress: true, - styleAttr: true, - }}, + { nativeOnly: { activityIndicatorViewStyle: true } } ); } diff --git a/Libraries/Components/Button.js b/Libraries/Components/Button.js index 9729f8832bd440..fc3f2ef5aab3ad 100644 --- a/Libraries/Components/Button.js +++ b/Libraries/Components/Button.js @@ -58,6 +58,7 @@ class Button extends React.Component<{ accessibilityLabel?: ?string, disabled?: ?boolean, testID?: ?string, + hasTVPreferredFocus?: ?boolean, }> { static propTypes = { /** @@ -84,6 +85,12 @@ class Button extends React.Component<{ * Used to locate this view in end-to-end tests. */ testID: PropTypes.string, + /** + * *(Apple TV only)* TV preferred focus (see documentation for the View component). + * + * @platform ios + */ + hasTVPreferredFocus: PropTypes.bool, }; render() { @@ -92,6 +99,7 @@ class Button extends React.Component<{ color, onPress, title, + hasTVPreferredFocus, disabled, testID, } = this.props; @@ -121,6 +129,7 @@ class Button extends React.Component<{ accessibilityComponentType="button" accessibilityLabel={accessibilityLabel} accessibilityTraits={accessibilityTraits} + hasTVPreferredFocus={hasTVPreferredFocus} testID={testID} disabled={disabled} onPress={onPress}> diff --git a/Libraries/Components/CheckBox/CheckBox.js b/Libraries/Components/CheckBox/CheckBox.android.js similarity index 98% rename from Libraries/Components/CheckBox/CheckBox.js rename to Libraries/Components/CheckBox/CheckBox.android.js index 183cbafa9b4372..bd69b0bed74727 100644 --- a/Libraries/Components/CheckBox/CheckBox.js +++ b/Libraries/Components/CheckBox/CheckBox.android.js @@ -27,7 +27,7 @@ type DefaultProps = { }; /** - * Renders a boolean input. + * Renders a boolean input (Android only). * * This is a controlled component that requires an `onValueChange` callback that * updates the `value` prop in order for the component to reflect user actions. diff --git a/Libraries/Components/CheckBox/CheckBox.ios.js b/Libraries/Components/CheckBox/CheckBox.ios.js new file mode 100644 index 00000000000000..02124a885a6477 --- /dev/null +++ b/Libraries/Components/CheckBox/CheckBox.ios.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2017-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 CheckBox + * @flow + * @format + */ +'use strict'; + +module.exports = require('UnimplementedView'); diff --git a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js index 21fc5185a611bd..6f076e53dc3f0b 100644 --- a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js +++ b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js @@ -10,13 +10,14 @@ */ 'use strict'; -const ActivityIndicator = require('ActivityIndicator'); const ColorPropType = require('ColorPropType'); const PropTypes = require('prop-types'); const React = require('React'); const ReactNative = require('ReactNative'); const ViewPropTypes = require('ViewPropTypes'); +const requireNativeComponent = require('requireNativeComponent'); + const STYLE_ATTRIBUTES = [ 'Horizontal', 'Normal', @@ -78,6 +79,10 @@ class ProgressBarAndroid extends ReactNative.NativeComponent { * - LargeInverse */ styleAttr: PropTypes.oneOf(STYLE_ATTRIBUTES), + /** + * Whether to show the ProgressBar (true, the default) or hide it (false). + */ + animating: PropTypes.bool, /** * If the progress bar will show indeterminate progress. Note that this * can only be false if styleAttr is Horizontal. @@ -99,21 +104,23 @@ class ProgressBarAndroid extends ReactNative.NativeComponent { static defaultProps = { styleAttr: 'Normal', - indeterminate: true + indeterminate: true, + animating: true, }; - componentDidMount() { - if (this.props.indeterminate && this.props.styleAttr !== 'Horizontal') { - console.warn( - 'Circular indeterminate `ProgressBarAndroid`' + - 'is deprecated. Use `ActivityIndicator` instead.' - ); - } - } - render() { - return ; + return ; } } +const AndroidProgressBar = requireNativeComponent( + 'AndroidProgressBar', + ProgressBarAndroid, + { + nativeOnly: { + animating: true, + }, + } +); + module.exports = ProgressBarAndroid; diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index dd4ca0cab1d2ed..6017f833983517 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -548,7 +548,7 @@ var ScrollResponderMixin = { warning( typeof keyboardShouldPersistTaps !== 'boolean', `'keyboardShouldPersistTaps={${keyboardShouldPersistTaps}}' is deprecated. ` - + `Use 'keyboardShouldPersistTaps="${keyboardShouldPersistTaps ? "always" : "never"}"' instead` + + `Use 'keyboardShouldPersistTaps="${keyboardShouldPersistTaps ? 'always' : 'never'}"' instead` ); this.keyboardWillOpenTo = null; diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index fcf9230a343e91..c3502df7294f82 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -284,7 +284,7 @@ const ScrollView = createReactClass({ * When false, the view cannot be scrolled via touch interaction. * The default value is true. * - * Note that the view can be always be scrolled by calling `scrollTo`. + * Note that the view can always be scrolled by calling `scrollTo`. */ scrollEnabled: PropTypes.bool, /** @@ -331,7 +331,6 @@ const ScrollView = createReactClass({ * with `horizontal={true}`. */ stickyHeaderIndices: PropTypes.arrayOf(PropTypes.number), - style: StyleSheetPropType(ViewStylePropTypes), /** * When set, causes the scroll view to stop at multiples of the value of * `snapToInterval`. This can be used for paginating through children @@ -447,7 +446,8 @@ const ScrollView = createReactClass({ }, componentWillMount: function() { - this._scrollAnimatedValue = new Animated.Value(0); + this._scrollAnimatedValue = new Animated.Value(this.props.contentOffset ? this.props.contentOffset.y : 0); + this._scrollAnimatedValue.setOffset(this.props.contentInset ? this.props.contentInset.top : 0); this._stickyHeaderRefs = new Map(); this._headerLayoutYs = new Map(); }, @@ -912,7 +912,6 @@ if (Platform.OS === 'android') { (ScrollView: React.ComponentType), nativeOnlyProps, ); - // $FlowFixMe (bvaughn) Update ComponentInterface in ViewPropTypes to include a string type (for Fiber host components) in a follow-up. RCTScrollContentView = requireNativeComponent('RCTScrollContentView', View); } diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 2ee75eb973e932..cdd1ad18d5c557 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -227,6 +227,11 @@ const TextInput = createReactClass({ * @platform android */ autoGrow: PropTypes.bool, + /** + * Specifies whether fonts should scale to respect Text Size accessibility settings. The + * default is `true`. + */ + allowFontScaling: PropTypes.bool, /** * If `false`, text is not editable. The default value is `true`. */ @@ -240,6 +245,25 @@ const TextInput = createReactClass({ * - `numeric` * - `email-address` * - `phone-pad` + * + * *iOS Only* + * + * The following values work on iOS only: + * + * - `ascii-capable` + * - `numbers-and-punctuation` + * - `url` + * - `number-pad` + * - `name-phone-pad` + * - `decimal-pad` + * - `twitter` + * - `web-search` + * + * *Android Only* + * + * The following values work on Android only: + * + * - `visible-password` */ keyboardType: PropTypes.oneOf([ // Cross-platform @@ -256,6 +280,8 @@ const TextInput = createReactClass({ 'decimal-pad', 'twitter', 'web-search', + // Android-only + 'visible-password', ]), /** * Determines the color of the keyboard. @@ -425,14 +451,14 @@ const TextInput = createReactClass({ /** * The string that will be rendered before text input has been entered. */ - placeholder: PropTypes.node, + placeholder: PropTypes.string, /** * The text color of the placeholder string. */ placeholderTextColor: ColorPropType, /** * If `true`, the text input obscures the text entered so that sensitive text - * like passwords stay secure. The default value is `false`. + * like passwords stay secure. The default value is `false`. Does not work with 'multiline={true}'. */ secureTextEntry: PropTypes.bool, /** @@ -507,7 +533,17 @@ const TextInput = createReactClass({ */ blurOnSubmit: PropTypes.bool, /** - * Note that not all Text styles are supported, + * Note that not all Text styles are supported, an incomplete list of what is not supported includes: + * + * - `borderLeftWidth` + * - `borderTopWidth` + * - `borderRightWidth` + * - `borderBottomWidth` + * - `borderTopLeftRadius` + * - `borderTopRightRadius` + * - `borderBottomRightRadius` + * - `borderBottomLeftRadius` + * * see [Issue#7070](https://github.com/facebook/react-native/issues/7070) * for more detail. * @@ -566,7 +602,11 @@ const TextInput = createReactClass({ */ caretHidden: PropTypes.bool, }, - + getDefaultProps(): Object { + return { + allowFontScaling: true, + }; + }, /** * `NativeMethodsMixin` will look for this when invoking `setNativeProps`. We * make `this` look like an actual native component class. @@ -704,7 +744,7 @@ const TextInput = createReactClass({ 'Cannot specify both value and children.' ); if (childCount >= 1) { - children = {children}; + children = {children}; } if (props.inputView) { children = [children, props.inputView]; diff --git a/Libraries/Components/TimePickerAndroid/TimePickerAndroid.android.js b/Libraries/Components/TimePickerAndroid/TimePickerAndroid.android.js index e1e3f7550d4823..e837075ded13b4 100644 --- a/Libraries/Components/TimePickerAndroid/TimePickerAndroid.android.js +++ b/Libraries/Components/TimePickerAndroid/TimePickerAndroid.android.js @@ -44,6 +44,10 @@ class TimePickerAndroid { * * `is24Hour` (boolean) - If `true`, the picker uses the 24-hour format. If `false`, * the picker shows an AM/PM chooser. If undefined, the default for the current locale * is used. + * * `mode` (`enum('clock', 'spinner', 'default')`) - set the time picker mode + * - 'clock': Show a time picker in clock mode. + * - 'spinner': Show a time picker in spinner mode. + * - 'default': Show a default time picker based on Android versions. * * Returns a Promise which will be invoked an object containing `action`, `hour` (0-23), * `minute` (0-59) if the user picked a time. If the user dismissed the dialog, the Promise will diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 2de2777e99e96a..1bcd162bb4fae9 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -138,7 +138,7 @@ const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30}; * * AppRegistry.registerComponent('App', () => App) * ``` - * + * */ var TouchableHighlight = createReactClass({ diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 4a46cdab939f62..1dced65d627528 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -129,6 +129,12 @@ var TouchableOpacity = createReactClass({ * active. Defaults to 0.2. */ activeOpacity: PropTypes.number, + /** + * *(Apple TV only)* TV preferred focus (see documentation for the View component). + * + * @platform ios + */ + hasTVPreferredFocus: PropTypes.bool, /** * Apple TV parallax effects */ @@ -246,6 +252,7 @@ var TouchableOpacity = createReactClass({ testID={this.props.testID} onLayout={this.props.onLayout} isTVSelectable={true} + hasTVPreferredFocus={this.props.hasTVPreferredFocus} tvParallaxProperties={this.props.tvParallaxProperties} hitSlop={this.props.hitSlop} onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder} diff --git a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap index 12896cafcaef50..2bd2f08e376c2c 100644 --- a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap +++ b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap @@ -31,7 +31,6 @@ exports[`TouchableHighlight renders correctly 1`] = ` Touchable diff --git a/Libraries/Components/View/ReactNativeStyleAttributes.js b/Libraries/Components/View/ReactNativeStyleAttributes.js index b5cebe4f7df1c0..821c5fa9167e77 100644 --- a/Libraries/Components/View/ReactNativeStyleAttributes.js +++ b/Libraries/Components/View/ReactNativeStyleAttributes.js @@ -40,6 +40,8 @@ ReactNativeStyleAttributes.borderColor = colorAttributes; ReactNativeStyleAttributes.borderLeftColor = colorAttributes; ReactNativeStyleAttributes.borderRightColor = colorAttributes; ReactNativeStyleAttributes.borderTopColor = colorAttributes; +ReactNativeStyleAttributes.borderStartColor = colorAttributes; +ReactNativeStyleAttributes.borderEndColor = colorAttributes; ReactNativeStyleAttributes.color = colorAttributes; ReactNativeStyleAttributes.shadowColor = colorAttributes; ReactNativeStyleAttributes.textDecorationColor = colorAttributes; diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index 49fff29d81883d..3c6886249a74ce 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -31,11 +31,17 @@ var ViewStylePropTypes = { borderRightColor: ColorPropType, borderBottomColor: ColorPropType, borderLeftColor: ColorPropType, + borderStartColor: ColorPropType, + borderEndColor: ColorPropType, borderRadius: ReactPropTypes.number, borderTopLeftRadius: ReactPropTypes.number, borderTopRightRadius: ReactPropTypes.number, + borderTopStartRadius: ReactPropTypes.number, + borderTopEndRadius: ReactPropTypes.number, borderBottomLeftRadius: ReactPropTypes.number, borderBottomRightRadius: ReactPropTypes.number, + borderBottomStartRadius: ReactPropTypes.number, + borderBottomEndRadius: ReactPropTypes.number, borderStyle: ReactPropTypes.oneOf(['solid', 'dotted', 'dashed']), borderWidth: ReactPropTypes.number, borderTopWidth: ReactPropTypes.number, diff --git a/Libraries/Components/ViewPager/ViewPagerAndroid.android.js b/Libraries/Components/ViewPager/ViewPagerAndroid.android.js index 6e5bde8df45c24..a44cd15b755c72 100644 --- a/Libraries/Components/ViewPager/ViewPagerAndroid.android.js +++ b/Libraries/Components/ViewPager/ViewPagerAndroid.android.js @@ -37,7 +37,7 @@ export type ViewPagerScrollState = $Enum<{ * * It is important all children are ``s and not composite components. * You can set style properties like `padding` or `backgroundColor` for each - * child. + * child. It is also important that each child have a `key` prop. * * Example: * @@ -47,10 +47,10 @@ export type ViewPagerScrollState = $Enum<{ * - * + * * First page * - * + * * Second page * * diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 0752028e72ae66..33ca08cc684e9b 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -241,6 +241,7 @@ class WebView extends React.Component { /** * The amount by which the web view content is inset from the edges of * the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}. + * @platform ios */ contentInset: EdgeInsetsPropType, /** @@ -376,7 +377,7 @@ class WebView extends React.Component { 'always', 'compatibility' ]), - + /** * Override the native component used to render the WebView. Enables a custom native * WebView which uses the same JavaScript as the original WebView. diff --git a/Libraries/Core/Devtools/symbolicateStackTrace.js b/Libraries/Core/Devtools/symbolicateStackTrace.js index 586034b4746c1f..283e4b78cc94aa 100644 --- a/Libraries/Core/Devtools/symbolicateStackTrace.js +++ b/Libraries/Core/Devtools/symbolicateStackTrace.js @@ -16,7 +16,7 @@ const getDevServer = require('getDevServer'); const {SourceCode} = require('NativeModules'); // Avoid requiring fetch on load of this module; see symbolicateStackTrace -let fetch; +let fetch; import type {StackFrame} from 'parseErrorStack'; @@ -25,13 +25,13 @@ function isSourcedFromDisk(sourcePath: string): boolean { } async function symbolicateStackTrace(stack: Array): Promise> { - // RN currently lazy loads whatwg-fetch using a custom fetch module, which, + // RN currently lazy loads whatwg-fetch using a custom fetch module, which, // when called for the first time, requires and re-exports 'whatwg-fetch'. - // However, when a dependency of the project tries to require whatwg-fetch - // either directly or indirectly, whatwg-fetch is required before + // However, when a dependency of the project tries to require whatwg-fetch + // either directly or indirectly, whatwg-fetch is required before // RN can lazy load whatwg-fetch. As whatwg-fetch checks - // for a fetch polyfill before loading, it will in turn try to load - // RN's fetch module, which immediately tries to import whatwg-fetch AGAIN. + // for a fetch polyfill before loading, it will in turn try to load + // RN's fetch module, which immediately tries to import whatwg-fetch AGAIN. // This causes a circular require which results in RN's fetch module // exporting fetch as 'undefined'. // The fix below postpones trying to load fetch until the first call to symbolicateStackTrace. diff --git a/Libraries/Core/InitializeCore.js b/Libraries/Core/InitializeCore.js index dcf3a640170aa9..0006ee12ce9453 100644 --- a/Libraries/Core/InitializeCore.js +++ b/Libraries/Core/InitializeCore.js @@ -116,22 +116,9 @@ if (!global.__fbDisableExceptionsManager) { ErrorUtils.setGlobalHandler(handleError); } -const formatVersion = version => - `${version.major}.${version.minor}.${version.patch}` + - (version.prerelease !== null ? `-${version.prerelease}` : ''); - -const ReactNativeVersion = require('ReactNativeVersion'); -const nativeVersion = require('NativeModules').PlatformConstants.reactNativeVersion; -if (ReactNativeVersion.version.major !== nativeVersion.major || - ReactNativeVersion.version.minor !== nativeVersion.minor) { - throw new Error( - `React Native version mismatch.\n\nJavaScript version: ${formatVersion(ReactNativeVersion.version)}\n` + - `Native version: ${formatVersion(nativeVersion)}\n\n` + - 'Make sure that you have rebuilt the native code. If the problem persists ' + - 'try clearing the watchman and packager caches with `watchman watch-del-all ' + - '&& react-native start --reset-cache`.' - ); -} +// Check for compatibility between the JS and native code +const ReactNativeVersionCheck = require('ReactNativeVersionCheck'); +ReactNativeVersionCheck.checkVersions(); // Set up collections const _shouldPolyfillCollection = require('_shouldPolyfillES6Collection'); @@ -219,6 +206,26 @@ BatchedBridge.registerLazyCallableModule('RCTDeviceEventEmitter', () => require( BatchedBridge.registerLazyCallableModule('RCTNativeAppEventEmitter', () => require('RCTNativeAppEventEmitter')); BatchedBridge.registerLazyCallableModule('PerformanceLogger', () => require('PerformanceLogger')); +global.fetchBundle = function( + bundleId: number, + callback: (?Error) => void, +) { + const {BundleFetcher} = require('NativeModules'); + if (!BundleFetcher) { + throw new Error('BundleFetcher is missing'); + } + + BundleFetcher.fetchBundle(bundleId, (errorObject: ?{message: string, code: string}) => { + if (errorObject) { + const error = new Error(errorObject.message); + (error: any).code = errorObject.code; + callback(error); + } + + callback(null); + }); +}; + // Set up devtools if (__DEV__) { if (!global.__RCTProfileIsProfiling) { @@ -232,6 +239,12 @@ if (__DEV__) { // Set up inspector const JSInspector = require('JSInspector'); + /* $FlowFixMe(>=0.56.0 site=react_native_oss) This comment suppresses an + * error found when Flow v0.56 was deployed. To see the error delete this + * comment and run Flow. */ + /* $FlowFixMe(>=0.56.0 site=react_native_fb,react_native_oss) This comment + * suppresses an error found when Flow v0.56 was deployed. To see the error + * delete this comment and run Flow. */ JSInspector.registerAgent(require('NetworkAgent')); } } diff --git a/Libraries/Core/ReactNativeVersionCheck.js b/Libraries/Core/ReactNativeVersionCheck.js new file mode 100644 index 00000000000000..33e494e5887d85 --- /dev/null +++ b/Libraries/Core/ReactNativeVersionCheck.js @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2017-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 ReactNativeVersionCheck + * @flow + * @format + */ +'use strict'; + +const {PlatformConstants} = require('NativeModules'); +const ReactNativeVersion = require('ReactNativeVersion'); + +/** + * Checks that the version of this React Native JS is compatible with the native + * code, throwing an error if it isn't. + * + * The existence of this module is part of the public interface of React Native + * even though it is used only internally within React Native. React Native + * implementations for other platforms (ex: Windows) may override this module + * and rely on its existence as a separate module. + */ +exports.checkVersions = function checkVersions(): void { + if (!PlatformConstants) { + return; + } + + const nativeVersion = PlatformConstants.reactNativeVersion; + if ( + ReactNativeVersion.version.major !== nativeVersion.major || + ReactNativeVersion.version.minor !== nativeVersion.minor + ) { + throw new Error( + `React Native version mismatch.\n\nJavaScript version: ${_formatVersion( + ReactNativeVersion.version, + )}\n` + + `Native version: ${_formatVersion(nativeVersion)}\n\n` + + 'Make sure that you have rebuilt the native code. If the problem ' + + 'persists try clearing the Watchman and packager caches with ' + + '`watchman watch-del-all && react-native start --reset-cache`.', + ); + } +}; + +function _formatVersion(version): string { + return ( + `${version.major}.${version.minor}.${version.patch}` + + (version.prerelease !== null ? `-${version.prerelease}` : '') + ); +} diff --git a/Libraries/Core/__tests__/ReactNativeVersionCheck-test.js b/Libraries/Core/__tests__/ReactNativeVersionCheck-test.js new file mode 100644 index 00000000000000..dbafabcd566bf2 --- /dev/null +++ b/Libraries/Core/__tests__/ReactNativeVersionCheck-test.js @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2017-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. + * + * @format + */ +'use strict'; + +describe('checkVersion', () => { + describe('in development', () => { + _setDevelopmentModeForTests(true); + _defineCheckVersionTests(); + }); + + describe('in production', () => { + _setDevelopmentModeForTests(false); + _defineCheckVersionTests(); + }); +}); + +function _setDevelopmentModeForTests(dev) { + let originalDev; + + beforeAll(() => { + originalDev = global.__DEV__; + global.__DEV__ = dev; + }); + + afterAll(() => { + global.__DEV__ = originalDev; + }); +} + +function _defineCheckVersionTests() { + afterEach(() => { + jest.resetModules(); + }); + + it('passes when all the versions are zero', () => { + jest.dontMock('ReactNativeVersion'); + _mockNativeVersion(0, 0, 0); + + const ReactNativeVersion = require('ReactNativeVersion'); + const ReactNativeVersionCheck = require('ReactNativeVersionCheck'); + expect(ReactNativeVersion).toMatchObject({ + version: {major: 0, minor: 0, patch: 0, prerelease: null}, + }); + expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow(); + }); + + it('passes when the minor matches when the major is zero', () => { + _mockJsVersion(0, 1, 0); + _mockNativeVersion(0, 1, 0); + + const ReactNativeVersionCheck = require('ReactNativeVersionCheck'); + expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow(); + }); + + it("throws when the minor doesn't match when the major is zero", () => { + _mockJsVersion(0, 1, 0); + _mockNativeVersion(0, 2, 0); + + const ReactNativeVersionCheck = require('ReactNativeVersionCheck'); + expect(() => ReactNativeVersionCheck.checkVersions()).toThrowError( + /React Native version mismatch/, + ); + }); + + it("throws when the major doesn't match", () => { + _mockJsVersion(1, 0, 0); + _mockNativeVersion(2, 0, 0); + + const ReactNativeVersionCheck = require('ReactNativeVersionCheck'); + expect(() => ReactNativeVersionCheck.checkVersions()).toThrowError( + /React Native version mismatch/, + ); + }); + + it("doesn't throw if the patch doesn't match", () => { + _mockJsVersion(0, 1, 0); + _mockNativeVersion(0, 1, 2); + + const ReactNativeVersionCheck = require('ReactNativeVersionCheck'); + expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow(); + }); + + it("doesn't throw if the prerelease doesn't match", () => { + _mockJsVersion(0, 1, 0, 'beta.0'); + _mockNativeVersion(0, 1, 0, 'alpha.1'); + + const ReactNativeVersionCheck = require('ReactNativeVersionCheck'); + expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow(); + }); +} + +function _mockJsVersion(major = 0, minor = 0, patch = 0, prerelease = null) { + jest.doMock('ReactNativeVersion', () => ({ + version: {major, minor, patch, prerelease}, + })); +} + +function _mockNativeVersion( + major = 0, + minor = 0, + patch = 0, + prerelease = null, +) { + jest.doMock('NativeModules', () => ({ + PlatformConstants: { + reactNativeVersion: {major, minor, patch, prerelease}, + }, + })); +} diff --git a/Libraries/EventEmitter/NativeEventEmitter.js b/Libraries/EventEmitter/NativeEventEmitter.js index be0de96f794946..5c979600295b54 100644 --- a/Libraries/EventEmitter/NativeEventEmitter.js +++ b/Libraries/EventEmitter/NativeEventEmitter.js @@ -14,18 +14,24 @@ const EventEmitter = require('EventEmitter'); const Platform = require('Platform'); const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); + const invariant = require('fbjs/lib/invariant'); import type EmitterSubscription from 'EmitterSubscription'; +type NativeModule = { + +addListener: (eventType: string) => void, + +removeListeners: (count: number) => void, +}; + /** * Abstract base class for implementing event-emitting modules. This implements * a subset of the standard EventEmitter node module API. */ class NativeEventEmitter extends EventEmitter { - _nativeModule: Object; + _nativeModule: ?NativeModule; - constructor(nativeModule: Object) { + constructor(nativeModule: ?NativeModule) { super(RCTDeviceEventEmitter.sharedSubscriber); if (Platform.OS === 'ios') { invariant(nativeModule, 'Native module cannot be null.'); @@ -33,8 +39,12 @@ class NativeEventEmitter extends EventEmitter { } } - addListener(eventType: string, listener: Function, context: ?Object): EmitterSubscription { - if (Platform.OS === 'ios') { + addListener( + eventType: string, + listener: Function, + context: ?Object, + ): EmitterSubscription { + if (this._nativeModule != null) { this._nativeModule.addListener(eventType); } return super.addListener(eventType, listener, context); @@ -42,15 +52,15 @@ class NativeEventEmitter extends EventEmitter { removeAllListeners(eventType: string) { invariant(eventType, 'eventType argument is required.'); - if (Platform.OS === 'ios') { - const count = this.listeners(eventType).length; + const count = this.listeners(eventType).length; + if (this._nativeModule != null) { this._nativeModule.removeListeners(count); } super.removeAllListeners(eventType); } removeSubscription(subscription: EmitterSubscription) { - if (Platform.OS === 'ios') { + if (this._nativeModule != null) { this._nativeModule.removeListeners(1); } super.removeSubscription(subscription); diff --git a/Libraries/Image/AssetSourceResolver.js b/Libraries/Image/AssetSourceResolver.js index 53d80be0ce7a09..0371dbad642c8c 100644 --- a/Libraries/Image/AssetSourceResolver.js +++ b/Libraries/Image/AssetSourceResolver.js @@ -42,7 +42,7 @@ function getScaledAssetPath(asset): string { */ function getAssetPathInDrawableFolder(asset): string { var scale = AssetSourceResolver.pickScale(asset.scales, PixelRatio.get()); - var drawbleFolder = assetPathUtils.getAndroidDrawableFolderName(asset, scale); + var drawbleFolder = assetPathUtils.getAndroidResourceFolderName(asset, scale); var fileName = assetPathUtils.getAndroidResourceIdentifier(asset); return drawbleFolder + '/' + fileName + '.' + asset.type; } diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index b5308b4642763b..b953d085515074 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -55,7 +55,7 @@ function generateRequestId() { * /> * * * ); @@ -107,6 +107,7 @@ var Image = createReactClass({ uri: PropTypes.string, width: PropTypes.number, height: PropTypes.number, + headers: PropTypes.objectOf(PropTypes.string), })) ]), /** diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index 16588e63f00d82..8b733822260b37 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -53,7 +53,7 @@ const ImageViewManager = NativeModules.ImageViewManager; * /> * * * React * @@ -58,9 +58,6 @@ class ImageBackground extends React.Component<$FlowFixMeProps> { _viewRef: ?NativeMethodsMixinType = null; _captureRef = ref => { - /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This comment - * suppresses an error when upgrading Flow's support for React. To see the - * error delete this comment and run Flow. */ this._viewRef = ref; }; diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index 13d02b437c3cf2..7e57f481b2ecd9 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -66,7 +66,7 @@ - (void)setUp - (float)handlerPriority { - return 1; + return 2; } - (id)imageCache diff --git a/Libraries/Image/RCTImageStoreManager.m b/Libraries/Image/RCTImageStoreManager.m index 690258e6522cd2..d57971de6c8048 100644 --- a/Libraries/Image/RCTImageStoreManager.m +++ b/Libraries/Image/RCTImageStoreManager.m @@ -32,6 +32,11 @@ @implementation RCTImageStoreManager RCT_EXPORT_MODULE() +- (float)handlerPriority +{ + return 1; +} + - (void)removeImageForTag:(NSString *)imageTag withBlock:(void (^)())block { dispatch_async(_methodQueue, ^{ diff --git a/Libraries/Interaction/BridgeSpyStallHandler.js b/Libraries/Interaction/BridgeSpyStallHandler.js index 920dab5f9db316..78d61c0321baae 100644 --- a/Libraries/Interaction/BridgeSpyStallHandler.js +++ b/Libraries/Interaction/BridgeSpyStallHandler.js @@ -45,7 +45,7 @@ const BridgeSpyStallHandler = { } } return `${info.type === TO_JS ? 'N->JS' : 'JS->N'} : ` + - `${info.module ? (info.module + '.') : ''}${info.method}(${args})`; + `${info.module ? (info.module + '.') : ''}${info.method}(${JSON.stringify(args)})`; }), ); }, diff --git a/Libraries/Lists/SectionList.js b/Libraries/Lists/SectionList.js index 42fe99d6c506a5..775685131d6eaa 100644 --- a/Libraries/Lists/SectionList.js +++ b/Libraries/Lists/SectionList.js @@ -23,7 +23,7 @@ import type {Props as VirtualizedSectionListProps} from 'VirtualizedSectionList' type Item = any; -type SectionBase = { +export type SectionBase = { /** * The data for rendering items in this section. */ @@ -220,7 +220,7 @@ type DefaultProps = typeof defaultProps; * } * renderSectionHeader={({section}) =>
} - * sections={[ // homogenous rendering between sections + * sections={[ // homogeneous rendering between sections * {data: [...], title: ...}, * {data: [...], title: ...}, * {data: [...], title: ...}, diff --git a/Libraries/Lists/ViewabilityHelper.js b/Libraries/Lists/ViewabilityHelper.js index 3742e607d8e9b2..05f1a1bc79b435 100644 --- a/Libraries/Lists/ViewabilityHelper.js +++ b/Libraries/Lists/ViewabilityHelper.js @@ -223,6 +223,13 @@ class ViewabilityHelper { } } + /** + * clean-up cached _viewableIndices to evaluate changed items on next update + */ + resetViewableIndices() { + this._viewableIndices = []; + } + /** * Records that an interaction has happened even if there has been no scroll. */ diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index d327ec30d93393..ba60b610432f3d 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -375,18 +375,12 @@ class VirtualizedList extends React.PureComponent { */ getScrollResponder() { if (this._scrollRef && this._scrollRef.getScrollResponder) { - /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This - * comment suppresses an error when upgrading Flow's support for React. - * To see the error delete this comment and run Flow. */ return this._scrollRef.getScrollResponder(); } } getScrollableNode() { if (this._scrollRef && this._scrollRef.getScrollableNode) { - /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This - * comment suppresses an error when upgrading Flow's support for React. - * To see the error delete this comment and run Flow. */ return this._scrollRef.getScrollableNode(); } else { return ReactNative.findNodeHandle(this._scrollRef); @@ -395,9 +389,6 @@ class VirtualizedList extends React.PureComponent { setNativeProps(props: Object) { if (this._scrollRef) { - /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This - * comment suppresses an error when upgrading Flow's support for React. - * To see the error delete this comment and run Flow. */ this._scrollRef.setNativeProps(props); } } @@ -525,6 +516,12 @@ class VirtualizedList extends React.PureComponent { }); if (data !== this.props.data || extraData !== this.props.extraData) { this._hasDataChangedSinceEndReached = true; + + // clear the viewableIndices cache to also trigger + // the onViewableItemsChanged callback with the new data + this._viewabilityTuples.forEach(tuple => { + tuple.viewabilityHelper.resetViewableIndices(); + }); } } @@ -636,9 +633,6 @@ class VirtualizedList extends React.PureComponent { ); cells.push( - /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This - * comment suppresses an error when upgrading Flow's support for React. - * To see the error delete this comment and run Flow. */ { ); cells.push( - /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This - * comment suppresses an error when upgrading Flow's support for React. - * To see the error delete this comment and run Flow. */ { ); cells.push( - /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This - * comment suppresses an error when upgrading Flow's support for React. - * To see the error delete this comment and run Flow. */ { ); expect(component).toMatchSnapshot(); }); + + it('returns the viewableItems correctly in the onViewableItemsChanged callback after changing the data', () => { + const ITEM_HEIGHT = 800; + let data = [{key: 'i1'}, {key: 'i2'}, {key: 'i3'}]; + const nativeEvent = { + contentOffset: {y: 0, x: 0}, + layoutMeasurement: {width: 300, height: 600}, + contentSize: {width: 300, height: data.length * ITEM_HEIGHT}, + zoomScale: 1, + contentInset: {right: 0, top: 0, left: 0, bottom: 0}, + }; + const onViewableItemsChanged = jest.fn(); + const props = { + data, + renderItem: ({item}) => , + getItem: (data, index) => data[index], + getItemCount: data => data.length, + getItemLayout: (data, index) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index, + }), + onViewableItemsChanged, + }; + + const component = ReactTestRenderer.create(); + + const instance = component.getInstance(); + + instance._onScrollBeginDrag({nativeEvent}); + instance._onScroll({ + timeStamp: 1000, + nativeEvent, + }); + + expect(onViewableItemsChanged).toHaveBeenCalledTimes(1); + expect(onViewableItemsChanged).toHaveBeenLastCalledWith( + expect.objectContaining({ + viewableItems: [expect.objectContaining({isViewable: true, key: 'i1'})], + }), + ); + data = [{key: 'i4'}, ...data]; + component.update(); + + instance._onScroll({ + timeStamp: 2000, + nativeEvent: { + ...nativeEvent, + contentOffset: {y: 100, x: 0}, + }, + }); + + expect(onViewableItemsChanged).toHaveBeenCalledTimes(2); + expect(onViewableItemsChanged).toHaveBeenLastCalledWith( + expect.objectContaining({ + viewableItems: [expect.objectContaining({isViewable: true, key: 'i4'})], + }), + ); + }); }); diff --git a/Libraries/Network/NetInfo.js b/Libraries/Network/NetInfo.js index 864929e094649d..ae598179feab32 100644 --- a/Libraries/Network/NetInfo.js +++ b/Libraries/Network/NetInfo.js @@ -158,12 +158,12 @@ const _isConnectedSubscriptions = new Map(); * function handleFirstConnectivityChange(isConnected) { * console.log('Then, is ' + (isConnected ? 'online' : 'offline')); * NetInfo.isConnected.removeEventListener( - * 'change', + * 'connectionChange', * handleFirstConnectivityChange * ); * } * NetInfo.isConnected.addEventListener( - * 'change', + * 'connectionChange', * handleFirstConnectivityChange * ); * ``` diff --git a/Libraries/ReactNative/I18nManager.js b/Libraries/ReactNative/I18nManager.js index 00529a043e905b..e682e8a77be9cc 100644 --- a/Libraries/ReactNative/I18nManager.js +++ b/Libraries/ReactNative/I18nManager.js @@ -14,14 +14,18 @@ type I18nManagerStatus = { isRTL: boolean, + doesRTLFlipLeftAndRightStyles: boolean, allowRTL: (allowRTL: boolean) => {}, forceRTL: (forceRTL: boolean) => {}, + makeRTLFlipLeftAndRightStyles: (flipStyles: boolean) => {}, }; const I18nManager: I18nManagerStatus = require('NativeModules').I18nManager || { isRTL: false, + doesRTLFlipLeftAndRightStyles: true, allowRTL: () => {}, forceRTL: () => {}, + makeRTLFlipLeftAndRightStyles: () => {}, }; module.exports = I18nManager; diff --git a/Libraries/ReactNative/YellowBox.js b/Libraries/ReactNative/YellowBox.js index e36681458d716e..88e786ab0d7d5b 100644 --- a/Libraries/ReactNative/YellowBox.js +++ b/Libraries/ReactNative/YellowBox.js @@ -106,17 +106,22 @@ function sprintf(format, ...args) { return format.replace(/%s/g, match => args[index++]); } -function updateWarningMap(format, ...args): void { +function updateWarningMap(...args): void { if (console.disableYellowBox) { return; } - format = String(format); - const argCount = (format.match(/%s/g) || []).length; - const warning = [ - sprintf(format, ...args.slice(0, argCount)), - ...args.slice(argCount).map(stringifySafe), - ].join(' '); + let warning; + if (typeof args[0] === 'string') { + const [format, ...formatArgs] = args; + const argCount = (format.match(/%s/g) || []).length; + warning = [ + sprintf(format, ...formatArgs.slice(0, argCount).map(stringifySafe)), + ...formatArgs.slice(argCount).map(stringifySafe), + ].join(' '); + } else { + warning = args.map(stringifySafe).join(' '); + } if (warning.startsWith('(ADVICE)')) { return; diff --git a/Libraries/ReactNative/requireNativeComponent.js b/Libraries/ReactNative/requireNativeComponent.js index b04749ed53d343..7034d871ad9987 100644 --- a/Libraries/ReactNative/requireNativeComponent.js +++ b/Libraries/ReactNative/requireNativeComponent.js @@ -52,6 +52,61 @@ function requireNativeComponent( componentInterface?: ?ComponentInterface, extraConfig?: ?{nativeOnly?: Object}, ): React$ComponentType | string { + function attachBubblingEventTypes(viewConfig) { + if (UIManager.genericBubblingEventTypes) { + viewConfig.bubblingEventTypes = merge( + viewConfig.bubblingEventTypes, + UIManager.genericBubblingEventTypes, + ); + // As genericBubblingEventTypes do not change over time, and there's + // merge of all the events happening in Fiber, we need to pass + // genericBubblingEventTypes to Fiber only once. Therefore, we can delete + // it and forget about it. + delete UIManager.genericBubblingEventTypes; + } + } + + function attachDirectEventTypes(viewConfig) { + if (UIManager.genericDirectEventTypes) { + viewConfig.directEventTypes = merge( + viewConfig.directEventTypes, + UIManager.genericDirectEventTypes, + ); + // As genericDirectEventTypes do not change over time, and there's merge + // of all the events happening in Fiber, we need to pass genericDirectEventTypes + // to Fiber only once. Therefore, we can delete it and forget about it. + delete UIManager.genericDirectEventTypes; + } + } + + function merge(destination: ?Object, source: ?Object): ?Object { + if (!source) { + return destination; + } + if (!destination) { + return source; + } + + for (const key in source) { + if (!source.hasOwnProperty(key)) { + continue; + } + + var sourceValue = source[key]; + if (destination.hasOwnProperty(key)) { + const destinationValue = destination[key]; + if ( + typeof sourceValue === 'object' && + typeof destinationValue === 'object' + ) { + sourceValue = merge(destinationValue, sourceValue); + } + } + destination[key] = sourceValue; + } + return destination; + } + // Don't load the ViewConfig from UIManager until it's needed for rendering. // Lazy-loading this can help avoid Prepack deopts. function getViewConfig() { @@ -129,6 +184,9 @@ function requireNativeComponent( ); } + attachBubblingEventTypes(viewConfig); + attachDirectEventTypes(viewConfig); + // Register this view's event types with the ReactNative renderer. // This enables view managers to be initialized lazily, improving perf, // While also enabling 3rd party components to define custom event types. diff --git a/Libraries/Renderer/REVISION b/Libraries/Renderer/REVISION index 87eaec8c7d139f..1caaa5f390ba1b 100644 --- a/Libraries/Renderer/REVISION +++ b/Libraries/Renderer/REVISION @@ -1 +1 @@ -b5ac963fb791d1298e7f396236383bc955f916c1 \ No newline at end of file +589c0a25dfa18c2090549cc6f5b626d69ea53c2a \ No newline at end of file diff --git a/Libraries/Renderer/ReactNativeFiber-dev.js b/Libraries/Renderer/ReactNativeFiber-dev.js index 17218a4a866a3a..7de81ef95f3c93 100644 --- a/Libraries/Renderer/ReactNativeFiber-dev.js +++ b/Libraries/Renderer/ReactNativeFiber-dev.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @noflow * @providesModule ReactNativeFiber-dev @@ -14,7 +12,7 @@ __DEV__ && function() { var invariant = require("fbjs/lib/invariant"), require$$0 = require("fbjs/lib/warning"), ExceptionsManager = require("ExceptionsManager"), emptyObject = require("fbjs/lib/emptyObject"), react = require("react"), checkPropTypes = require("prop-types/checkPropTypes"), shallowEqual = require("fbjs/lib/shallowEqual"), deepDiffer = require("deepDiffer"), flattenStyle = require("flattenStyle"), TextInputState = require("TextInputState"), UIManager = require("UIManager"), deepFreezeAndThrowOnMutationInDev = require("deepFreezeAndThrowOnMutationInDev"); require("InitializeCore"); - var RCTEventEmitter = require("RCTEventEmitter"), emptyFunction = require("fbjs/lib/emptyFunction"), ExecutionEnvironment = require("fbjs/lib/ExecutionEnvironment"), performanceNow = require("fbjs/lib/performanceNow"), defaultShowDialog = function(capturedError) { + var RCTEventEmitter = require("RCTEventEmitter"), emptyFunction = require("fbjs/lib/emptyFunction"), defaultShowDialog = function(capturedError) { return !0; }, showDialog = defaultShowDialog; function logCapturedError(capturedError) { @@ -181,13 +179,9 @@ __DEV__ && function() { function restoreStateOfTarget(target) { var internalInstance = EventPluginUtils_1.getInstanceFromNode(target); if (internalInstance) { - if ("number" == typeof internalInstance.tag) { - invariant(fiberHostComponent && "function" == typeof fiberHostComponent.restoreControlledState, "Fiber needs to be injected to handle a fiber target for controlled " + "events. This error is likely caused by a bug in React. Please file an issue."); - var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(internalInstance.stateNode); - return void fiberHostComponent.restoreControlledState(internalInstance.stateNode, internalInstance.type, props); - } - invariant("function" == typeof internalInstance.restoreControlledState, "The internal instance must be a React host component. " + "This error is likely caused by a bug in React. Please file an issue."), - internalInstance.restoreControlledState(); + invariant(fiberHostComponent && "function" == typeof fiberHostComponent.restoreControlledState, "Fiber needs to be injected to handle a fiber target for controlled " + "events. This error is likely caused by a bug in React. Please file an issue."); + var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(internalInstance.stateNode); + fiberHostComponent.restoreControlledState(internalInstance.stateNode, internalInstance.type, props); } } var ReactControlledComponent = { @@ -201,16 +195,11 @@ __DEV__ && function() { if (restoreTarget = null, restoreQueue = null, restoreStateOfTarget(target), queuedTargets) for (var i = 0; i < queuedTargets.length; i++) restoreStateOfTarget(queuedTargets[i]); } } - }, ReactControlledComponent_1 = ReactControlledComponent, stackBatchedUpdates = function(fn, a, b, c, d, e) { - return fn(a, b, c, d, e); - }, fiberBatchedUpdates = function(fn, bookkeeping) { + }, ReactControlledComponent_1 = ReactControlledComponent, fiberBatchedUpdates = function(fn, bookkeeping) { return fn(bookkeeping); }; - function performFiberBatchedUpdates(fn, bookkeeping) { - return fiberBatchedUpdates(fn, bookkeeping); - } function batchedUpdates(fn, bookkeeping) { - return stackBatchedUpdates(performFiberBatchedUpdates, fn, bookkeeping); + return fiberBatchedUpdates(fn, bookkeeping); } var isNestingBatched = !1; function batchedUpdatesWithControlledComponents(fn, bookkeeping) { @@ -223,9 +212,6 @@ __DEV__ && function() { } } var ReactGenericBatchingInjection = { - injectStackBatchedUpdates: function(_batchedUpdates) { - stackBatchedUpdates = _batchedUpdates; - }, injectFiberBatchedUpdates: function(_batchedUpdates) { fiberBatchedUpdates = _batchedUpdates; } @@ -262,21 +248,9 @@ __DEV__ && function() { isPortal: isPortal, REACT_PORTAL_TYPE: REACT_PORTAL_TYPE_1 }, instanceCache = {}, instanceProps = {}; - function getRenderedHostOrTextFromComponent(component) { - for (var rendered; rendered = component._renderedComponent; ) component = rendered; - return component; - } - function precacheNode(inst, tag) { - var nativeInst = getRenderedHostOrTextFromComponent(inst); - instanceCache[tag] = nativeInst; - } function precacheFiberNode(hostInst, tag) { instanceCache[tag] = hostInst; } - function uncacheNode(inst) { - var tag = inst._rootNodeID; - tag && delete instanceCache[tag]; - } function uncacheFiberNode(tag) { delete instanceCache[tag], delete instanceProps[tag]; } @@ -298,9 +272,7 @@ __DEV__ && function() { getInstanceFromNode: getInstanceFromTag, getNodeFromInstance: getTagFromInstance, precacheFiberNode: precacheFiberNode, - precacheNode: precacheNode, uncacheFiberNode: uncacheFiberNode, - uncacheNode: uncacheNode, getFiberCurrentPropsFromNode: getFiberCurrentPropsFromNode, updateFiberProps: updateFiberProps }, ReactNativeComponentTree_1 = ReactNativeComponentTree, commonjsGlobal = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {}, ReactFeatureFlags = { @@ -500,16 +472,9 @@ __DEV__ && function() { beginUpdateQueue: beginUpdateQueue_1, commitCallbacks: commitCallbacks_1 }; - function getComponentName$1(instanceOrFiber) { - if ("function" == typeof instanceOrFiber.getName) { - return instanceOrFiber.getName(); - } - if ("number" == typeof instanceOrFiber.tag) { - var fiber = instanceOrFiber, type = fiber.type; - if ("string" == typeof type) return type; - if ("function" == typeof type) return type.displayName || type.name; - } - return null; + function getComponentName$1(fiber) { + var type = fiber.type; + return "string" == typeof type ? type : "function" == typeof type ? type.displayName || type.name : null; } var getComponentName_1 = getComponentName$1, ReactInstanceMap = { remove: function(key) { @@ -528,7 +493,6 @@ __DEV__ && function() { ReactCurrentOwner: ReactInternals.ReactCurrentOwner }; Object.assign(ReactGlobalSharedState, { - ReactComponentTreeHook: ReactInternals.ReactComponentTreeHook, ReactDebugCurrentFrame: ReactInternals.ReactDebugCurrentFrame }); var ReactGlobalSharedState_1 = ReactGlobalSharedState, ReactCurrentOwner = ReactGlobalSharedState_1.ReactCurrentOwner, warning$4 = require$$0, ClassComponent$2 = ReactTypeOfWork.ClassComponent, HostComponent$1 = ReactTypeOfWork.HostComponent, HostRoot$2 = ReactTypeOfWork.HostRoot, HostPortal = ReactTypeOfWork.HostPortal, HostText = ReactTypeOfWork.HostText, NoEffect = ReactTypeOfSideEffect.NoEffect, Placement = ReactTypeOfSideEffect.Placement, MOUNTING = 1, MOUNTED = 2, UNMOUNTED = 3; @@ -689,7 +653,9 @@ __DEV__ && function() { }, ReactDebugCurrentFrame = ReactGlobalSharedState_1.ReactDebugCurrentFrame, getComponentName$3 = getComponentName_1, _require2$1 = ReactFiberComponentTreeHook, getStackAddendumByWorkInProgressFiber = _require2$1.getStackAddendumByWorkInProgressFiber; function getCurrentFiberOwnerName() { var fiber = ReactDebugCurrentFiber$2.current; - return null === fiber ? null : null != fiber._debugOwner ? getComponentName$3(fiber._debugOwner) : null; + if (null === fiber) return null; + var owner = fiber._debugOwner; + return null !== owner && void 0 !== owner ? getComponentName$3(owner) : null; } function getCurrentFiberStackAddendum() { var fiber = ReactDebugCurrentFiber$2.current; @@ -699,8 +665,11 @@ __DEV__ && function() { ReactDebugCurrentFrame.getCurrentStack = null, ReactDebugCurrentFiber$2.current = null, ReactDebugCurrentFiber$2.phase = null; } - function setCurrentFiber(fiber, phase) { + function setCurrentFiber(fiber) { ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackAddendum, ReactDebugCurrentFiber$2.current = fiber, + ReactDebugCurrentFiber$2.phase = null; + } + function setCurrentPhase(phase) { ReactDebugCurrentFiber$2.phase = phase; } var ReactDebugCurrentFiber$2 = { @@ -708,6 +677,7 @@ __DEV__ && function() { phase: null, resetCurrentFiber: resetCurrentFiber, setCurrentFiber: setCurrentFiber, + setCurrentPhase: setCurrentPhase, getCurrentFiberOwnerName: getCurrentFiberOwnerName, getCurrentFiberStackAddendum: getCurrentFiberStackAddendum }, ReactDebugCurrentFiber_1 = ReactDebugCurrentFiber$2, ReactDebugFiberPerf = null, _require$2 = ReactTypeOfWork, HostRoot$3 = _require$2.HostRoot, HostComponent$3 = _require$2.HostComponent, HostText$1 = _require$2.HostText, HostPortal$1 = _require$2.HostPortal, YieldComponent = _require$2.YieldComponent, Fragment = _require$2.Fragment, getComponentName$4 = getComponentName_1, reactEmoji = "⚛", warningEmoji = "⛔", supportsUserTiming = "undefined" != typeof performance && "function" == typeof performance.mark && "function" == typeof performance.clearMarks && "function" == typeof performance.measure && "function" == typeof performance.clearMeasures, currentFiber = null, currentPhase = null, currentPhaseFiber = null, isCommitting = !1, hasScheduledUpdateInCurrentCommit = !1, hasScheduledUpdateInCurrentPhase = !1, commitCountInCurrentWorkLoop = 0, effectCountInCurrentCommit = 0, labelsInCurrentCommit = new Set(), formatMarkName = function(markName) { @@ -859,9 +829,8 @@ __DEV__ && function() { var context = {}; for (var key in contextTypes) context[key] = unmaskedContext[key]; var name = getComponentName_1(workInProgress) || "Unknown"; - return ReactDebugCurrentFiber$1.setCurrentFiber(workInProgress, null), checkPropTypes$1(contextTypes, context, "context", name, ReactDebugCurrentFiber$1.getCurrentFiberStackAddendum), - ReactDebugCurrentFiber$1.resetCurrentFiber(), instance && cacheContext(workInProgress, unmaskedContext, context), - context; + return checkPropTypes$1(contextTypes, context, "context", name, ReactDebugCurrentFiber$1.getCurrentFiberStackAddendum), + instance && cacheContext(workInProgress, unmaskedContext, context), context; }, hasContextChanged = function() { return didPerformWorkStackCursor.current; }; @@ -882,7 +851,7 @@ __DEV__ && function() { invariant(null == contextStackCursor.cursor, "Unexpected context found on stack. " + "This error is likely caused by a bug in React. Please file an issue."), push(contextStackCursor, context, fiber), push(didPerformWorkStackCursor, didChange, fiber); }; - function processChildContext$1(fiber, parentContext, isReconciling) { + function processChildContext$1(fiber, parentContext) { var instance = fiber.stateNode, childContextTypes = fiber.type.childContextTypes; if ("function" != typeof instance.getChildContext) { var componentName = getComponentName_1(fiber) || "Unknown"; @@ -891,12 +860,12 @@ __DEV__ && function() { parentContext; } var childContext = void 0; - ReactDebugCurrentFiber$1.setCurrentFiber(fiber, "getChildContext"), startPhaseTimer(fiber, "getChildContext"), - childContext = instance.getChildContext(), stopPhaseTimer(), ReactDebugCurrentFiber$1.resetCurrentFiber(); + ReactDebugCurrentFiber$1.setCurrentPhase("getChildContext"), startPhaseTimer(fiber, "getChildContext"), + childContext = instance.getChildContext(), stopPhaseTimer(), ReactDebugCurrentFiber$1.setCurrentPhase(null); for (var contextKey in childContext) invariant(contextKey in childContextTypes, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', getComponentName_1(fiber) || "Unknown", contextKey); - var name = getComponentName_1(fiber) || "Unknown", workInProgress = isReconciling ? fiber : null; - return ReactDebugCurrentFiber$1.setCurrentFiber(workInProgress, null), checkPropTypes$1(childContextTypes, childContext, "child context", name, ReactDebugCurrentFiber$1.getCurrentFiberStackAddendum), - ReactDebugCurrentFiber$1.resetCurrentFiber(), Object.assign({}, parentContext, childContext); + var name = getComponentName_1(fiber) || "Unknown"; + return checkPropTypes$1(childContextTypes, childContext, "child context", name, ReactDebugCurrentFiber$1.getCurrentFiberStackAddendum), + Object.assign({}, parentContext, childContext); } var processChildContext_1 = processChildContext$1, pushContextProvider = function(workInProgress) { if (!isContextProvider$1(workInProgress)) return !1; @@ -908,7 +877,7 @@ __DEV__ && function() { var instance = workInProgress.stateNode; if (invariant(instance, "Expected to have an instance by this point. " + "This error is likely caused by a bug in React. Please file an issue."), didChange) { - var mergedContext = processChildContext$1(workInProgress, previousContext, !0); + var mergedContext = processChildContext$1(workInProgress, previousContext); instance.__reactInternalMemoizedMergedChildContext = mergedContext, pop(didPerformWorkStackCursor, workInProgress), pop(contextStackCursor, workInProgress), push(contextStackCursor, mergedContext, workInProgress), push(didPerformWorkStackCursor, didChange, workInProgress); @@ -1098,11 +1067,11 @@ __DEV__ && function() { if (null !== mixedRef && "function" != typeof mixedRef) { if (element._owner) { var owner = element._owner, inst = void 0; - if (owner) if ("number" == typeof owner.tag) { + if (owner) { var ownerFiber = owner; invariant(ownerFiber.tag === ClassComponent$7, "Stateless function components cannot have refs."), inst = ownerFiber.stateNode; - } else inst = owner.getPublicInstance(); + } invariant(inst, "Missing owner for string ref %s. This error is likely caused by a " + "bug in React. Please file an issue.", mixedRef); var stringRef = "" + mixedRef; if (null !== current && null !== current.ref && current.ref._stringRef === stringRef) return current.ref; @@ -1678,8 +1647,8 @@ __DEV__ && function() { var fn = workInProgress.type, nextProps = workInProgress.pendingProps, memoizedProps = workInProgress.memoizedProps; if (hasContextChanged$1()) null === nextProps && (nextProps = memoizedProps); else if (null === nextProps || memoizedProps === nextProps) return bailoutOnAlreadyFinishedWork(current, workInProgress); var nextChildren, unmaskedContext = getUnmaskedContext$1(workInProgress), context = getMaskedContext$1(workInProgress, unmaskedContext); - return ReactCurrentOwner$2.current = workInProgress, ReactDebugCurrentFiber$4.setCurrentFiber(workInProgress, "render"), - nextChildren = fn(nextProps, context), ReactDebugCurrentFiber$4.setCurrentFiber(workInProgress, null), + return ReactCurrentOwner$2.current = workInProgress, ReactDebugCurrentFiber$4.setCurrentPhase("render"), + nextChildren = fn(nextProps, context), ReactDebugCurrentFiber$4.setCurrentPhase(null), workInProgress.effectTag |= PerformedWork$1, reconcileChildren(current, workInProgress, nextChildren), memoizeProps(workInProgress, nextProps), workInProgress.child; } @@ -1695,8 +1664,8 @@ __DEV__ && function() { var instance = workInProgress.stateNode; ReactCurrentOwner$2.current = workInProgress; var nextChildren = void 0; - return ReactDebugCurrentFiber$4.setCurrentFiber(workInProgress, "render"), nextChildren = instance.render(), - ReactDebugCurrentFiber$4.setCurrentFiber(workInProgress, null), workInProgress.effectTag |= PerformedWork$1, + return ReactDebugCurrentFiber$4.setCurrentPhase("render"), nextChildren = instance.render(), + ReactDebugCurrentFiber$4.setCurrentPhase(null), workInProgress.effectTag |= PerformedWork$1, reconcileChildren(current, workInProgress, nextChildren), memoizeState(workInProgress, instance.state), memoizeProps(workInProgress, instance.props), hasContext && invalidateContextProvider$1(workInProgress, !0), workInProgress.child; @@ -1806,7 +1775,7 @@ __DEV__ && function() { } function beginWork(current, workInProgress, priorityLevel) { if (workInProgress.pendingWorkPriority === NoWork$3 || workInProgress.pendingWorkPriority > priorityLevel) return bailoutOnLowPriority(current, workInProgress); - switch (ReactDebugCurrentFiber$4.setCurrentFiber(workInProgress, null), workInProgress.tag) { + switch (workInProgress.tag) { case IndeterminateComponent$2: return mountIndeterminateComponent(current, workInProgress, priorityLevel); @@ -1870,7 +1839,7 @@ __DEV__ && function() { beginWork: beginWork, beginFailedWork: beginFailedWork }; - }, reconcileChildFibers$2 = ReactChildFiber.reconcileChildFibers, popContextProvider$2 = ReactFiberContext.popContextProvider, popTopLevelContextObject$1 = ReactFiberContext.popTopLevelContextObject, IndeterminateComponent$3 = ReactTypeOfWork.IndeterminateComponent, FunctionalComponent$3 = ReactTypeOfWork.FunctionalComponent, ClassComponent$8 = ReactTypeOfWork.ClassComponent, HostRoot$7 = ReactTypeOfWork.HostRoot, HostComponent$7 = ReactTypeOfWork.HostComponent, HostText$5 = ReactTypeOfWork.HostText, HostPortal$6 = ReactTypeOfWork.HostPortal, CoroutineComponent$3 = ReactTypeOfWork.CoroutineComponent, CoroutineHandlerPhase$1 = ReactTypeOfWork.CoroutineHandlerPhase, YieldComponent$4 = ReactTypeOfWork.YieldComponent, Fragment$4 = ReactTypeOfWork.Fragment, Placement$4 = ReactTypeOfSideEffect.Placement, Ref$2 = ReactTypeOfSideEffect.Ref, Update$2 = ReactTypeOfSideEffect.Update, OffscreenPriority$2 = ReactPriorityLevel.OffscreenPriority, ReactDebugCurrentFiber$5 = ReactDebugCurrentFiber_1, ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { + }, reconcileChildFibers$2 = ReactChildFiber.reconcileChildFibers, popContextProvider$2 = ReactFiberContext.popContextProvider, popTopLevelContextObject$1 = ReactFiberContext.popTopLevelContextObject, IndeterminateComponent$3 = ReactTypeOfWork.IndeterminateComponent, FunctionalComponent$3 = ReactTypeOfWork.FunctionalComponent, ClassComponent$8 = ReactTypeOfWork.ClassComponent, HostRoot$7 = ReactTypeOfWork.HostRoot, HostComponent$7 = ReactTypeOfWork.HostComponent, HostText$5 = ReactTypeOfWork.HostText, HostPortal$6 = ReactTypeOfWork.HostPortal, CoroutineComponent$3 = ReactTypeOfWork.CoroutineComponent, CoroutineHandlerPhase$1 = ReactTypeOfWork.CoroutineHandlerPhase, YieldComponent$4 = ReactTypeOfWork.YieldComponent, Fragment$4 = ReactTypeOfWork.Fragment, Placement$4 = ReactTypeOfSideEffect.Placement, Ref$2 = ReactTypeOfSideEffect.Ref, Update$2 = ReactTypeOfSideEffect.Update, OffscreenPriority$2 = ReactPriorityLevel.OffscreenPriority, ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { var createInstance = config.createInstance, createTextInstance = config.createTextInstance, appendInitialChild = config.appendInitialChild, finalizeInitialChildren = config.finalizeInitialChildren, prepareUpdate = config.prepareUpdate, getRootHostContainer = hostContext.getRootHostContainer, popHostContext = hostContext.popHostContext, getHostContext = hostContext.getHostContext, popHostContainer = hostContext.popHostContainer, prepareToHydrateHostInstance = hydrationContext.prepareToHydrateHostInstance, prepareToHydrateHostTextInstance = hydrationContext.prepareToHydrateHostTextInstance, popHydrationState = hydrationContext.popHydrationState; function markUpdate(workInProgress) { workInProgress.effectTag |= Update$2; @@ -1917,7 +1886,6 @@ __DEV__ && function() { } } function completeWork(current, workInProgress, renderPriority) { - ReactDebugCurrentFiber$5.setCurrentFiber(workInProgress, null); var newProps = workInProgress.pendingProps; switch (null === newProps ? newProps = workInProgress.memoizedProps : workInProgress.pendingWorkPriority === OffscreenPriority$2 && renderPriority !== OffscreenPriority$2 || (workInProgress.pendingProps = null), workInProgress.tag) { @@ -2453,7 +2421,7 @@ __DEV__ && function() { } function commitAllHostEffects() { for (;null !== nextEffect; ) { - ReactDebugCurrentFiber$3.setCurrentFiber(nextEffect, null), recordEffect(); + ReactDebugCurrentFiber$3.setCurrentFiber(nextEffect), recordEffect(); var effectTag = nextEffect.effectTag; if (effectTag & ContentReset && config.resetTextContent(nextEffect.stateNode), effectTag & Ref) { var current = nextEffect.alternate; @@ -2532,7 +2500,11 @@ __DEV__ && function() { } function completeUnitOfWork(workInProgress) { for (;!0; ) { - var current = workInProgress.alternate, next = completeWork(current, workInProgress, nextPriorityLevel), returnFiber = workInProgress.return, siblingFiber = workInProgress.sibling; + var current = workInProgress.alternate; + ReactDebugCurrentFiber$3.setCurrentFiber(workInProgress); + var next = completeWork(current, workInProgress, nextPriorityLevel); + ReactDebugCurrentFiber$3.resetCurrentFiber(); + var returnFiber = workInProgress.return, siblingFiber = workInProgress.sibling; if (resetWorkPriority(workInProgress, nextPriorityLevel), null !== next) return stopWorkTimer(workInProgress), !0 && ReactFiberInstrumentation$1.debugTool && ReactFiberInstrumentation$1.debugTool.onCompleteWork(workInProgress), next; @@ -2552,19 +2524,19 @@ __DEV__ && function() { } function performUnitOfWork(workInProgress) { var current = workInProgress.alternate; - startWorkTimer(workInProgress); + startWorkTimer(workInProgress), ReactDebugCurrentFiber$3.setCurrentFiber(workInProgress); var next = beginWork(current, workInProgress, nextPriorityLevel); - return !0 && ReactFiberInstrumentation$1.debugTool && ReactFiberInstrumentation$1.debugTool.onBeginWork(workInProgress), + return ReactDebugCurrentFiber$3.resetCurrentFiber(), !0 && ReactFiberInstrumentation$1.debugTool && ReactFiberInstrumentation$1.debugTool.onBeginWork(workInProgress), null === next && (next = completeUnitOfWork(workInProgress)), ReactCurrentOwner$1.current = null, - ReactDebugCurrentFiber$3.resetCurrentFiber(), next; + next; } function performFailedUnitOfWork(workInProgress) { var current = workInProgress.alternate; - startWorkTimer(workInProgress); + startWorkTimer(workInProgress), ReactDebugCurrentFiber$3.setCurrentFiber(workInProgress); var next = beginFailedWork(current, workInProgress, nextPriorityLevel); - return !0 && ReactFiberInstrumentation$1.debugTool && ReactFiberInstrumentation$1.debugTool.onBeginWork(workInProgress), + return ReactDebugCurrentFiber$3.resetCurrentFiber(), !0 && ReactFiberInstrumentation$1.debugTool && ReactFiberInstrumentation$1.debugTool.onBeginWork(workInProgress), null === next && (next = completeUnitOfWork(workInProgress)), ReactCurrentOwner$1.current = null, - ReactDebugCurrentFiber$3.resetCurrentFiber(), next; + next; } function performDeferredWork(deadline) { performWork(OffscreenPriority, deadline); @@ -2821,22 +2793,12 @@ __DEV__ && function() { flushSync: flushSync, deferredUpdates: deferredUpdates }; - }, getContextFiber = function(arg) { - invariant(!1, "Missing injection for fiber getContextForSubtree"); - }; + }, addTopLevelUpdate = ReactFiberUpdateQueue.addTopLevelUpdate, findCurrentUnmaskedContext = ReactFiberContext.findCurrentUnmaskedContext, isContextProvider = ReactFiberContext.isContextProvider, processChildContext = ReactFiberContext.processChildContext, createFiberRoot = ReactFiberRoot.createFiberRoot, HostComponent = ReactTypeOfWork.HostComponent, warning$1 = require$$0, ReactFiberInstrumentation = ReactFiberInstrumentation_1, ReactDebugCurrentFiber = ReactDebugCurrentFiber_1, getComponentName = getComponentName_1, findCurrentHostFiber = ReactFiberTreeReflection.findCurrentHostFiber, findCurrentHostFiberWithNoPortals = ReactFiberTreeReflection.findCurrentHostFiberWithNoPortals; function getContextForSubtree(parentComponent) { if (!parentComponent) return emptyObject; - var instance = ReactInstanceMap_1.get(parentComponent); - return "number" == typeof instance.tag ? getContextFiber(instance) : instance._processChildContext(instance._context); + var fiber = ReactInstanceMap_1.get(parentComponent), parentContext = findCurrentUnmaskedContext(fiber); + return isContextProvider(fiber) ? processChildContext(fiber, parentContext) : parentContext; } - getContextForSubtree._injectFiber = function(fn) { - getContextFiber = fn; - }; - var getContextForSubtree_1 = getContextForSubtree, addTopLevelUpdate = ReactFiberUpdateQueue.addTopLevelUpdate, findCurrentUnmaskedContext = ReactFiberContext.findCurrentUnmaskedContext, isContextProvider = ReactFiberContext.isContextProvider, processChildContext = ReactFiberContext.processChildContext, createFiberRoot = ReactFiberRoot.createFiberRoot, HostComponent = ReactTypeOfWork.HostComponent, warning$1 = require$$0, ReactFiberInstrumentation = ReactFiberInstrumentation_1, ReactDebugCurrentFiber = ReactDebugCurrentFiber_1, getComponentName = getComponentName_1, findCurrentHostFiber = ReactFiberTreeReflection.findCurrentHostFiber, findCurrentHostFiberWithNoPortals = ReactFiberTreeReflection.findCurrentHostFiberWithNoPortals; - getContextForSubtree_1._injectFiber(function(fiber) { - var parentContext = findCurrentUnmaskedContext(fiber); - return isContextProvider(fiber) ? processChildContext(fiber, parentContext, !1) : parentContext; - }); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function"); } @@ -3036,7 +2998,7 @@ __DEV__ && function() { updateContainer: function(element, container, parentComponent, callback) { var current = container.current; ReactFiberInstrumentation.debugTool && (null === current.alternate ? ReactFiberInstrumentation.debugTool.onMountContainer(container) : null === element ? ReactFiberInstrumentation.debugTool.onUnmountContainer(container) : ReactFiberInstrumentation.debugTool.onUpdateContainer(container)); - var context = getContextForSubtree_1(parentComponent); + var context = getContextForSubtree(parentComponent); null === container.context ? container.context = context : container.pendingContext = context, scheduleTopLevelUpdate(current, element, callback); }, @@ -3213,7 +3175,7 @@ __DEV__ && function() { }; var ReactNativeFiberInspector = { getInspectorDataForViewTag: getInspectorDataForViewTag - }, ReactVersion = "16.0.0-rc.3", ReactCurrentOwner$3 = ReactGlobalSharedState_1.ReactCurrentOwner, warning$11 = require$$0; + }, ReactVersion = "16.0.0", ReactCurrentOwner$3 = ReactGlobalSharedState_1.ReactCurrentOwner, warning$11 = require$$0; function findNodeHandle(componentOrHandle) { var owner = ReactCurrentOwner$3.current; if (null !== owner && null !== owner.stateNode && (warning$11(owner.stateNode._warnedAboutRefsInRender, "%s is accessing findNodeHandle inside its render(). " + "render() should be a pure function of props and state. It should " + "never access something that requires stale data from the previous " + "render, such as refs. Move this logic to componentDidMount and " + "componentDidUpdate instead.", getComponentName_1(owner) || "A component"), @@ -3323,22 +3285,11 @@ __DEV__ && function() { injectEventPluginsByName: EventPluginRegistry_1.injectEventPluginsByName }, getListener: function(inst, registrationName) { - var listener; - if ("number" == typeof inst.tag) { - var stateNode = inst.stateNode; - if (!stateNode) return null; - var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(stateNode); - if (!props) return null; - if (listener = props[registrationName], shouldPreventMouseEvent(registrationName, inst.type, props)) return null; - } else { - var currentElement = inst._currentElement; - if ("string" == typeof currentElement || "number" == typeof currentElement) return null; - if (!inst._rootNodeID) return null; - var _props = currentElement.props; - if (listener = _props[registrationName], shouldPreventMouseEvent(registrationName, currentElement.type, _props)) return null; - } - return invariant(!listener || "function" == typeof listener, "Expected `%s` listener to be a function, instead got a value of `%s` type.", registrationName, typeof listener), - listener; + var listener, stateNode = inst.stateNode; + if (!stateNode) return null; + var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(stateNode); + return props ? (listener = props[registrationName], shouldPreventMouseEvent(registrationName, inst.type, props) ? null : (invariant(!listener || "function" == typeof listener, "Expected `%s` listener to be a function, instead got a value of `%s` type.", registrationName, typeof listener), + listener)) : null; }, extractEvents: function(topLevelType, targetInst, nativeEvent, nativeEventTarget) { for (var events, plugins = EventPluginRegistry_1.plugins, i = 0; i < plugins.length; i++) { @@ -3361,14 +3312,10 @@ __DEV__ && function() { } }, EventPluginHub_1 = EventPluginHub, HostComponent$11 = ReactTypeOfWork.HostComponent; function getParent(inst) { - if (void 0 !== inst._hostParent) return inst._hostParent; - if ("number" == typeof inst.tag) { - do { - inst = inst.return; - } while (inst && inst.tag !== HostComponent$11); - if (inst) return inst; - } - return null; + do { + inst = inst.return; + } while (inst && inst.tag !== HostComponent$11); + return inst || null; } function getLowestCommonAncestor(instA, instB) { for (var depthA = 0, tempA = instA; tempA; tempA = getParent(tempA)) depthA++; @@ -3988,430 +3935,7 @@ __DEV__ && function() { return "number" != typeof view && "window" !== view && (view = findNumericNodeHandle(view) || "window"), UIManager.__takeSnapshot(view, options); } - var takeSnapshot_1 = takeSnapshot, ReactInvalidSetStateWarningHook = {}, warning$17 = require$$0, processingChildContext = !1, warnInvalidSetState = function() { - warning$17(!processingChildContext, "setState(...): Cannot call setState() inside getChildContext()"); - }; - ReactInvalidSetStateWarningHook = { - onBeginProcessingChildContext: function() { - processingChildContext = !0; - }, - onEndProcessingChildContext: function() { - processingChildContext = !1; - }, - onSetState: function() { - warnInvalidSetState(); - } - }; - var ReactInvalidSetStateWarningHook_1 = ReactInvalidSetStateWarningHook, ReactHostOperationHistoryHook = null, history = []; - ReactHostOperationHistoryHook = { - onHostOperation: function(operation) { - history.push(operation); - }, - clearHistory: function() { - ReactHostOperationHistoryHook._preventClearing || (history = []); - }, - getHistory: function() { - return history; - } - }; - var ReactHostOperationHistoryHook_1 = ReactHostOperationHistoryHook, ReactComponentTreeHook = ReactGlobalSharedState_1.ReactComponentTreeHook, warning$16 = require$$0, ReactDebugTool = null, hooks = [], didHookThrowForEvent = {}, callHook = function(event, fn, context, arg1, arg2, arg3, arg4, arg5) { - try { - fn.call(context, arg1, arg2, arg3, arg4, arg5); - } catch (e) { - warning$16(didHookThrowForEvent[event], "Exception thrown by hook while handling %s: %s", event, e + "\n" + e.stack), - didHookThrowForEvent[event] = !0; - } - }, emitEvent = function(event, arg1, arg2, arg3, arg4, arg5) { - for (var i = 0; i < hooks.length; i++) { - var hook = hooks[i], fn = hook[event]; - fn && callHook(event, fn, hook, arg1, arg2, arg3, arg4, arg5); - } - }, isProfiling = !1, flushHistory = [], lifeCycleTimerStack = [], currentFlushNesting = 0, currentFlushMeasurements = [], currentFlushStartTime = 0, currentTimerDebugID = null, currentTimerStartTime = 0, currentTimerNestedFlushDuration = 0, currentTimerType = null, lifeCycleTimerHasWarned = !1, clearHistory = function() { - ReactComponentTreeHook.purgeUnmountedComponents(), ReactHostOperationHistoryHook_1.clearHistory(); - }, getTreeSnapshot = function(registeredIDs) { - return registeredIDs.reduce(function(tree, id) { - var ownerID = ReactComponentTreeHook.getOwnerID(id), parentID = ReactComponentTreeHook.getParentID(id); - return tree[id] = { - displayName: ReactComponentTreeHook.getDisplayName(id), - text: ReactComponentTreeHook.getText(id), - updateCount: ReactComponentTreeHook.getUpdateCount(id), - childIDs: ReactComponentTreeHook.getChildIDs(id), - ownerID: ownerID || parentID && ReactComponentTreeHook.getOwnerID(parentID) || 0, - parentID: parentID - }, tree; - }, {}); - }, resetMeasurements = function() { - var previousStartTime = currentFlushStartTime, previousMeasurements = currentFlushMeasurements, previousOperations = ReactHostOperationHistoryHook_1.getHistory(); - if (0 === currentFlushNesting) return currentFlushStartTime = 0, currentFlushMeasurements = [], - void clearHistory(); - if (previousMeasurements.length || previousOperations.length) { - var registeredIDs = ReactComponentTreeHook.getRegisteredIDs(); - flushHistory.push({ - duration: performanceNow() - previousStartTime, - measurements: previousMeasurements || [], - operations: previousOperations || [], - treeSnapshot: getTreeSnapshot(registeredIDs) - }); - } - clearHistory(), currentFlushStartTime = performanceNow(), currentFlushMeasurements = []; - }, checkDebugID = function(debugID) { - arguments.length > 1 && void 0 !== arguments[1] && arguments[1] && 0 === debugID || debugID || warning$16(!1, "ReactDebugTool: debugID may not be empty."); - }, beginLifeCycleTimer = function(debugID, timerType) { - 0 !== currentFlushNesting && (currentTimerType && !lifeCycleTimerHasWarned && (warning$16(!1, "There is an internal error in the React performance measurement code." + "\n\nDid not expect %s timer to start while %s timer is still in " + "progress for %s instance.", timerType, currentTimerType || "no", debugID === currentTimerDebugID ? "the same" : "another"), - lifeCycleTimerHasWarned = !0), currentTimerStartTime = performanceNow(), currentTimerNestedFlushDuration = 0, - currentTimerDebugID = debugID, currentTimerType = timerType); - }, endLifeCycleTimer = function(debugID, timerType) { - 0 !== currentFlushNesting && (currentTimerType === timerType || lifeCycleTimerHasWarned || (warning$16(!1, "There is an internal error in the React performance measurement code. " + "We did not expect %s timer to stop while %s timer is still in " + "progress for %s instance. Please report this as a bug in React.", timerType, currentTimerType || "no", debugID === currentTimerDebugID ? "the same" : "another"), - lifeCycleTimerHasWarned = !0), isProfiling && currentFlushMeasurements.push({ - timerType: timerType, - instanceID: debugID, - duration: performanceNow() - currentTimerStartTime - currentTimerNestedFlushDuration - }), currentTimerStartTime = 0, currentTimerNestedFlushDuration = 0, currentTimerDebugID = null, - currentTimerType = null); - }, pauseCurrentLifeCycleTimer = function() { - var currentTimer = { - startTime: currentTimerStartTime, - nestedFlushStartTime: performanceNow(), - debugID: currentTimerDebugID, - timerType: currentTimerType - }; - lifeCycleTimerStack.push(currentTimer), currentTimerStartTime = 0, currentTimerNestedFlushDuration = 0, - currentTimerDebugID = null, currentTimerType = null; - }, resumeCurrentLifeCycleTimer = function() { - var _lifeCycleTimerStack$ = lifeCycleTimerStack.pop(), startTime = _lifeCycleTimerStack$.startTime, nestedFlushStartTime = _lifeCycleTimerStack$.nestedFlushStartTime, debugID = _lifeCycleTimerStack$.debugID, timerType = _lifeCycleTimerStack$.timerType, nestedFlushDuration = performanceNow() - nestedFlushStartTime; - currentTimerStartTime = startTime, currentTimerNestedFlushDuration += nestedFlushDuration, - currentTimerDebugID = debugID, currentTimerType = timerType; - }, lastMarkTimeStamp = 0, canUsePerformanceMeasure = "undefined" != typeof performance && "function" == typeof performance.mark && "function" == typeof performance.clearMarks && "function" == typeof performance.measure && "function" == typeof performance.clearMeasures, shouldMark = function(debugID) { - if (!isProfiling || !canUsePerformanceMeasure) return !1; - var element = ReactComponentTreeHook.getElement(debugID); - return null != element && "object" == typeof element && !("string" == typeof element.type); - }, markBegin = function(debugID, markType) { - if (shouldMark(debugID)) { - var markName = debugID + "::" + markType; - lastMarkTimeStamp = performanceNow(), performance.mark(markName); - } - }, markEnd = function(debugID, markType) { - if (shouldMark(debugID)) { - var markName = debugID + "::" + markType, displayName = ReactComponentTreeHook.getDisplayName(debugID) || "Unknown"; - if (performanceNow() - lastMarkTimeStamp > .1) { - var measurementName = displayName + " [" + markType + "]"; - performance.measure(measurementName, markName); - } - performance.clearMarks(markName), measurementName && performance.clearMeasures(measurementName); - } - }; - ReactDebugTool = { - addHook: function(hook) { - hooks.push(hook); - }, - removeHook: function(hook) { - for (var i = 0; i < hooks.length; i++) hooks[i] === hook && (hooks.splice(i, 1), - i--); - }, - isProfiling: function() { - return isProfiling; - }, - beginProfiling: function() { - isProfiling || (isProfiling = !0, flushHistory.length = 0, resetMeasurements(), - ReactDebugTool.addHook(ReactHostOperationHistoryHook_1)); - }, - endProfiling: function() { - isProfiling && (isProfiling = !1, resetMeasurements(), ReactDebugTool.removeHook(ReactHostOperationHistoryHook_1)); - }, - getFlushHistory: function() { - return flushHistory; - }, - onBeginFlush: function() { - currentFlushNesting++, resetMeasurements(), pauseCurrentLifeCycleTimer(), emitEvent("onBeginFlush"); - }, - onEndFlush: function() { - resetMeasurements(), currentFlushNesting--, resumeCurrentLifeCycleTimer(), emitEvent("onEndFlush"); - }, - onBeginLifeCycleTimer: function(debugID, timerType) { - checkDebugID(debugID), emitEvent("onBeginLifeCycleTimer", debugID, timerType), markBegin(debugID, timerType), - beginLifeCycleTimer(debugID, timerType); - }, - onEndLifeCycleTimer: function(debugID, timerType) { - checkDebugID(debugID), endLifeCycleTimer(debugID, timerType), markEnd(debugID, timerType), - emitEvent("onEndLifeCycleTimer", debugID, timerType); - }, - onBeginProcessingChildContext: function() { - emitEvent("onBeginProcessingChildContext"); - }, - onEndProcessingChildContext: function() { - emitEvent("onEndProcessingChildContext"); - }, - onHostOperation: function(operation) { - checkDebugID(operation.instanceID), emitEvent("onHostOperation", operation); - }, - onSetState: function() { - emitEvent("onSetState"); - }, - onSetChildren: function(debugID, childDebugIDs) { - checkDebugID(debugID), childDebugIDs.forEach(checkDebugID), emitEvent("onSetChildren", debugID, childDebugIDs); - }, - onBeforeMountComponent: function(debugID, element, parentDebugID) { - checkDebugID(debugID), checkDebugID(parentDebugID, !0), emitEvent("onBeforeMountComponent", debugID, element, parentDebugID), - markBegin(debugID, "mount"); - }, - onMountComponent: function(debugID) { - checkDebugID(debugID), markEnd(debugID, "mount"), emitEvent("onMountComponent", debugID); - }, - onBeforeUpdateComponent: function(debugID, element) { - checkDebugID(debugID), emitEvent("onBeforeUpdateComponent", debugID, element), markBegin(debugID, "update"); - }, - onUpdateComponent: function(debugID) { - checkDebugID(debugID), markEnd(debugID, "update"), emitEvent("onUpdateComponent", debugID); - }, - onBeforeUnmountComponent: function(debugID) { - checkDebugID(debugID), emitEvent("onBeforeUnmountComponent", debugID), markBegin(debugID, "unmount"); - }, - onUnmountComponent: function(debugID) { - checkDebugID(debugID), markEnd(debugID, "unmount"), emitEvent("onUnmountComponent", debugID); - }, - onTestEvent: function() { - emitEvent("onTestEvent"); - } - }, ReactDebugTool.addHook(ReactInvalidSetStateWarningHook_1), ReactDebugTool.addHook(ReactComponentTreeHook), - /[?&]react_perf\b/.test(ExecutionEnvironment.canUseDOM && window.location.href || "") && ReactDebugTool.beginProfiling(); - var ReactDebugTool_1 = ReactDebugTool, lowPriorityWarning = function() {}, printWarning = function(format) { - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) args[_key - 1] = arguments[_key]; - var argIndex = 0, message = "Warning: " + format.replace(/%s/g, function() { - return args[argIndex++]; - }); - "undefined" != typeof console && console.warn(message); - try { - throw new Error(message); - } catch (x) {} - }; - lowPriorityWarning = function(condition, format) { - if (void 0 === format) throw new Error("`warning(condition, format, ...args)` requires a warning " + "message argument"); - if (!condition) { - for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) args[_key2 - 2] = arguments[_key2]; - printWarning.apply(void 0, [ format ].concat(args)); - } - }; - var lowPriorityWarning_1 = lowPriorityWarning; - function roundFloat(val) { - var base = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 2, n = Math.pow(10, base); - return Math.floor(val * n) / n; - } - function consoleTable(table) { - console.table(table); - } - function getLastMeasurements() { - return ReactDebugTool_1.getFlushHistory(); - } - function getExclusive() { - var flushHistory = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : getLastMeasurements(), aggregatedStats = {}, affectedIDs = {}; - function updateAggregatedStats(treeSnapshot, instanceID, timerType, applyUpdate) { - var displayName = treeSnapshot[instanceID].displayName, key = displayName, stats = aggregatedStats[key]; - stats || (affectedIDs[key] = {}, stats = aggregatedStats[key] = { - key: key, - instanceCount: 0, - counts: {}, - durations: {}, - totalDuration: 0 - }), stats.durations[timerType] || (stats.durations[timerType] = 0), stats.counts[timerType] || (stats.counts[timerType] = 0), - affectedIDs[key][instanceID] = !0, applyUpdate(stats); - } - return flushHistory.forEach(function(flush) { - var measurements = flush.measurements, treeSnapshot = flush.treeSnapshot; - measurements.forEach(function(measurement) { - var duration = measurement.duration, instanceID = measurement.instanceID, timerType = measurement.timerType; - updateAggregatedStats(treeSnapshot, instanceID, timerType, function(stats) { - stats.totalDuration += duration, stats.durations[timerType] += duration, stats.counts[timerType]++; - }); - }); - }), Object.keys(aggregatedStats).map(function(key) { - return Object.assign({}, aggregatedStats[key], { - instanceCount: Object.keys(affectedIDs[key]).length - }); - }).sort(function(a, b) { - return b.totalDuration - a.totalDuration; - }); - } - function getInclusive() { - var flushHistory = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : getLastMeasurements(), aggregatedStats = {}, affectedIDs = {}; - function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) { - var _treeSnapshot$instanc = treeSnapshot[instanceID], displayName = _treeSnapshot$instanc.displayName, ownerID = _treeSnapshot$instanc.ownerID, owner = treeSnapshot[ownerID], key = (owner ? owner.displayName + " > " : "") + displayName, stats = aggregatedStats[key]; - stats || (affectedIDs[key] = {}, stats = aggregatedStats[key] = { - key: key, - instanceCount: 0, - inclusiveRenderDuration: 0, - renderCount: 0 - }), affectedIDs[key][instanceID] = !0, applyUpdate(stats); - } - var isCompositeByID = {}; - return flushHistory.forEach(function(flush) { - flush.measurements.forEach(function(measurement) { - var instanceID = measurement.instanceID; - "render" === measurement.timerType && (isCompositeByID[instanceID] = !0); - }); - }), flushHistory.forEach(function(flush) { - var measurements = flush.measurements, treeSnapshot = flush.treeSnapshot; - measurements.forEach(function(measurement) { - var duration = measurement.duration, instanceID = measurement.instanceID; - if ("render" === measurement.timerType) { - updateAggregatedStats(treeSnapshot, instanceID, function(stats) { - stats.renderCount++; - }); - for (var nextParentID = instanceID; nextParentID; ) isCompositeByID[nextParentID] && updateAggregatedStats(treeSnapshot, nextParentID, function(stats) { - stats.inclusiveRenderDuration += duration; - }), nextParentID = treeSnapshot[nextParentID].parentID; - } - }); - }), Object.keys(aggregatedStats).map(function(key) { - return Object.assign({}, aggregatedStats[key], { - instanceCount: Object.keys(affectedIDs[key]).length - }); - }).sort(function(a, b) { - return b.inclusiveRenderDuration - a.inclusiveRenderDuration; - }); - } - function getWasted() { - var flushHistory = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : getLastMeasurements(), aggregatedStats = {}, affectedIDs = {}; - function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) { - var _treeSnapshot$instanc2 = treeSnapshot[instanceID], displayName = _treeSnapshot$instanc2.displayName, ownerID = _treeSnapshot$instanc2.ownerID, owner = treeSnapshot[ownerID], key = (owner ? owner.displayName + " > " : "") + displayName, stats = aggregatedStats[key]; - stats || (affectedIDs[key] = {}, stats = aggregatedStats[key] = { - key: key, - instanceCount: 0, - inclusiveRenderDuration: 0, - renderCount: 0 - }), affectedIDs[key][instanceID] = !0, applyUpdate(stats); - } - return flushHistory.forEach(function(flush) { - var measurements = flush.measurements, treeSnapshot = flush.treeSnapshot, operations = flush.operations, isDefinitelyNotWastedByID = {}; - operations.forEach(function(operation) { - for (var instanceID = operation.instanceID, nextParentID = instanceID; nextParentID; ) isDefinitelyNotWastedByID[nextParentID] = !0, - nextParentID = treeSnapshot[nextParentID].parentID; - }); - var renderedCompositeIDs = {}; - measurements.forEach(function(measurement) { - var instanceID = measurement.instanceID; - "render" === measurement.timerType && (renderedCompositeIDs[instanceID] = !0); - }), measurements.forEach(function(measurement) { - var duration = measurement.duration, instanceID = measurement.instanceID; - if ("render" === measurement.timerType) { - var updateCount = treeSnapshot[instanceID].updateCount; - if (!isDefinitelyNotWastedByID[instanceID] && 0 !== updateCount) { - updateAggregatedStats(treeSnapshot, instanceID, function(stats) { - stats.renderCount++; - }); - for (var nextParentID = instanceID; nextParentID; ) renderedCompositeIDs[nextParentID] && !isDefinitelyNotWastedByID[nextParentID] && updateAggregatedStats(treeSnapshot, nextParentID, function(stats) { - stats.inclusiveRenderDuration += duration; - }), nextParentID = treeSnapshot[nextParentID].parentID; - } - } - }); - }), Object.keys(aggregatedStats).map(function(key) { - return Object.assign({}, aggregatedStats[key], { - instanceCount: Object.keys(affectedIDs[key]).length - }); - }).sort(function(a, b) { - return b.inclusiveRenderDuration - a.inclusiveRenderDuration; - }); - } - function getOperations() { - var flushHistory = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : getLastMeasurements(), stats = []; - return flushHistory.forEach(function(flush, flushIndex) { - var operations = flush.operations, treeSnapshot = flush.treeSnapshot; - operations.forEach(function(operation) { - var instanceID = operation.instanceID, type = operation.type, payload = operation.payload, _treeSnapshot$instanc3 = treeSnapshot[instanceID], displayName = _treeSnapshot$instanc3.displayName, ownerID = _treeSnapshot$instanc3.ownerID, owner = treeSnapshot[ownerID], key = (owner ? owner.displayName + " > " : "") + displayName; - stats.push({ - flushIndex: flushIndex, - instanceID: instanceID, - key: key, - type: type, - ownerID: ownerID, - payload: payload - }); - }); - }), stats; - } - function printExclusive(flushHistory) { - consoleTable(getExclusive(flushHistory).map(function(item) { - var key = item.key, instanceCount = item.instanceCount, totalDuration = item.totalDuration, renderCount = item.counts.render || 0, renderDuration = item.durations.render || 0; - return { - Component: key, - "Total time (ms)": roundFloat(totalDuration), - "Instance count": instanceCount, - "Total render time (ms)": roundFloat(renderDuration), - "Average render time (ms)": renderCount ? roundFloat(renderDuration / renderCount) : void 0, - "Render count": renderCount, - "Total lifecycle time (ms)": roundFloat(totalDuration - renderDuration) - }; - })); - } - function printInclusive(flushHistory) { - consoleTable(getInclusive(flushHistory).map(function(item) { - var key = item.key, instanceCount = item.instanceCount, inclusiveRenderDuration = item.inclusiveRenderDuration, renderCount = item.renderCount; - return { - "Owner > Component": key, - "Inclusive render time (ms)": roundFloat(inclusiveRenderDuration), - "Instance count": instanceCount, - "Render count": renderCount - }; - })); - } - function printWasted(flushHistory) { - consoleTable(getWasted(flushHistory).map(function(item) { - var key = item.key, instanceCount = item.instanceCount, inclusiveRenderDuration = item.inclusiveRenderDuration, renderCount = item.renderCount; - return { - "Owner > Component": key, - "Inclusive wasted time (ms)": roundFloat(inclusiveRenderDuration), - "Instance count": instanceCount, - "Render count": renderCount - }; - })); - } - function printOperations(flushHistory) { - consoleTable(getOperations(flushHistory).map(function(stat) { - return { - "Owner > Node": stat.key, - Operation: stat.type, - Payload: "object" == typeof stat.payload ? JSON.stringify(stat.payload) : stat.payload, - "Flush index": stat.flushIndex, - "Owner Component ID": stat.ownerID, - "DOM Component ID": stat.instanceID - }; - })); - } - var warnedAboutPrintDOM = !1; - function printDOM(measurements) { - return lowPriorityWarning_1(warnedAboutPrintDOM, "`ReactPerf.printDOM(...)` is deprecated. Use " + "`ReactPerf.printOperations(...)` instead."), - warnedAboutPrintDOM = !0, printOperations(measurements); - } - var warnedAboutGetMeasurementsSummaryMap = !1; - function getMeasurementsSummaryMap(measurements) { - return lowPriorityWarning_1(warnedAboutGetMeasurementsSummaryMap, "`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use " + "`ReactPerf.getWasted(...)` instead."), - warnedAboutGetMeasurementsSummaryMap = !0, getWasted(measurements); - } - function start() { - ReactDebugTool_1.beginProfiling(); - } - function stop() { - ReactDebugTool_1.endProfiling(); - } - function isRunning() { - return ReactDebugTool_1.isProfiling(); - } - var ReactPerfAnalysis = { - getLastMeasurements: getLastMeasurements, - getExclusive: getExclusive, - getInclusive: getInclusive, - getWasted: getWasted, - getOperations: getOperations, - printExclusive: printExclusive, - printInclusive: printInclusive, - printWasted: printWasted, - printOperations: printOperations, - start: start, - stop: stop, - isRunning: isRunning, - printDOM: printDOM, - getMeasurementsSummaryMap: getMeasurementsSummaryMap - }, ReactPerf = ReactPerfAnalysis, injectInternals = ReactFiberDevToolsHook.injectInternals; + var takeSnapshot_1 = takeSnapshot, injectInternals = ReactFiberDevToolsHook.injectInternals; ReactGenericBatching_1.injection.injectFiberBatchedUpdates(ReactNativeFiberRenderer.batchedUpdates); var roots = new Map(); ReactFiberErrorLogger.injection.injectDialog(ReactNativeFiberErrorDialog_1.showDialog); @@ -4450,8 +3974,16 @@ __DEV__ && function() { } }; Object.assign(ReactNativeFiber.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, { - ReactDebugTool: ReactDebugTool_1, - ReactPerf: ReactPerf + ReactDebugTool: { + addHook: function() {}, + removeHook: function() {} + }, + ReactPerf: { + start: function() {}, + stop: function() {}, + printInclusive: function() {}, + printWasted: function() {} + } }), injectInternals({ findFiberByHostInstance: ReactNativeComponentTree_1.getClosestInstanceFromNode, findHostInstanceByFiber: ReactNativeFiberRenderer.findHostInstance, diff --git a/Libraries/Renderer/ReactNativeFiber-prod.js b/Libraries/Renderer/ReactNativeFiber-prod.js index 92357de33627ed..12e7faa7d5419b 100644 --- a/Libraries/Renderer/ReactNativeFiber-prod.js +++ b/Libraries/Renderer/ReactNativeFiber-prod.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @noflow * @providesModule ReactNativeFiber-prod @@ -166,13 +164,9 @@ var EventPluginUtils = { function restoreStateOfTarget(target) { var internalInstance = EventPluginUtils_1.getInstanceFromNode(target); if (internalInstance) { - if ("number" == typeof internalInstance.tag) { - invariant(fiberHostComponent && "function" == typeof fiberHostComponent.restoreControlledState, "Fiber needs to be injected to handle a fiber target for controlled " + "events. This error is likely caused by a bug in React. Please file an issue."); - var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(internalInstance.stateNode); - return void fiberHostComponent.restoreControlledState(internalInstance.stateNode, internalInstance.type, props); - } - invariant("function" == typeof internalInstance.restoreControlledState, "The internal instance must be a React host component. " + "This error is likely caused by a bug in React. Please file an issue."), - internalInstance.restoreControlledState(); + invariant(fiberHostComponent && "function" == typeof fiberHostComponent.restoreControlledState, "Fiber needs to be injected to handle a fiber target for controlled " + "events. This error is likely caused by a bug in React. Please file an issue."); + var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(internalInstance.stateNode); + fiberHostComponent.restoreControlledState(internalInstance.stateNode, internalInstance.type, props); } } @@ -187,18 +181,12 @@ var ReactControlledComponent = { if (restoreTarget = null, restoreQueue = null, restoreStateOfTarget(target), queuedTargets) for (var i = 0; i < queuedTargets.length; i++) restoreStateOfTarget(queuedTargets[i]); } } -}, ReactControlledComponent_1 = ReactControlledComponent, stackBatchedUpdates = function(fn, a, b, c, d, e) { - return fn(a, b, c, d, e); -}, fiberBatchedUpdates = function(fn, bookkeeping) { +}, ReactControlledComponent_1 = ReactControlledComponent, fiberBatchedUpdates = function(fn, bookkeeping) { return fn(bookkeeping); }; -function performFiberBatchedUpdates(fn, bookkeeping) { - return fiberBatchedUpdates(fn, bookkeeping); -} - function batchedUpdates(fn, bookkeeping) { - return stackBatchedUpdates(performFiberBatchedUpdates, fn, bookkeeping); + return fiberBatchedUpdates(fn, bookkeeping); } var isNestingBatched = !1; @@ -214,9 +202,6 @@ function batchedUpdatesWithControlledComponents(fn, bookkeeping) { } var ReactGenericBatchingInjection = { - injectStackBatchedUpdates: function(_batchedUpdates) { - stackBatchedUpdates = _batchedUpdates; - }, injectFiberBatchedUpdates: function(_batchedUpdates) { fiberBatchedUpdates = _batchedUpdates; } @@ -256,25 +241,10 @@ var showDialog$1 = ReactNativeFiberErrorDialog, ReactNativeFiberErrorDialog_1 = REACT_PORTAL_TYPE: REACT_PORTAL_TYPE_1 }, instanceCache = {}, instanceProps = {}; -function getRenderedHostOrTextFromComponent(component) { - for (var rendered; rendered = component._renderedComponent; ) component = rendered; - return component; -} - -function precacheNode(inst, tag) { - var nativeInst = getRenderedHostOrTextFromComponent(inst); - instanceCache[tag] = nativeInst; -} - function precacheFiberNode(hostInst, tag) { instanceCache[tag] = hostInst; } -function uncacheNode(inst) { - var tag = inst._rootNodeID; - tag && delete instanceCache[tag]; -} - function uncacheFiberNode(tag) { delete instanceCache[tag], delete instanceProps[tag]; } @@ -301,9 +271,7 @@ var ReactNativeComponentTree = { getInstanceFromNode: getInstanceFromTag, getNodeFromInstance: getTagFromInstance, precacheFiberNode: precacheFiberNode, - precacheNode: precacheNode, uncacheFiberNode: uncacheFiberNode, - uncacheNode: uncacheNode, getFiberCurrentPropsFromNode: getFiberCurrentPropsFromNode, updateFiberProps: updateFiberProps }, ReactNativeComponentTree_1 = ReactNativeComponentTree, commonjsGlobal = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {}, ReactFeatureFlags = { @@ -522,16 +490,9 @@ var commitCallbacks_1 = commitCallbacks, ReactFiberUpdateQueue = { commitCallbacks: commitCallbacks_1 }; -function getComponentName$1(instanceOrFiber) { - if ("function" == typeof instanceOrFiber.getName) { - return instanceOrFiber.getName(); - } - if ("number" == typeof instanceOrFiber.tag) { - var fiber = instanceOrFiber, type = fiber.type; - if ("string" == typeof type) return type; - if ("function" == typeof type) return type.displayName || type.name; - } - return null; +function getComponentName$1(fiber) { + var type = fiber.type; + return "string" == typeof type ? type : "function" == typeof type ? type.displayName || type.name : null; } var getComponentName_1 = getComponentName$1, ReactInstanceMap = { @@ -724,7 +685,7 @@ var popContextProvider_1 = popContextProvider, popTopLevelContextObject = functi push(contextStackCursor, context, fiber), push(didPerformWorkStackCursor, didChange, fiber); }; -function processChildContext$1(fiber, parentContext, isReconciling) { +function processChildContext$1(fiber, parentContext) { var instance = fiber.stateNode, childContextTypes = fiber.type.childContextTypes; if ("function" != typeof instance.getChildContext) return parentContext; var childContext = void 0; @@ -743,7 +704,7 @@ var processChildContext_1 = processChildContext$1, pushContextProvider = functio var instance = workInProgress.stateNode; if (invariant(instance, "Expected to have an instance by this point. " + "This error is likely caused by a bug in React. Please file an issue."), didChange) { - var mergedContext = processChildContext$1(workInProgress, previousContext, !0); + var mergedContext = processChildContext$1(workInProgress, previousContext); instance.__reactInternalMemoizedMergedChildContext = mergedContext, pop(didPerformWorkStackCursor, workInProgress), pop(contextStackCursor, workInProgress), push(contextStackCursor, mergedContext, workInProgress), push(didPerformWorkStackCursor, didChange, workInProgress); @@ -946,11 +907,11 @@ function coerceRef(current, element) { if (null !== mixedRef && "function" != typeof mixedRef) { if (element._owner) { var owner = element._owner, inst = void 0; - if (owner) if ("number" == typeof owner.tag) { + if (owner) { var ownerFiber = owner; invariant(ownerFiber.tag === ClassComponent$7, "Stateless function components cannot have refs."), inst = ownerFiber.stateNode; - } else inst = owner.getPublicInstance(); + } invariant(inst, "Missing owner for string ref %s. This error is likely caused by a " + "bug in React. Please file an issue.", mixedRef); var stringRef = "" + mixedRef; if (null !== current && null !== current.ref && current.ref._stringRef === stringRef) return current.ref; @@ -2526,27 +2487,14 @@ var injectInternals_1 = injectInternals$1, onCommitRoot_1 = onCommitRoot$1, onCo flushSync: flushSync, deferredUpdates: deferredUpdates }; -}, getContextFiber = function(arg) { - invariant(!1, "Missing injection for fiber getContextForSubtree"); -}; +}, addTopLevelUpdate = ReactFiberUpdateQueue.addTopLevelUpdate, findCurrentUnmaskedContext = ReactFiberContext.findCurrentUnmaskedContext, isContextProvider = ReactFiberContext.isContextProvider, processChildContext = ReactFiberContext.processChildContext, createFiberRoot = ReactFiberRoot.createFiberRoot, HostComponent = ReactTypeOfWork.HostComponent, findCurrentHostFiber = ReactFiberTreeReflection.findCurrentHostFiber, findCurrentHostFiberWithNoPortals = ReactFiberTreeReflection.findCurrentHostFiberWithNoPortals; function getContextForSubtree(parentComponent) { if (!parentComponent) return emptyObject; - var instance = ReactInstanceMap_1.get(parentComponent); - return "number" == typeof instance.tag ? getContextFiber(instance) : instance._processChildContext(instance._context); + var fiber = ReactInstanceMap_1.get(parentComponent), parentContext = findCurrentUnmaskedContext(fiber); + return isContextProvider(fiber) ? processChildContext(fiber, parentContext) : parentContext; } -getContextForSubtree._injectFiber = function(fn) { - getContextFiber = fn; -}; - -var getContextForSubtree_1 = getContextForSubtree, addTopLevelUpdate = ReactFiberUpdateQueue.addTopLevelUpdate, findCurrentUnmaskedContext = ReactFiberContext.findCurrentUnmaskedContext, isContextProvider = ReactFiberContext.isContextProvider, processChildContext = ReactFiberContext.processChildContext, createFiberRoot = ReactFiberRoot.createFiberRoot, HostComponent = ReactTypeOfWork.HostComponent, findCurrentHostFiber = ReactFiberTreeReflection.findCurrentHostFiber, findCurrentHostFiberWithNoPortals = ReactFiberTreeReflection.findCurrentHostFiberWithNoPortals; - -getContextForSubtree_1._injectFiber(function(fiber) { - var parentContext = findCurrentUnmaskedContext(fiber); - return isContextProvider(fiber) ? processChildContext(fiber, parentContext, !1) : parentContext; -}); - var ReactFiberReconciler = function(config) { var getPublicInstance = config.getPublicInstance, _ReactFiberScheduler = ReactFiberScheduler(config), scheduleUpdate = _ReactFiberScheduler.scheduleUpdate, getPriorityContext = _ReactFiberScheduler.getPriorityContext, batchedUpdates = _ReactFiberScheduler.batchedUpdates, unbatchedUpdates = _ReactFiberScheduler.unbatchedUpdates, flushSync = _ReactFiberScheduler.flushSync, deferredUpdates = _ReactFiberScheduler.deferredUpdates; function scheduleTopLevelUpdate(current, element, callback) { @@ -2561,7 +2509,7 @@ var ReactFiberReconciler = function(config) { return createFiberRoot(containerInfo); }, updateContainer: function(element, container, parentComponent, callback) { - var current = container.current, context = getContextForSubtree_1(parentComponent); + var current = container.current, context = getContextForSubtree(parentComponent); null === container.context ? container.context = context : container.pendingContext = context, scheduleTopLevelUpdate(current, element, callback); }, @@ -2892,7 +2840,7 @@ getInspectorDataForViewTag = function() { var ReactNativeFiberInspector = { getInspectorDataForViewTag: getInspectorDataForViewTag -}, ReactVersion = "16.0.0-rc.3"; +}, ReactVersion = "16.0.0"; function findNodeHandle(componentOrHandle) { if (null == componentOrHandle) return null; @@ -3010,22 +2958,11 @@ var EventPluginHub = { injectEventPluginsByName: EventPluginRegistry_1.injectEventPluginsByName }, getListener: function(inst, registrationName) { - var listener; - if ("number" == typeof inst.tag) { - var stateNode = inst.stateNode; - if (!stateNode) return null; - var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(stateNode); - if (!props) return null; - if (listener = props[registrationName], shouldPreventMouseEvent(registrationName, inst.type, props)) return null; - } else { - var currentElement = inst._currentElement; - if ("string" == typeof currentElement || "number" == typeof currentElement) return null; - if (!inst._rootNodeID) return null; - var _props = currentElement.props; - if (listener = _props[registrationName], shouldPreventMouseEvent(registrationName, currentElement.type, _props)) return null; - } - return invariant(!listener || "function" == typeof listener, "Expected `%s` listener to be a function, instead got a value of `%s` type.", registrationName, typeof listener), - listener; + var listener, stateNode = inst.stateNode; + if (!stateNode) return null; + var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(stateNode); + return props ? (listener = props[registrationName], shouldPreventMouseEvent(registrationName, inst.type, props) ? null : (invariant(!listener || "function" == typeof listener, "Expected `%s` listener to be a function, instead got a value of `%s` type.", registrationName, typeof listener), + listener)) : null; }, extractEvents: function(topLevelType, targetInst, nativeEvent, nativeEventTarget) { for (var events, plugins = EventPluginRegistry_1.plugins, i = 0; i < plugins.length; i++) { @@ -3049,14 +2986,10 @@ var EventPluginHub = { }, EventPluginHub_1 = EventPluginHub, HostComponent$10 = ReactTypeOfWork.HostComponent; function getParent(inst) { - if (void 0 !== inst._hostParent) return inst._hostParent; - if ("number" == typeof inst.tag) { - do { - inst = inst.return; - } while (inst && inst.tag !== HostComponent$10); - if (inst) return inst; - } - return null; + do { + inst = inst.return; + } while (inst && inst.tag !== HostComponent$10); + return inst || null; } function getLowestCommonAncestor(instA, instB) { diff --git a/Libraries/Renderer/shims/NativeMethodsMixin.js b/Libraries/Renderer/shims/NativeMethodsMixin.js index 085c653bfeee8e..cd0e673b3bffe1 100644 --- a/Libraries/Renderer/shims/NativeMethodsMixin.js +++ b/Libraries/Renderer/shims/NativeMethodsMixin.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule NativeMethodsMixin * @flow diff --git a/Libraries/Renderer/shims/PooledClass.js b/Libraries/Renderer/shims/PooledClass.js index 7b5d93ab737bd6..ad5d874baa9c03 100644 --- a/Libraries/Renderer/shims/PooledClass.js +++ b/Libraries/Renderer/shims/PooledClass.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule PooledClass * @flow diff --git a/Libraries/Renderer/shims/ReactDebugTool.js b/Libraries/Renderer/shims/ReactDebugTool.js index c532f4bbe9b6b1..e45af4fb099433 100644 --- a/Libraries/Renderer/shims/ReactDebugTool.js +++ b/Libraries/Renderer/shims/ReactDebugTool.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactDebugTool */ diff --git a/Libraries/Renderer/shims/ReactGlobalSharedState.js b/Libraries/Renderer/shims/ReactGlobalSharedState.js index a2b58402019e75..089449a9491972 100644 --- a/Libraries/Renderer/shims/ReactGlobalSharedState.js +++ b/Libraries/Renderer/shims/ReactGlobalSharedState.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactGlobalSharedState */ diff --git a/Libraries/Renderer/shims/ReactNative.js b/Libraries/Renderer/shims/ReactNative.js index cb23d1f68f9e7d..35b7c3909512a3 100644 --- a/Libraries/Renderer/shims/ReactNative.js +++ b/Libraries/Renderer/shims/ReactNative.js @@ -1,10 +1,8 @@ /** * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactNative * @flow diff --git a/Libraries/Renderer/shims/ReactNativeBridgeEventPlugin.js b/Libraries/Renderer/shims/ReactNativeBridgeEventPlugin.js index c7a1ed8c199c7a..3170faedbad7c0 100644 --- a/Libraries/Renderer/shims/ReactNativeBridgeEventPlugin.js +++ b/Libraries/Renderer/shims/ReactNativeBridgeEventPlugin.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactNativeBridgeEventPlugin */ diff --git a/Libraries/Renderer/shims/ReactNativeComponentTree.js b/Libraries/Renderer/shims/ReactNativeComponentTree.js index 92672f3e8fcce7..30ec5257f424c8 100644 --- a/Libraries/Renderer/shims/ReactNativeComponentTree.js +++ b/Libraries/Renderer/shims/ReactNativeComponentTree.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactNativeComponentTree * @flow diff --git a/Libraries/Renderer/shims/ReactNativePropRegistry.js b/Libraries/Renderer/shims/ReactNativePropRegistry.js index d72d46018351fa..1ffd01057d51e4 100644 --- a/Libraries/Renderer/shims/ReactNativePropRegistry.js +++ b/Libraries/Renderer/shims/ReactNativePropRegistry.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactNativePropRegistry * @flow diff --git a/Libraries/Renderer/shims/ReactNativeTypes.js b/Libraries/Renderer/shims/ReactNativeTypes.js index 89c4948c63ae9f..5da6886014ffb1 100644 --- a/Libraries/Renderer/shims/ReactNativeTypes.js +++ b/Libraries/Renderer/shims/ReactNativeTypes.js @@ -1,10 +1,8 @@ /** * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactNativeTypes * @flow diff --git a/Libraries/Renderer/shims/ReactPerf.js b/Libraries/Renderer/shims/ReactPerf.js index e879d267a80ca2..bb6777fff0b989 100644 --- a/Libraries/Renderer/shims/ReactPerf.js +++ b/Libraries/Renderer/shims/ReactPerf.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactPerf */ diff --git a/Libraries/Renderer/shims/ReactTypes.js b/Libraries/Renderer/shims/ReactTypes.js index 8af18318844ed6..736d1210e1f5c2 100644 --- a/Libraries/Renderer/shims/ReactTypes.js +++ b/Libraries/Renderer/shims/ReactTypes.js @@ -1,10 +1,8 @@ /** - * Copyright 2014-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2014-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule ReactTypes * @flow diff --git a/Libraries/Renderer/shims/TouchHistoryMath.js b/Libraries/Renderer/shims/TouchHistoryMath.js index 339bfe03f94f5a..75edb24a7209c4 100644 --- a/Libraries/Renderer/shims/TouchHistoryMath.js +++ b/Libraries/Renderer/shims/TouchHistoryMath.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule TouchHistoryMath */ diff --git a/Libraries/Renderer/shims/createReactNativeComponentClass.js b/Libraries/Renderer/shims/createReactNativeComponentClass.js index 6934beb791d84c..d5d2b8c590093a 100644 --- a/Libraries/Renderer/shims/createReactNativeComponentClass.js +++ b/Libraries/Renderer/shims/createReactNativeComponentClass.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule createReactNativeComponentClass * @flow diff --git a/Libraries/Renderer/shims/takeSnapshot.js b/Libraries/Renderer/shims/takeSnapshot.js index e594b97837be18..00abd891769c7d 100644 --- a/Libraries/Renderer/shims/takeSnapshot.js +++ b/Libraries/Renderer/shims/takeSnapshot.js @@ -1,10 +1,8 @@ /** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. * - * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. * * @providesModule takeSnapshot */ diff --git a/Libraries/StyleSheet/LayoutPropTypes.js b/Libraries/StyleSheet/LayoutPropTypes.js index 69d7310946d087..a4d7a5de3b83bc 100644 --- a/Libraries/StyleSheet/LayoutPropTypes.js +++ b/Libraries/StyleSheet/LayoutPropTypes.js @@ -59,6 +59,28 @@ var LayoutPropTypes = { ReactPropTypes.string, ]), + /** + * When the direction is `ltr`, `start` is equivalent to `left`. + * When the direction is `rtl`, `start` is equivalent to `right`. + * + * This style takes precedence over the `left`, `right`, and `end` styles. + */ + start: ReactPropTypes.oneOfType([ + ReactPropTypes.number, + ReactPropTypes.string, + ]), + + /** + * When the direction is `ltr`, `end` is equivalent to `right`. + * When the direction is `rtl`, `end` is equivalent to `left`. + * + * This style takes precedence over the `left` and `right` styles. + */ + end: ReactPropTypes.oneOfType([ + ReactPropTypes.number, + ReactPropTypes.string, + ]), + /** `top` is the number of logical pixels to offset the top edge of * this component. * @@ -229,6 +251,24 @@ var LayoutPropTypes = { ReactPropTypes.string, ]), + /** + * When direction is `ltr`, `marginStart` is equivalent to `marginLeft`. + * When direction is `rtl`, `marginStart` is equivalent to `marginRight`. + */ + marginStart: ReactPropTypes.oneOfType([ + ReactPropTypes.number, + ReactPropTypes.string, + ]), + + /** + * When direction is `ltr`, `marginEnd` is equivalent to `marginRight`. + * When direction is `rtl`, `marginEnd` is equivalent to `marginLeft`. + */ + marginEnd: ReactPropTypes.oneOfType([ + ReactPropTypes.number, + ReactPropTypes.string, + ]), + /** Setting `padding` has the same effect as setting each of * `paddingTop`, `paddingBottom`, `paddingLeft`, and `paddingRight`. * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding @@ -291,6 +331,24 @@ var LayoutPropTypes = { ReactPropTypes.string, ]), + /** + * When direction is `ltr`, `paddingStart` is equivalent to `paddingLeft`. + * When direction is `rtl`, `paddingStart` is equivalent to `paddingRight`. + */ + paddingStart: ReactPropTypes.oneOfType([ + ReactPropTypes.number, + ReactPropTypes.string, + ]), + + /** + * When direction is `ltr`, `paddingEnd` is equivalent to `paddingRight`. + * When direction is `rtl`, `paddingEnd` is equivalent to `paddingLeft`. + */ + paddingEnd: ReactPropTypes.oneOfType([ + ReactPropTypes.number, + ReactPropTypes.string, + ]), + /** `borderWidth` works like `border-width` in CSS. * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-width * for more details. @@ -303,6 +361,18 @@ var LayoutPropTypes = { */ borderTopWidth: ReactPropTypes.number, + /** + * When direction is `ltr`, `borderStartWidth` is equivalent to `borderLeftWidth`. + * When direction is `rtl`, `borderStartWidth` is equivalent to `borderRightWidth`. + */ + borderStartWidth: ReactPropTypes.number, + + /** + * When direction is `ltr`, `borderEndWidth` is equivalent to `borderRightWidth`. + * When direction is `rtl`, `borderEndWidth` is equivalent to `borderLeftWidth`. + */ + borderEndWidth: ReactPropTypes.number, + /** `borderRightWidth` works like `border-right-width` in CSS. * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-right-width * for more details. diff --git a/Libraries/StyleSheet/StyleSheet.js b/Libraries/StyleSheet/StyleSheet.js index 067ae505fda516..8fd9b1f6cd5141 100644 --- a/Libraries/StyleSheet/StyleSheet.js +++ b/Libraries/StyleSheet/StyleSheet.js @@ -20,6 +20,8 @@ const flatten = require('flattenStyle'); export type Styles = {[key: string]: Object}; export type StyleSheet = {[key: $Keys]: number}; +export type StyleValue = {[key: string]: Object} | number | false | null; +export type StyleProp = StyleValue | Array; let hairlineWidth = PixelRatio.roundToNearestPixel(0.4); if (hairlineWidth === 0) { diff --git a/Libraries/StyleSheet/StyleSheetValidation.js b/Libraries/StyleSheet/StyleSheetValidation.js index 7fe0f234fc64cf..c2ee260c89a44b 100644 --- a/Libraries/StyleSheet/StyleSheetValidation.js +++ b/Libraries/StyleSheet/StyleSheetValidation.js @@ -32,6 +32,12 @@ class StyleSheetValidation { var message1 = '"' + prop + '" is not a valid style property.'; var message2 = '\nValid style props: ' + JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' '); + /* $FlowFixMe(>=0.56.0 site=react_native_oss) This comment suppresses an + * error found when Flow v0.56 was deployed. To see the error delete this + * comment and run Flow. */ + /* $FlowFixMe(>=0.56.0 site=react_native_fb,react_native_oss) This + * comment suppresses an error found when Flow v0.56 was deployed. To see + * the error delete this comment and run Flow. */ styleError(message1, style, caller, message2); } var error = allStylePropTypes[prop]( @@ -43,6 +49,12 @@ class StyleSheetValidation { ReactPropTypesSecret, ); if (error) { + /* $FlowFixMe(>=0.56.0 site=react_native_oss) This comment suppresses an + * error found when Flow v0.56 was deployed. To see the error delete this + * comment and run Flow. */ + /* $FlowFixMe(>=0.56.0 site=react_native_fb,react_native_oss) This + * comment suppresses an error found when Flow v0.56 was deployed. To see + * the error delete this comment and run Flow. */ styleError(error.message, style, caller); } } diff --git a/Libraries/Text/RCTFontAttributes.h b/Libraries/Text/RCTFontAttributes.h new file mode 100644 index 00000000000000..37b9954cd3dda0 --- /dev/null +++ b/Libraries/Text/RCTFontAttributes.h @@ -0,0 +1,31 @@ +/** + * 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. + */ + +#import + +#import "RCTFontAttributesDelegate.h" + +@class RCTAccessibilityManager; + +@interface RCTFontAttributes : NSObject + +@property (nonatomic, weak) id delegate; + +@property (readonly, nonatomic, strong) UIFont *font; + +@property (nonatomic, assign) BOOL allowFontScaling; +@property (nonatomic, copy) NSString *fontFamily; +@property (nonatomic, strong) NSNumber *fontSize; +@property (nonatomic, assign) CGFloat fontSizeMultiplier; +@property (nonatomic, copy) NSString *fontStyle; +@property (nonatomic, copy) NSString *fontWeight; + +- (instancetype)initWithAccessibilityManager:(RCTAccessibilityManager *)accessibilityManager; + +@end diff --git a/Libraries/Text/RCTFontAttributes.m b/Libraries/Text/RCTFontAttributes.m new file mode 100644 index 00000000000000..3cb8da119ee4ab --- /dev/null +++ b/Libraries/Text/RCTFontAttributes.m @@ -0,0 +1,112 @@ +/** + * 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. + */ + +#import "RCTFontAttributes.h" + +#import +#import +#import +#import + +@interface RCTFontAttributes () +{ + RCTAccessibilityManager *_accessibilityManager; +} + +@property (nonatomic, strong) UIFont *font; + +@end + +@implementation RCTFontAttributes + +- (instancetype)initWithAccessibilityManager:(RCTAccessibilityManager *)accessibilityManager +{ + RCTAssertParam(accessibilityManager); + + if (self = [super init]) { + _accessibilityManager = accessibilityManager; + _fontSizeMultiplier = _accessibilityManager.multiplier; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(contentSizeMultiplierDidChange) + name:RCTAccessibilityManagerDidUpdateMultiplierNotification + object:_accessibilityManager]; + + [self updateFont]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)contentSizeMultiplierDidChange +{ + self.fontSizeMultiplier = _accessibilityManager.multiplier; +} + +- (void)setAllowFontScaling:(BOOL)allowFontScaling +{ + _allowFontScaling = allowFontScaling; + [self updateFont]; +} + +- (void)setFontFamily:(NSString *)fontFamily +{ + _fontFamily = fontFamily; + [self updateFont]; +} + +- (void)setFontSize:(NSNumber *)fontSize +{ + _fontSize = fontSize; + [self updateFont]; +} + +- (void)setFontSizeMultiplier:(CGFloat)fontSizeMultiplier +{ + _fontSizeMultiplier = fontSizeMultiplier; + + if (_fontSizeMultiplier == 0) { + RCTLogError(@"fontSizeMultiplier value must be > zero."); + _fontSizeMultiplier = 1.0; + } + + [self updateFont]; +} + +- (void)setFontStyle:(NSString *)fontStyle +{ + _fontStyle = fontStyle; + [self updateFont]; +} + +- (void)setFontWeight:(NSString *)fontWeight +{ + _fontWeight = fontWeight; + [self updateFont]; +} + +- (void)updateFont +{ + CGFloat scaleMultiplier = self.allowFontScaling ? self.fontSizeMultiplier : 1.0; + self.font = [RCTFont updateFont:nil + withFamily:self.fontFamily + size:self.fontSize + weight:self.fontWeight + style:self.fontStyle + variant:nil + scaleMultiplier:scaleMultiplier]; + + [self.delegate fontAttributesDidChangeWithFont:self.font]; +} + +@end diff --git a/Libraries/Text/RCTFontAttributesDelegate.h b/Libraries/Text/RCTFontAttributesDelegate.h new file mode 100644 index 00000000000000..6436c3c8c7e3ea --- /dev/null +++ b/Libraries/Text/RCTFontAttributesDelegate.h @@ -0,0 +1,14 @@ +/** + * 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. + */ + +@protocol RCTFontAttributesDelegate + +- (void)fontAttributesDidChangeWithFont:(UIFont *)font; + +@end diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m index 00770dea920123..fe2b037724f7a3 100644 --- a/Libraries/Text/RCTShadowText.m +++ b/Libraries/Text/RCTShadowText.m @@ -38,7 +38,7 @@ @implementation RCTShadowText CGFloat _cachedTextStorageWidthMode; NSAttributedString *_cachedAttributedString; CGFloat _effectiveLetterSpacing; - UIUserInterfaceLayoutDirection _cachedEffectiveLayoutDirection; + UIUserInterfaceLayoutDirection _cachedLayoutDirection; } static YGSize RCTMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) @@ -72,7 +72,7 @@ - (instancetype)init _fontSizeMultiplier = 1.0; _textAlign = NSTextAlignmentNatural; _writingDirection = NSWritingDirectionNatural; - _cachedEffectiveLayoutDirection = UIUserInterfaceLayoutDirectionLeftToRight; + _cachedLayoutDirection = UIUserInterfaceLayoutDirectionLeftToRight; YGNodeSetMeasureFunc(self.yogaNode, RCTMeasure); @@ -198,7 +198,7 @@ - (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width widthMode:(YGMeasureM _cachedTextStorage && (width == _cachedTextStorageWidth || (isnan(width) && isnan(_cachedTextStorageWidth))) && widthMode == _cachedTextStorageWidthMode && - _cachedEffectiveLayoutDirection == self.effectiveLayoutDirection + _cachedLayoutDirection == self.layoutDirection ) { return _cachedTextStorage; } @@ -272,12 +272,12 @@ - (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily if ( ![self isTextDirty] && _cachedAttributedString && - _cachedEffectiveLayoutDirection == self.effectiveLayoutDirection + _cachedLayoutDirection == self.layoutDirection ) { return _cachedAttributedString; } - _cachedEffectiveLayoutDirection = self.effectiveLayoutDirection; + _cachedLayoutDirection = self.layoutDirection; if (_fontSize && !isnan(_fontSize)) { fontSize = @(_fontSize); @@ -419,7 +419,7 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib // Text alignment NSTextAlignment textAlign = _textAlign; if (textAlign == NSTextAlignmentRight || textAlign == NSTextAlignmentLeft) { - if (_cachedEffectiveLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) { + if (_cachedLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) { if (textAlign == NSTextAlignmentRight) { textAlign = NSTextAlignmentLeft; } else { diff --git a/Libraries/Text/RCTText.xcodeproj/project.pbxproj b/Libraries/Text/RCTText.xcodeproj/project.pbxproj index 9beaaaa51f9ab8..cb8b92e4f38c7a 100644 --- a/Libraries/Text/RCTText.xcodeproj/project.pbxproj +++ b/Libraries/Text/RCTText.xcodeproj/project.pbxproj @@ -39,10 +39,12 @@ 59AF89AB1EDCBCC700F004B1 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59AF89A91EDCBCC700F004B1 /* RCTUITextField.m */; }; 59B125C91E6E4E15004E2A67 /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59B125C81E6E4E15004E2A67 /* RCTUITextView.m */; }; 59B125CA1E6E4E15004E2A67 /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59B125C81E6E4E15004E2A67 /* RCTUITextView.m */; }; + 59E8C5CC1F8833D100204F5E /* RCTFontAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = A85C82991F742AA20036C019 /* RCTFontAttributes.m */; }; 59F60E911E661BDD0081153B /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; }; 59F60E921E661BDD0081153B /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; }; 59F60E931E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; }; 59F60E941E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; }; + A85C829A1F742AA20036C019 /* RCTFontAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = A85C82991F742AA20036C019 /* RCTFontAttributes.m */; }; AF3225F91DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; }; AF3225FA1DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; }; /* End PBXBuildFile section */ @@ -111,6 +113,9 @@ 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextField.m; sourceTree = ""; }; 59F60E8F1E661BDD0081153B /* RCTShadowTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowTextView.h; sourceTree = ""; }; 59F60E901E661BDD0081153B /* RCTShadowTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextView.m; sourceTree = ""; }; + A85C82981F742AA20036C019 /* RCTFontAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFontAttributes.h; sourceTree = ""; }; + A85C82991F742AA20036C019 /* RCTFontAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFontAttributes.m; sourceTree = ""; }; + A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFontAttributesDelegate.h; sourceTree = ""; }; AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+Text.h"; sourceTree = ""; }; AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -126,6 +131,9 @@ 599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */, AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */, AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */, + A85C82981F742AA20036C019 /* RCTFontAttributes.h */, + A85C82991F742AA20036C019 /* RCTFontAttributes.m */, + A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */, 58B511C61A9E6C5C00147676 /* RCTRawTextManager.h */, 58B511C71A9E6C5C00147676 /* RCTRawTextManager.m */, 58B511C81A9E6C5C00147676 /* RCTShadowRawText.h */, @@ -254,6 +262,7 @@ 2D3B5F3B1D9B106F00451313 /* RCTTextView.m in Sources */, 59AF89AB1EDCBCC700F004B1 /* RCTUITextField.m in Sources */, 598F41271F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m in Sources */, + 59E8C5CC1F8833D100204F5E /* RCTFontAttributes.m in Sources */, 2D3B5F3A1D9B106F00451313 /* RCTTextFieldManager.m in Sources */, 599DF2651F03076D0079B53E /* RCTTextInput.m in Sources */, 2D3B5F341D9B103100451313 /* RCTRawTextManager.m in Sources */, @@ -278,6 +287,7 @@ 1362F1001B4D51F400E06D8C /* RCTTextField.m in Sources */, 59AF89AA1EDCBCC700F004B1 /* RCTUITextField.m in Sources */, 598F41261F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m in Sources */, + A85C829A1F742AA20036C019 /* RCTFontAttributes.m in Sources */, 58B512161A9E6EFF00147676 /* RCTText.m in Sources */, 599DF2641F03076D0079B53E /* RCTTextInput.m in Sources */, 1362F1011B4D51F400E06D8C /* RCTTextFieldManager.m in Sources */, diff --git a/Libraries/Text/RCTTextField.m b/Libraries/Text/RCTTextField.m index 567ab31a9c400e..e6addcaf155708 100644 --- a/Libraries/Text/RCTTextField.m +++ b/Libraries/Text/RCTTextField.m @@ -12,6 +12,7 @@ #import #import #import +#import #import #import #import @@ -40,6 +41,7 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _backedTextInput = [[RCTUITextField alloc] initWithFrame:self.bounds]; _backedTextInput.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _backedTextInput.textInputDelegate = self; + _backedTextInput.font = self.fontAttributes.font; [self addSubview:_backedTextInput]; } diff --git a/Libraries/Text/RCTTextFieldManager.m b/Libraries/Text/RCTTextFieldManager.m index f43e607654be7a..7e0d1dc2492927 100644 --- a/Libraries/Text/RCTTextFieldManager.m +++ b/Libraries/Text/RCTTextFieldManager.m @@ -35,11 +35,16 @@ - (UIView *)view #pragma mark - Unified properties +RCT_REMAP_VIEW_PROPERTY(allowFontScaling, fontAttributes.allowFontScaling, BOOL) RCT_REMAP_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType) RCT_REMAP_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType) RCT_REMAP_VIEW_PROPERTY(color, backedTextInputView.textColor, UIColor) RCT_REMAP_VIEW_PROPERTY(editable, backedTextInputView.editable, BOOL) RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL) +RCT_REMAP_VIEW_PROPERTY(fontSize, fontAttributes.fontSize, NSNumber) +RCT_REMAP_VIEW_PROPERTY(fontWeight, fontAttributes.fontWeight, NSString) +RCT_REMAP_VIEW_PROPERTY(fontStyle, fontAttributes.fontStyle, NSString) +RCT_REMAP_VIEW_PROPERTY(fontFamily, fontAttributes.fontFamily, NSString) RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance) RCT_REMAP_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType) RCT_REMAP_VIEW_PROPERTY(placeholder, backedTextInputView.placeholder, NSString) @@ -61,22 +66,7 @@ - (UIView *)view RCT_REMAP_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL) RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode) RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock) -RCT_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, RCTTextField) -{ - view.backedTextInputView.font = [RCTFont updateFont:view.backedTextInputView.font withSize:json ?: @(defaultView.backedTextInputView.font.pointSize)]; -} -RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused RCTTextField) -{ - view.backedTextInputView.font = [RCTFont updateFont:view.backedTextInputView.font withWeight:json]; // defaults to normal -} -RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused RCTTextField) -{ - view.backedTextInputView.font = [RCTFont updateFont:view.backedTextInputView.font withStyle:json]; // defaults to normal -} -RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTTextField) -{ - view.backedTextInputView.font = [RCTFont updateFont:view.backedTextInputView.font withFamily:json ?: defaultView.backedTextInputView.font.familyName]; -} + RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView diff --git a/Libraries/Text/RCTTextInput.h b/Libraries/Text/RCTTextInput.h index 5531fca4e7f4d4..47fcf4263af8b1 100644 --- a/Libraries/Text/RCTTextInput.h +++ b/Libraries/Text/RCTTextInput.h @@ -12,12 +12,14 @@ #import #import "RCTBackedTextInputViewProtocol.h" +#import "RCTFontAttributes.h" +#import "RCTFontAttributesDelegate.h" @class RCTBridge; @class RCTEventDispatcher; @class RCTTextSelection; -@interface RCTTextInput : RCTView { +@interface RCTTextInput : RCTView { @protected RCTBridge *_bridge; RCTEventDispatcher *_eventDispatcher; @@ -41,12 +43,16 @@ @property (nonatomic, copy) RCTDirectEventBlock onContentSizeChange; @property (nonatomic, copy) RCTDirectEventBlock onSelectionChange; +@property (nonatomic, readonly, strong) RCTFontAttributes *fontAttributes; + @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, assign) BOOL blurOnSubmit; @property (nonatomic, assign) BOOL selectTextOnFocus; @property (nonatomic, assign) BOOL clearTextOnFocus; @property (nonatomic, copy) RCTTextSelection *selection; +- (void)setFont:(UIFont *)font; + - (void)invalidateContentSize; // Temporary exposure of particial `RCTBackedTextInputDelegate` support. diff --git a/Libraries/Text/RCTTextInput.m b/Libraries/Text/RCTTextInput.m index ed7bade627f5da..8cf7ff60895cd5 100644 --- a/Libraries/Text/RCTTextInput.m +++ b/Libraries/Text/RCTTextInput.m @@ -9,17 +9,19 @@ #import "RCTTextInput.h" +#import #import #import #import -#import #import +#import #import #import "RCTTextSelection.h" @implementation RCTTextInput { CGSize _previousContentSize; + BOOL _hasInputAccesoryView; } - (instancetype)initWithBridge:(RCTBridge *)bridge @@ -29,6 +31,8 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge if (self = [super initWithFrame:CGRectZero]) { _bridge = bridge; _eventDispatcher = bridge.eventDispatcher; + _fontAttributes = [[RCTFontAttributes alloc] initWithAccessibilityManager:bridge.accessibilityManager]; + _fontAttributes.delegate = self; } return self; @@ -44,6 +48,17 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge return nil; } +- (void)setFont:(UIFont *)font +{ + self.backedTextInputView.font = font; + [self invalidateContentSize]; +} + +- (void)fontAttributesDidChangeWithFont:(UIFont *)font +{ + self.font = font; +} + #pragma mark - Properties - (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets @@ -290,12 +305,12 @@ - (void)invalidateInputAccessoryView ) && textInputView.returnKeyType == UIReturnKeyDone; - BOOL hasInputAccesoryView = textInputView.inputAccessoryView != nil; - - if (hasInputAccesoryView == shouldHaveInputAccesoryView) { + if (_hasInputAccesoryView == shouldHaveInputAccesoryView) { return; } + _hasInputAccesoryView = shouldHaveInputAccesoryView; + if (shouldHaveInputAccesoryView) { UIToolbar *toolbarView = [[UIToolbar alloc] init]; [toolbarView sizeToFit]; diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index 3342e445e42f6a..f3f19067c0bc49 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -11,6 +11,7 @@ #import #import +#import #import #import #import @@ -54,8 +55,8 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _backedTextInput.scrollsToTop = NO; #endif _backedTextInput.scrollEnabled = YES; - _backedTextInput.textInputDelegate = self; + _backedTextInput.font = self.fontAttributes.font; [self addSubview:_backedTextInput]; } diff --git a/Libraries/Text/RCTTextViewManager.m b/Libraries/Text/RCTTextViewManager.m index 5f0fa6de9ad333..814f2070f7b4b6 100644 --- a/Libraries/Text/RCTTextViewManager.m +++ b/Libraries/Text/RCTTextViewManager.m @@ -35,11 +35,16 @@ - (UIView *)view #pragma mark - Unified properties +RCT_REMAP_VIEW_PROPERTY(allowFontScaling, fontAttributes.allowFontScaling, BOOL) RCT_REMAP_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType) RCT_REMAP_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType) RCT_REMAP_VIEW_PROPERTY(color, backedTextInputView.textColor, UIColor) RCT_REMAP_VIEW_PROPERTY(editable, backedTextInputView.editable, BOOL) RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL) +RCT_REMAP_VIEW_PROPERTY(fontSize, fontAttributes.fontSize, NSNumber) +RCT_REMAP_VIEW_PROPERTY(fontWeight, fontAttributes.fontWeight, NSString) +RCT_REMAP_VIEW_PROPERTY(fontStyle, fontAttributes.fontStyle, NSString) +RCT_REMAP_VIEW_PROPERTY(fontFamily, fontAttributes.fontFamily, NSString) RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance) RCT_REMAP_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType) RCT_REMAP_VIEW_PROPERTY(placeholder, backedTextInputView.placeholder, NSString) @@ -64,22 +69,6 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onTextInput, RCTDirectEventBlock) -RCT_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, RCTTextView) -{ - view.font = [RCTFont updateFont:view.font withSize:json ?: @(defaultView.font.pointSize)]; -} -RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused RCTTextView) -{ - view.font = [RCTFont updateFont:view.font withWeight:json]; // defaults to normal -} -RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused RCTTextView) -{ - view.font = [RCTFont updateFont:view.font withStyle:json]; // defaults to normal -} -RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTTextView) -{ - view.font = [RCTFont updateFont:view.font withFamily:json ?: defaultView.font.familyName]; -} RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) #if !TARGET_OS_TV diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js index 7c013f30fdd922..232a32837ab15f 100644 --- a/Libraries/Text/Text.js +++ b/Libraries/Text/Text.js @@ -52,7 +52,7 @@ const viewConfig = { * * In the following example, the nested title and body text will inherit the * `fontFamily` from `styles.baseText`, but the title provides its own - * additional styles. The title and body willstack on top of each other on + * additional styles. The title and body will stack on top of each other on * account of the literal newlines: * * ```ReactNativeWebPlayer diff --git a/Libraries/Utilities/Dimensions.js b/Libraries/Utilities/Dimensions.js index 76b4098b8589cd..cbb33d41e426cc 100644 --- a/Libraries/Utilities/Dimensions.js +++ b/Libraries/Utilities/Dimensions.js @@ -107,7 +107,7 @@ class Dimensions { handler: Function ) { invariant( - 'change' === type, + type === 'change', 'Trying to subscribe to unknown event: "%s"', type ); eventEmitter.addListener(type, handler); @@ -121,7 +121,7 @@ class Dimensions { handler: Function ) { invariant( - 'change' === type, + type === 'change', 'Trying to remove listener for unknown event: "%s"', type ); eventEmitter.removeListener(type, handler); diff --git a/Libraries/Utilities/PixelRatio.js b/Libraries/Utilities/PixelRatio.js index b2bbb0e0c4fdb1..6d68ef03c70e2c 100644 --- a/Libraries/Utilities/PixelRatio.js +++ b/Libraries/Utilities/PixelRatio.js @@ -29,33 +29,33 @@ var Dimensions = require('Dimensions'); * }); * * ``` - * + * * ## Pixel grid snapping - * - * In iOS, you can specify positions and dimensions for elements with arbitrary + * + * In iOS, you can specify positions and dimensions for elements with arbitrary * precision, for example 29.674825. But, ultimately the physical display only * have a fixed number of pixels, for example 640×960 for iPhone 4 or 750×1334 - * for iPhone 6. iOS tries to be as faithful as possible to the user value by - * spreading one original pixel into multiple ones to trick the eye. The - * downside of this technique is that it makes the resulting element look + * for iPhone 6. iOS tries to be as faithful as possible to the user value by + * spreading one original pixel into multiple ones to trick the eye. The + * downside of this technique is that it makes the resulting element look * blurry. - * - * In practice, we found out that developers do not want this feature and they - * have to work around it by doing manual rounding in order to avoid having - * blurry elements. In React Native, we are rounding all the pixels + * + * In practice, we found out that developers do not want this feature and they + * have to work around it by doing manual rounding in order to avoid having + * blurry elements. In React Native, we are rounding all the pixels * automatically. - * - * We have to be careful when to do this rounding. You never want to work with - * rounded and unrounded values at the same time as you're going to accumulate - * rounding errors. Having even one rounding error is deadly because a one + * + * We have to be careful when to do this rounding. You never want to work with + * rounded and unrounded values at the same time as you're going to accumulate + * rounding errors. Having even one rounding error is deadly because a one * pixel border may vanish or be twice as big. - * + * * In React Native, everything in JavaScript and within the layout engine works - * with arbitrary precision numbers. It's only when we set the position and - * dimensions of the native element on the main thread that we round. Also, - * rounding is done relative to the root rather than the parent, again to avoid + * with arbitrary precision numbers. It's only when we set the position and + * dimensions of the native element on the main thread that we round. Also, + * rounding is done relative to the root rather than the parent, again to avoid * accumulating rounding errors. - * + * */ class PixelRatio { /** diff --git a/Libraries/Utilities/RCTLog.js b/Libraries/Utilities/RCTLog.js index 6d09e8a9274a5a..a51d38d2d36cff 100644 --- a/Libraries/Utilities/RCTLog.js +++ b/Libraries/Utilities/RCTLog.js @@ -51,6 +51,6 @@ const RCTLog = { setWarningHandler(handler: typeof warningHandler): void { warningHandler = handler; } -} +}; module.exports = RCTLog; diff --git a/Libraries/Utilities/__tests__/buildStyleInterpolator-test.js b/Libraries/Utilities/__tests__/buildStyleInterpolator-test.js index c919e314ddcede..c1af55175be6f6 100644 --- a/Libraries/Utilities/__tests__/buildStyleInterpolator-test.js +++ b/Libraries/Utilities/__tests__/buildStyleInterpolator-test.js @@ -227,7 +227,97 @@ describe('buildStyleInterpolator', function() { }); expect(res).toBe(false); }); + it('should handle identity', function() { + var testAnim = { + opacity: { + type: 'identity', + }, + }; + var interpolator = buildStyleInterpolator(testAnim); + var obj = {}; + var res = interpolator(obj, 0.5); + expect(obj).toEqual({ + opacity: 0.5, + }); + expect(res).toBe(true); + res = interpolator(obj, 0.5); + // No change detected + expect(obj).toEqual({ + opacity: 0.5, + }); + expect(res).toBe(false); + }); + it('should translate', function() { + var testAnim = { + transformTranslate: { + from: {x: 1, y: 10, z: 100}, + to: {x: 5, y: 50, z: 500}, + min: 0, + max: 4, + type: 'linear', + }, + }; + var interpolator = buildStyleInterpolator(testAnim); + var obj = {}; + var res = interpolator(obj, 1); + expect(obj).toEqual({ + transform: [{matrix: [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 2, 20, 200, 1]}] + }); + expect(res).toBe(true); + }); + it('should scale', function() { + var testAnim = { + transformScale: { + from: {x: 1, y: 10, z: 100}, + to: {x: 5, y: 50, z: 500}, + min: 0, + max: 4, + type: 'linear', + }, + }; + var interpolator = buildStyleInterpolator(testAnim); + var obj = {}; + var res = interpolator(obj, 1); + expect(obj).toEqual({ + transform: [{matrix: [2, 0, 0, 0, + 0, 20, 0, 0, + 0, 0, 200, 0, + 0, 0, 0, 1]}] + }); + expect(res).toBe(true); + }); + it('should combine scale and translate', function() { + var testAnim = { + transformScale: { + from: {x: 1, y: 10, z: 100}, + to: {x: 5, y: 50, z: 500}, + min: 0, + max: 4, + type: 'linear', + }, + transformTranslate: { + from: {x: 1, y: 10, z: 100}, + to: {x: 5, y: 50, z: 500}, + min: 0, + max: 4, + type: 'linear', + }, + }; + var interpolator = buildStyleInterpolator(testAnim); + var obj = {}; + var res = interpolator(obj, 1); + expect(obj).toEqual({ + transform: [{matrix: [2, 0, 0, 0, + 0, 20, 0, 0, + 0, 0, 200, 0, + 4, 400, 40000, 1]}] + }); + expect(res).toBe(true); + }); it('should step', function() { var testAnim = { opacity: { diff --git a/Libraries/Utilities/buildStyleInterpolator.js b/Libraries/Utilities/buildStyleInterpolator.js index 06dee18987239e..41d65e1bf344b9 100644 --- a/Libraries/Utilities/buildStyleInterpolator.js +++ b/Libraries/Utilities/buildStyleInterpolator.js @@ -9,547 +9,137 @@ * @providesModule buildStyleInterpolator */ -/** - * Cannot "use strict" because we must use eval in this file. - */ -/* eslint-disable global-strict */ +'use strict'; var keyOf = require('fbjs/lib/keyOf'); var X_DIM = keyOf({x: null}); var Y_DIM = keyOf({y: null}); var Z_DIM = keyOf({z: null}); -var W_DIM = keyOf({w: null}); - -var TRANSFORM_ROTATE_NAME = keyOf({transformRotateRadians: null}); - -var ShouldAllocateReusableOperationVars = { - transformRotateRadians: true, - transformScale: true, - transformTranslate: true, -}; var InitialOperationField = { - transformRotateRadians: [0, 0, 0, 1], transformTranslate: [0, 0, 0], transformScale: [1, 1, 1], }; - -/** - * Creates a highly specialized animation function that may be evaluated every - * frame. For example: - * - * var ToTheLeft = { - * opacity: { - * from: 1, - * to: 0.7, - * min: 0, - * max: 1, - * type: 'linear', - * extrapolate: false, - * round: 100, - * }, - * left: { - * from: 0, - * to: -SCREEN_WIDTH * 0.3, - * min: 0, - * max: 1, - * type: 'linear', - * extrapolate: true, - * round: PixelRatio.get(), - * }, - * }; - * - * var toTheLeft = buildStyleInterpolator(ToTheLeft); - * - * Would returns a specialized function of the form: - * - * function(result, value) { - * var didChange = false; - * var nextScalarVal; - * var ratio; - * ratio = (value - 0) / 1; - * ratio = ratio > 1 ? 1 : (ratio < 0 ? 0 : ratio); - * nextScalarVal = Math.round(100 * (1 * (1 - ratio) + 0.7 * ratio)) / 100; - * if (!didChange) { - * var prevVal = result.opacity; - * result.opacity = nextScalarVal; - * didChange = didChange || (nextScalarVal !== prevVal); - * } else { - * result.opacity = nextScalarVal; - * } - * ratio = (value - 0) / 1; - * nextScalarVal = Math.round(2 * (0 * (1 - ratio) + -30 * ratio)) / 2; - * if (!didChange) { - * var prevVal = result.left; - * result.left = nextScalarVal; - * didChange = didChange || (nextScalarVal !== prevVal); - * } else { - * result.left = nextScalarVal; - * } - * return didChange; - * } - */ - -var ARGUMENT_NAMES_RE = /([^\s,]+)/g; -/** - * This is obviously a huge hack. Proper tooling would allow actual inlining. - * This only works in a few limited cases (where there is no function return - * value, and the function operates mutatively on parameters). - * - * Example: - * - * - * var inlineMe(a, b) { - * a = b + b; - * }; - * - * inline(inlineMe, ['hi', 'bye']); // "hi = bye + bye;" - * - * @param {string} fnStr Source of any simple function who's arguments can be - * replaced via a regex. - * @param {array} replaceWithArgs Corresponding names of variables - * within an environment, to replace `func` args with. - * @return {string} Resulting function body string. - */ -var inline = function(fnStr, replaceWithArgs) { - var parameterNames = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')) - .match(ARGUMENT_NAMES_RE) || - []; - var replaceRegexStr = parameterNames.map(function(paramName) { - return '\\b' + paramName + '\\b'; - }).join('|'); - var replaceRegex = new RegExp(replaceRegexStr, 'g'); - var fnBody = fnStr.substring(fnStr.indexOf('{') + 1, fnStr.lastIndexOf('}')); - var newFnBody = fnBody.replace(replaceRegex, function(parameterName) { - var indexInParameterNames = parameterNames.indexOf(parameterName); - var replacementName = replaceWithArgs[indexInParameterNames]; - return replacementName; - }); - return newFnBody.split('\n'); -}; - -/** - * Simply a convenient way to inline functions using the inline function. - */ -var MatrixOps = { - unroll: `function(matVar, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15) { - m0 = matVar[0]; - m1 = matVar[1]; - m2 = matVar[2]; - m3 = matVar[3]; - m4 = matVar[4]; - m5 = matVar[5]; - m6 = matVar[6]; - m7 = matVar[7]; - m8 = matVar[8]; - m9 = matVar[9]; - m10 = matVar[10]; - m11 = matVar[11]; - m12 = matVar[12]; - m13 = matVar[13]; - m14 = matVar[14]; - m15 = matVar[15]; - }`, - - matrixDiffers: `function(retVar, matVar, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15) { - retVar = retVar || - m0 !== matVar[0] || - m1 !== matVar[1] || - m2 !== matVar[2] || - m3 !== matVar[3] || - m4 !== matVar[4] || - m5 !== matVar[5] || - m6 !== matVar[6] || - m7 !== matVar[7] || - m8 !== matVar[8] || - m9 !== matVar[9] || - m10 !== matVar[10] || - m11 !== matVar[11] || - m12 !== matVar[12] || - m13 !== matVar[13] || - m14 !== matVar[14] || - m15 !== matVar[15]; - }`, - - transformScale: `function(matVar, opVar) { - // Scaling matVar by opVar - var x = opVar[0]; - var y = opVar[1]; - var z = opVar[2]; - matVar[0] = matVar[0] * x; - matVar[1] = matVar[1] * x; - matVar[2] = matVar[2] * x; - matVar[3] = matVar[3] * x; - matVar[4] = matVar[4] * y; - matVar[5] = matVar[5] * y; - matVar[6] = matVar[6] * y; - matVar[7] = matVar[7] * y; - matVar[8] = matVar[8] * z; - matVar[9] = matVar[9] * z; - matVar[10] = matVar[10] * z; - matVar[11] = matVar[11] * z; - matVar[12] = matVar[12]; - matVar[13] = matVar[13]; - matVar[14] = matVar[14]; - matVar[15] = matVar[15]; - }`, - - /** - * All of these matrix transforms are not general purpose utilities, and are - * only suitable for being inlined for the use of building up interpolators. - */ - transformTranslate: `function(matVar, opVar) { - // Translating matVar by opVar - var x = opVar[0]; - var y = opVar[1]; - var z = opVar[2]; - matVar[12] = matVar[0] * x + matVar[4] * y + matVar[8] * z + matVar[12]; - matVar[13] = matVar[1] * x + matVar[5] * y + matVar[9] * z + matVar[13]; - matVar[14] = matVar[2] * x + matVar[6] * y + matVar[10] * z + matVar[14]; - matVar[15] = matVar[3] * x + matVar[7] * y + matVar[11] * z + matVar[15]; - }`, - - /** - * @param {array} matVar Both the input, and the output matrix. - * @param {quaternion specification} q Four element array describing rotation. - */ - transformRotateRadians: `function(matVar, q) { - // Rotating matVar by q - var xQuat = q[0], yQuat = q[1], zQuat = q[2], wQuat = q[3]; - var x2Quat = xQuat + xQuat; - var y2Quat = yQuat + yQuat; - var z2Quat = zQuat + zQuat; - var xxQuat = xQuat * x2Quat; - var xyQuat = xQuat * y2Quat; - var xzQuat = xQuat * z2Quat; - var yyQuat = yQuat * y2Quat; - var yzQuat = yQuat * z2Quat; - var zzQuat = zQuat * z2Quat; - var wxQuat = wQuat * x2Quat; - var wyQuat = wQuat * y2Quat; - var wzQuat = wQuat * z2Quat; - // Step 1: Inlines the construction of a quaternion matrix ('quatMat') - var quatMat0 = 1 - (yyQuat + zzQuat); - var quatMat1 = xyQuat + wzQuat; - var quatMat2 = xzQuat - wyQuat; - var quatMat4 = xyQuat - wzQuat; - var quatMat5 = 1 - (xxQuat + zzQuat); - var quatMat6 = yzQuat + wxQuat; - var quatMat8 = xzQuat + wyQuat; - var quatMat9 = yzQuat - wxQuat; - var quatMat10 = 1 - (xxQuat + yyQuat); - // quatMat3/7/11/12/13/14 = 0, quatMat15 = 1 - - // Step 2: Inlines multiplication, takes advantage of constant quatMat cells - var a00 = matVar[0]; - var a01 = matVar[1]; - var a02 = matVar[2]; - var a03 = matVar[3]; - var a10 = matVar[4]; - var a11 = matVar[5]; - var a12 = matVar[6]; - var a13 = matVar[7]; - var a20 = matVar[8]; - var a21 = matVar[9]; - var a22 = matVar[10]; - var a23 = matVar[11]; - - var b0 = quatMat0, b1 = quatMat1, b2 = quatMat2; - matVar[0] = b0 * a00 + b1 * a10 + b2 * a20; - matVar[1] = b0 * a01 + b1 * a11 + b2 * a21; - matVar[2] = b0 * a02 + b1 * a12 + b2 * a22; - matVar[3] = b0 * a03 + b1 * a13 + b2 * a23; - b0 = quatMat4; b1 = quatMat5; b2 = quatMat6; - matVar[4] = b0 * a00 + b1 * a10 + b2 * a20; - matVar[5] = b0 * a01 + b1 * a11 + b2 * a21; - matVar[6] = b0 * a02 + b1 * a12 + b2 * a22; - matVar[7] = b0 * a03 + b1 * a13 + b2 * a23; - b0 = quatMat8; b1 = quatMat9; b2 = quatMat10; - matVar[8] = b0 * a00 + b1 * a10 + b2 * a20; - matVar[9] = b0 * a01 + b1 * a11 + b2 * a21; - matVar[10] = b0 * a02 + b1 * a12 + b2 * a22; - matVar[11] = b0 * a03 + b1 * a13 + b2 * a23; - }` -}; - -// Optimized version of general operation applications that can be used when -// the target matrix is known to be the identity matrix. -var MatrixOpsInitial = { - transformScale: `function(matVar, opVar) { - // Scaling matVar known to be identity by opVar - matVar[0] = opVar[0]; - matVar[1] = 0; - matVar[2] = 0; - matVar[3] = 0; - matVar[4] = 0; - matVar[5] = opVar[1]; - matVar[6] = 0; - matVar[7] = 0; - matVar[8] = 0; - matVar[9] = 0; - matVar[10] = opVar[2]; - matVar[11] = 0; - matVar[12] = 0; - matVar[13] = 0; - matVar[14] = 0; - matVar[15] = 1; - }`, - - transformTranslate: `function(matVar, opVar) { - // Translating matVar known to be identity by opVar; - matVar[0] = 1; - matVar[1] = 0; - matVar[2] = 0; - matVar[3] = 0; - matVar[4] = 0; - matVar[5] = 1; - matVar[6] = 0; - matVar[7] = 0; - matVar[8] = 0; - matVar[9] = 0; - matVar[10] = 1; - matVar[11] = 0; - matVar[12] = opVar[0]; - matVar[13] = opVar[1]; - matVar[14] = opVar[2]; - matVar[15] = 1; - }`, - - /** - * @param {array} matVar Both the input, and the output matrix - assumed to be - * identity. - * @param {quaternion specification} q Four element array describing rotation. - */ - transformRotateRadians: `function(matVar, q) { - - // Rotating matVar which is known to be identity by q - var xQuat = q[0], yQuat = q[1], zQuat = q[2], wQuat = q[3]; - var x2Quat = xQuat + xQuat; - var y2Quat = yQuat + yQuat; - var z2Quat = zQuat + zQuat; - var xxQuat = xQuat * x2Quat; - var xyQuat = xQuat * y2Quat; - var xzQuat = xQuat * z2Quat; - var yyQuat = yQuat * y2Quat; - var yzQuat = yQuat * z2Quat; - var zzQuat = zQuat * z2Quat; - var wxQuat = wQuat * x2Quat; - var wyQuat = wQuat * y2Quat; - var wzQuat = wQuat * z2Quat; - // Step 1: Inlines the construction of a quaternion matrix ('quatMat') - var quatMat0 = 1 - (yyQuat + zzQuat); - var quatMat1 = xyQuat + wzQuat; - var quatMat2 = xzQuat - wyQuat; - var quatMat4 = xyQuat - wzQuat; - var quatMat5 = 1 - (xxQuat + zzQuat); - var quatMat6 = yzQuat + wxQuat; - var quatMat8 = xzQuat + wyQuat; - var quatMat9 = yzQuat - wxQuat; - var quatMat10 = 1 - (xxQuat + yyQuat); - // quatMat3/7/11/12/13/14 = 0, quatMat15 = 1 - - // Step 2: Inlines the multiplication with identity matrix. - var b0 = quatMat0, b1 = quatMat1, b2 = quatMat2; - matVar[0] = b0; - matVar[1] = b1; - matVar[2] = b2; - matVar[3] = 0; - b0 = quatMat4; b1 = quatMat5; b2 = quatMat6; - matVar[4] = b0; - matVar[5] = b1; - matVar[6] = b2; - matVar[7] = 0; - b0 = quatMat8; b1 = quatMat9; b2 = quatMat10; - matVar[8] = b0; - matVar[9] = b1; - matVar[10] = b2; - matVar[11] = 0; - matVar[12] = 0; - matVar[13] = 0; - matVar[14] = 0; - matVar[15] = 1; - }` -}; - - -var setNextValAndDetectChange = function(name, tmpVarName) { - return ( - ' if (!didChange) {\n' + - ' var prevVal = result.' + name + ';\n' + - ' result.' + name + ' = ' + tmpVarName + ';\n' + - ' didChange = didChange || (' + tmpVarName + ' !== prevVal);\n' + - ' } else {\n' + - ' result.' + name + ' = ' + tmpVarName + ';\n' + - ' }\n' - ); +var InterpolateMatrix = { + transformScale: function(mat, x, y, z) { + mat[0] = mat[0] * x; + mat[1] = mat[1] * x; + mat[2] = mat[2] * x; + mat[3] = mat[3] * x; + mat[4] = mat[4] * y; + mat[5] = mat[5] * y; + mat[6] = mat[6] * y; + mat[7] = mat[7] * y; + mat[8] = mat[8] * z; + mat[9] = mat[9] * z; + mat[10] = mat[10] * z; + mat[11] = mat[11] * z; + }, + transformTranslate: function(mat, x, y, z) { + mat[12] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12]; + mat[13] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13]; + mat[14] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14]; + mat[15] = mat[3] * x + mat[7] * y + mat[11] * z + mat[15]; + } }; -var computeNextValLinear = function(anim, from, to, tmpVarName) { +var computeNextValLinear = function(anim, from, to, value) { var hasRoundRatio = 'round' in anim; var roundRatio = anim.round; - var fn = ' ratio = (value - ' + anim.min + ') / ' + (anim.max - anim.min) + ';\n'; + var ratio = (value - anim.min) / (anim.max - anim.min); if (!anim.extrapolate) { - fn += ' ratio = ratio > 1 ? 1 : (ratio < 0 ? 0 : ratio);\n'; + ratio = ratio > 1 ? 1 : (ratio < 0 ? 0 : ratio); } - - var roundOpen = (hasRoundRatio ? 'Math.round(' + roundRatio + ' * ' : '' ); - var roundClose = (hasRoundRatio ? ') / ' + roundRatio : '' ); - fn += - ' ' + tmpVarName + ' = ' + - roundOpen + - '(' + from + ' * (1 - ratio) + ' + to + ' * ratio)' + - roundClose + ';\n'; - return fn; -}; - -var computeNextValLinearScalar = function(anim) { - return computeNextValLinear(anim, anim.from, anim.to, 'nextScalarVal'); -}; - -var computeNextValConstant = function(anim) { - var constantExpression = JSON.stringify(anim.value); - return ' nextScalarVal = ' + constantExpression + ';\n'; -}; - -var computeNextValStep = function(anim) { - return ( - ' nextScalarVal = value >= ' + - (anim.threshold + ' ? ' + anim.to + ' : ' + anim.from) + ';\n' - ); -}; - -var computeNextValIdentity = function(anim) { - return ' nextScalarVal = value;\n'; -}; - -var operationVar = function(name) { - return name + 'ReuseOp'; -}; - -var createReusableOperationVars = function(anims) { - var ret = ''; - for (var name in anims) { - if (ShouldAllocateReusableOperationVars[name]) { - ret += 'var ' + operationVar(name) + ' = [];\n'; - } + var nextVal = from * (1 - ratio) + to * ratio; + if (hasRoundRatio) { + nextVal = Math.round(roundRatio * nextVal) / roundRatio; } - return ret; + return nextVal; }; -var newlines = function(statements) { - return '\n' + statements.join('\n') + '\n'; +var computeNextValLinearScalar = function(anim, value) { + return computeNextValLinear(anim, anim.from, anim.to, value); }; -/** - * @param {Animation} anim Configuration entry. - * @param {key} dimension Key to examine in `from`/`to`. - * @param {number} index Field in operationVar to set. - * @return {string} Code that sets the operation variable's field. - */ -var computeNextMatrixOperationField = function(anim, name, dimension, index) { - var fieldAccess = operationVar(name) + '[' + index + ']'; - if (anim.from[dimension] !== undefined && anim.to[dimension] !== undefined) { - return ' ' + anim.from[dimension] !== anim.to[dimension] ? - computeNextValLinear(anim, anim.from[dimension], anim.to[dimension], fieldAccess) : - fieldAccess + ' = ' + anim.from[dimension] + ';'; +var setNextValAndDetectChange = function(result, name, nextVal, didChange) { + if (!didChange) { + var prevVal = result[name]; + result[name] = nextVal; + didChange = didChange || (nextVal !== prevVal); } else { - return ' ' + fieldAccess + ' = ' + InitialOperationField[name][index] + ';'; + result[name] = nextVal; } -}; - -var unrolledVars = []; -for (var varIndex = 0; varIndex < 16; varIndex++) { - unrolledVars.push('m' + varIndex); -} -var setNextMatrixAndDetectChange = function(orderedMatrixOperations) { - var fn = [ - ' var transform = result.transform !== undefined ? ' + - 'result.transform : (result.transform = [{ matrix: [] }]);' + - ' var transformMatrix = transform[0].matrix;' - ]; - fn.push.apply( - fn, - inline(MatrixOps.unroll, ['transformMatrix'].concat(unrolledVars)) - ); - for (var i = 0; i < orderedMatrixOperations.length; i++) { - var opName = orderedMatrixOperations[i]; - if (i === 0) { - fn.push.apply( - fn, - inline(MatrixOpsInitial[opName], ['transformMatrix', operationVar(opName)]) - ); - } else { - fn.push.apply( - fn, - inline(MatrixOps[opName], ['transformMatrix', operationVar(opName)]) - ); - } + return didChange; +}; + +var initIdentity = function(mat) { + mat[0] = 1; + mat[1] = 0; + mat[2] = 0; + mat[3] = 0; + mat[4] = 0; + mat[5] = 1; + mat[6] = 0; + mat[7] = 0; + mat[8] = 0; + mat[9] = 0; + mat[10] = 1; + mat[11] = 0; + mat[12] = 0; + mat[13] = 0; + mat[14] = 0; + mat[15] = 1; +}; + +var computeNextMatrixOperationField = function(anim, name, dim, index, value) { + if (anim.from[dim] !== undefined && anim.to[dim] !== undefined) { + return computeNextValLinear(anim, anim.from[dim], anim.to[dim], value); + } else { + return InitialOperationField[name][index]; } - fn.push.apply( - fn, - inline(MatrixOps.matrixDiffers, ['didChange', 'transformMatrix'].concat(unrolledVars)) - ); - return fn; }; -var InterpolateMatrix = { - transformTranslate: true, - transformRotateRadians: true, - transformScale: true, -}; - -var createFunctionString = function(anims) { - // We must track the order they appear in so transforms are applied in the - // correct order. - var orderedMatrixOperations = []; - - // Wrapping function allows the final function to contain state (for - // caching). - var fn = 'return (function() {\n'; - fn += createReusableOperationVars(anims); - fn += 'return function(result, value) {\n'; - fn += ' var didChange = false;\n'; - fn += ' var nextScalarVal;\n'; - fn += ' var ratio;\n'; - - for (var name in anims) { - var anim = anims[name]; - if (anim.type === 'linear') { - if (InterpolateMatrix[name]) { - orderedMatrixOperations.push(name); - var setOperations = [ - computeNextMatrixOperationField(anim, name, X_DIM, 0), - computeNextMatrixOperationField(anim, name, Y_DIM, 1), - computeNextMatrixOperationField(anim, name, Z_DIM, 2) - ]; - if (name === TRANSFORM_ROTATE_NAME) { - setOperations.push(computeNextMatrixOperationField(anim, name, W_DIM, 3)); - } - fn += newlines(setOperations); - } else { - fn += computeNextValLinearScalar(anim, 'nextScalarVal'); - fn += setNextValAndDetectChange(name, 'nextScalarVal'); - } - } else if (anim.type === 'constant') { - fn += computeNextValConstant(anim); - fn += setNextValAndDetectChange(name, 'nextScalarVal'); - } else if (anim.type === 'step') { - fn += computeNextValStep(anim); - fn += setNextValAndDetectChange(name, 'nextScalarVal'); - } else if (anim.type === 'identity') { - fn += computeNextValIdentity(anim); - fn += setNextValAndDetectChange(name, 'nextScalarVal'); - } +var computeTransform = function(anim, name, value, result, + didChange, didMatrix) { + var transform = result.transform !== undefined ? + result.transform : (result.transform = [{ matrix: [] }]); + var mat = transform[0].matrix; + var m0 = mat[0]; + var m1 = mat[1]; + var m2 = mat[2]; + var m3 = mat[3]; + var m4 = mat[4]; + var m5 = mat[5]; + var m6 = mat[6]; + var m7 = mat[7]; + var m8 = mat[8]; + var m9 = mat[9]; + var m10 = mat[10]; + var m11 = mat[11]; + var m12 = mat[12]; + var m13 = mat[13]; + var m14 = mat[14]; + var m15 = mat[15]; + if (!didMatrix) { + initIdentity(mat); // This will be the first transform. } - if (orderedMatrixOperations.length) { - fn += newlines(setNextMatrixAndDetectChange(orderedMatrixOperations)); + var x = computeNextMatrixOperationField(anim, name, X_DIM, 0, value); + var y = computeNextMatrixOperationField(anim, name, Y_DIM, 1, value); + var z = computeNextMatrixOperationField(anim, name, Z_DIM, 2, value); + InterpolateMatrix[name](mat, x, y, z); + if (!didChange) { + didChange = m0 !== mat[0] || m1 !== mat[1] || + m2 !== mat[2] || m3 !== mat[3] || + m4 !== mat[4] || m5 !== mat[5] || + m6 !== mat[6] || m7 !== mat[7] || + m8 !== mat[8] || m9 !== mat[9] || + m10 !== mat[10] || m11 !== mat[11] || + m12 !== mat[12] || m13 !== mat[13] || + m14 !== mat[14] || m15 !== mat[15]; } - fn += ' return didChange;\n'; - fn += '};\n'; - fn += '})()'; - return fn; + return didChange; }; /** @@ -558,15 +148,34 @@ var createFunctionString = function(anims) { * object and returns a boolean describing if any update was actually applied. */ var buildStyleInterpolator = function(anims) { - // Defer compiling this method until we really need it. - var interpolator = null; - function lazyStyleInterpolator(result, value) { - if (interpolator === null) { - interpolator = Function(createFunctionString(anims))(); + function styleInterpolator(result, value) { + var didChange = false; + var didMatrix = false; + for (var name in anims) { + var anim = anims[name]; + if (anim.type === 'linear') { + if (name in InterpolateMatrix) { + didChange = computeTransform(anim, name, value, result, + didChange, didMatrix); + didMatrix = true; + } else { + var next = computeNextValLinearScalar(anim, value); + didChange = setNextValAndDetectChange(result, name, next, didChange); + } + } else if (anim.type === 'constant') { + var next = anim.value; + didChange = setNextValAndDetectChange(result, name, next, didChange); + } else if (anim.type === 'step') { + var next = value >= anim.threshold ? anim.to : anim.from; + didChange = setNextValAndDetectChange(result, name, next, didChange); + } else if (anim.type === 'identity') { + var next = value; + didChange = setNextValAndDetectChange(result, name, next, didChange); + } } - return interpolator(result, value); + return didChange; } - return lazyStyleInterpolator; + return styleInterpolator; }; module.exports = buildStyleInterpolator; diff --git a/Libraries/Utilities/createStrictShapeTypeChecker.js b/Libraries/Utilities/createStrictShapeTypeChecker.js index 884e8a96b9413d..078d103a3e2cbe 100644 --- a/Libraries/Utilities/createStrictShapeTypeChecker.js +++ b/Libraries/Utilities/createStrictShapeTypeChecker.js @@ -47,8 +47,8 @@ function createStrictShapeTypeChecker( invariant( false, `Invalid props.${propName} key \`${key}\` supplied to \`${componentName}\`.` + - `\nBad object: ` + JSON.stringify(props[propName], null, ' ') + - `\nValid keys: ` + JSON.stringify(Object.keys(shapeTypes), null, ' ') + '\nBad object: ' + JSON.stringify(props[propName], null, ' ') + + '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ') ); } var error = checker(propValue, key, componentName, location, ...rest); @@ -56,7 +56,7 @@ function createStrictShapeTypeChecker( invariant( false, error.message + - `\nBad object: ` + JSON.stringify(props[propName], null, ' ') + '\nBad object: ' + JSON.stringify(props[propName], null, ' ') ); } } diff --git a/Libraries/Vibration/Vibration.js b/Libraries/Vibration/Vibration.js index 699f8da25de906..2f5d7d61086972 100644 --- a/Libraries/Vibration/Vibration.js +++ b/Libraries/Vibration/Vibration.js @@ -90,7 +90,7 @@ function vibrateScheduler(id, pattern: Array, repeat: boolean, nextIndex return; } } - setTimeout(() => vibrateScheduler(id, pattern, repeat, nextIndex+1), pattern[nextIndex]); + setTimeout(() => vibrateScheduler(id, pattern, repeat, nextIndex + 1), pattern[nextIndex]); } var Vibration = { diff --git a/Libraries/WebSocket/RCTWebSocket.xcodeproj/project.pbxproj b/Libraries/WebSocket/RCTWebSocket.xcodeproj/project.pbxproj index d2d5a80f023890..8121ed0c108179 100644 --- a/Libraries/WebSocket/RCTWebSocket.xcodeproj/project.pbxproj +++ b/Libraries/WebSocket/RCTWebSocket.xcodeproj/project.pbxproj @@ -12,11 +12,9 @@ 13526A521F362F7F0008EF00 /* libfishhook.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13526A511F362F7F0008EF00 /* libfishhook.a */; }; 2D3B5F3D1D9B165B00451313 /* RCTSRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */; }; 2D3B5F3E1D9B165B00451313 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */; }; - 2D3B5F3F1D9B165B00451313 /* RCTWebSocketObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB9106E1C74B1ED00838BBE /* RCTWebSocketObserver.m */; }; 2D3B5F401D9B165B00451313 /* RCTWebSocketModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */; }; 2DC5E5281F3A6CFD000EE84B /* libfishhook-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DC5E5271F3A6CFD000EE84B /* libfishhook-tvOS.a */; }; 3C86DF7C1ADF695F0047B81A /* RCTWebSocketModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */; }; - 3DB9106F1C74B1ED00838BBE /* RCTWebSocketObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB9106E1C74B1ED00838BBE /* RCTWebSocketObserver.m */; }; 3DBE0D141F3B185A0099AA32 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DBE0D121F3B185A0099AA32 /* fishhook.c */; }; 3DBE0D151F3B185A0099AA32 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DBE0D121F3B185A0099AA32 /* fishhook.c */; }; 3DBE0D801F3B1AF00099AA32 /* fishhook.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DBE0D131F3B185A0099AA32 /* fishhook.h */; }; @@ -76,8 +74,6 @@ 3C86DF461ADF2C930047B81A /* libRCTWebSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWebSocket.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3C86DF7A1ADF695F0047B81A /* RCTWebSocketModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketModule.h; sourceTree = ""; }; 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketModule.m; sourceTree = ""; tabWidth = 2; }; - 3DB9106D1C74B1ED00838BBE /* RCTWebSocketObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketObserver.h; sourceTree = ""; }; - 3DB9106E1C74B1ED00838BBE /* RCTWebSocketObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketObserver.m; sourceTree = ""; }; 3DBE0D001F3B181A0099AA32 /* libfishhook.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libfishhook.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3DBE0D0D1F3B181C0099AA32 /* libfishhook-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libfishhook-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3DBE0D121F3B185A0099AA32 /* fishhook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fishhook.c; path = ../fishhook/fishhook.c; sourceTree = ""; }; @@ -126,8 +122,6 @@ 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */, 1338BBDE1B04ACC80064A9C9 /* RCTWebSocketExecutor.h */, 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */, - 3DB9106D1C74B1ED00838BBE /* RCTWebSocketObserver.h */, - 3DB9106E1C74B1ED00838BBE /* RCTWebSocketObserver.m */, 3C86DF7A1ADF695F0047B81A /* RCTWebSocketModule.h */, 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */, 3C86DF471ADF2C930047B81A /* Products */, @@ -265,7 +259,6 @@ 2D3B5F401D9B165B00451313 /* RCTWebSocketModule.m in Sources */, A12E9E2F1E5DEC550029001B /* RCTReconnectingWebSocket.m in Sources */, 2D3B5F3D1D9B165B00451313 /* RCTSRWebSocket.m in Sources */, - 2D3B5F3F1D9B165B00451313 /* RCTWebSocketObserver.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -277,7 +270,6 @@ 3C86DF7C1ADF695F0047B81A /* RCTWebSocketModule.m in Sources */, A12E9E2E1E5DEC4E0029001B /* RCTReconnectingWebSocket.m in Sources */, 1338BBE11B04ACC80064A9C9 /* RCTWebSocketExecutor.m in Sources */, - 3DB9106F1C74B1ED00838BBE /* RCTWebSocketObserver.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Libraries/WebSocket/RCTWebSocketObserver.h b/Libraries/WebSocket/RCTWebSocketObserver.h deleted file mode 100644 index e20c232d9aeb43..00000000000000 --- a/Libraries/WebSocket/RCTWebSocketObserver.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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. - */ - -#import - -#if RCT_DEV // Only supported in dev mode - -@protocol RCTWebSocketObserverDelegate - -- (void)didReceiveWebSocketMessage:(NSDictionary *)message; - -@end - -@interface RCTWebSocketObserver : NSObject - -- (instancetype)initWithURL:(NSURL *)url; -- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; - -@property (nonatomic, weak) id delegate; - -- (void)start; -- (void)stop; - -@end - -#endif diff --git a/Libraries/WebSocket/RCTWebSocketObserver.m b/Libraries/WebSocket/RCTWebSocketObserver.m deleted file mode 100644 index d446d5d88f6dc7..00000000000000 --- a/Libraries/WebSocket/RCTWebSocketObserver.m +++ /dev/null @@ -1,79 +0,0 @@ -/** - * 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. - */ - -#import "RCTWebSocketObserver.h" - -#import -#import -#import -#import - -#import "RCTReconnectingWebSocket.h" - -#if RCT_DEV // Only supported in dev mode - -@interface RCTWebSocketObserver () -@end - -@implementation RCTWebSocketObserver { - RCTReconnectingWebSocket *_socket; -} - -@synthesize delegate = _delegate; - -- (instancetype)initWithURL:(NSURL *)url -{ - if (self = [super init]) { - _socket = [[RCTReconnectingWebSocket alloc] initWithURL:url]; - _socket.delegate = self; - } - return self; -} - -- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue -{ - [_socket setDelegateDispatchQueue:queue]; -} - -- (void)start -{ - _socket.delegate = self; - [_socket start]; -} - -- (void)stop -{ - [_socket stop]; -} - -- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message -{ - if (_delegate) { - NSError *error = nil; - NSDictionary *msg = RCTJSONParse(message, &error); - - if (!error) { - [_delegate didReceiveWebSocketMessage:msg]; - } else { - RCTLogError(@"WebSocketManager failed to parse message with error %@\n\n%@\n", error, message); - } - } -} - -- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket -{ -} - -- (void)webSocket:(RCTSRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean -{ -} - -@end - -#endif diff --git a/Libraries/Wrapper/Example/RCTWrapperExampleView.h b/Libraries/Wrapper/Example/RCTWrapperExampleView.h new file mode 100644 index 00000000000000..e169368c0fb67c --- /dev/null +++ b/Libraries/Wrapper/Example/RCTWrapperExampleView.h @@ -0,0 +1,11 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTWrapperExampleView : UIView + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Wrapper/Example/RCTWrapperExampleView.m b/Libraries/Wrapper/Example/RCTWrapperExampleView.m new file mode 100644 index 00000000000000..05494750c96d8e --- /dev/null +++ b/Libraries/Wrapper/Example/RCTWrapperExampleView.m @@ -0,0 +1,55 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTWrapperExampleView.h" + +#import + +@implementation RCTWrapperExampleView { + NSTimer *_timer; + CGSize _intrinsicContentSize; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + self.backgroundColor = [UIColor whiteColor]; + + _intrinsicContentSize = CGSizeMake(64, 64); + _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 + target:self + selector:@selector(tick) + userInfo:nil + repeats:YES]; + + UITapGestureRecognizer *gestureRecognizer = + [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tick)]; + [self addGestureRecognizer:gestureRecognizer]; + } + return self; +} + +- (void)tick +{ + _intrinsicContentSize.width = 32 + arc4random() % 128; + _intrinsicContentSize.height = 32 + arc4random() % 128; + + [self invalidateIntrinsicContentSize]; + [self.superview setNeedsLayout]; +} + +- (CGSize)intrinsicContentSize +{ + return _intrinsicContentSize; +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + return CGSizeMake( + MIN(size.width, _intrinsicContentSize.width), + MIN(size.height, _intrinsicContentSize.height) + ); +} + +@end + +RCT_WRAPPER_FOR_VIEW(RCTWrapperExampleView) diff --git a/Libraries/Wrapper/Example/RCTWrapperExampleViewController.h b/Libraries/Wrapper/Example/RCTWrapperExampleViewController.h new file mode 100644 index 00000000000000..2778090a5b68c3 --- /dev/null +++ b/Libraries/Wrapper/Example/RCTWrapperExampleViewController.h @@ -0,0 +1,11 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTWrapperExampleViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Wrapper/Example/RCTWrapperExampleViewController.m b/Libraries/Wrapper/Example/RCTWrapperExampleViewController.m new file mode 100644 index 00000000000000..0d5473600e7f6f --- /dev/null +++ b/Libraries/Wrapper/Example/RCTWrapperExampleViewController.m @@ -0,0 +1,17 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTWrapperExampleViewController.h" + +#import + +#import "RCTWrapperExampleView.h" + +@implementation RCTWrapperExampleViewController + +- (void)loadView { + self.view = [RCTWrapperExampleView new]; +} + +@end + +RCT_WRAPPER_FOR_VIEW_CONTROLLER(RCTWrapperExampleViewController) diff --git a/Libraries/Wrapper/Example/RCTWrapperReactRootViewController.h b/Libraries/Wrapper/Example/RCTWrapperReactRootViewController.h new file mode 100644 index 00000000000000..8b915dcafad47d --- /dev/null +++ b/Libraries/Wrapper/Example/RCTWrapperReactRootViewController.h @@ -0,0 +1,15 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +@class RCTBridge; + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTWrapperReactRootViewController : UIViewController + +- (instancetype)initWithBridge:(RCTBridge *)bridge; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Wrapper/Example/RCTWrapperReactRootViewController.m b/Libraries/Wrapper/Example/RCTWrapperReactRootViewController.m new file mode 100644 index 00000000000000..acdef85cf94314 --- /dev/null +++ b/Libraries/Wrapper/Example/RCTWrapperReactRootViewController.m @@ -0,0 +1,42 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTWrapperReactRootViewController.h" + +#import +#import +#import + +#import "RCTWrapperExampleView.h" + +@implementation RCTWrapperReactRootViewController { + RCTBridge *_bridge; +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge +{ + if (self = [super initWithNibName:nil bundle:nil]) { + _bridge = bridge; + } + + return self; +} + +- (void)loadView +{ + RCTRootView *rootView = + [[RCTRootView alloc] initWithBridge:_bridge + moduleName:@"WrapperExample" + initialProperties:@{}]; + + rootView.backgroundColor = [UIColor whiteColor]; + + UIActivityIndicatorView *progressIndicatorView = + [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + [progressIndicatorView startAnimating]; + rootView.loadingView = progressIndicatorView; + + rootView.sizeFlexibility = RCTRootViewSizeFlexibilityWidthAndHeight; + self.view = rootView; +} + +@end diff --git a/Libraries/Wrapper/Example/RCTWrapperReactRootViewManager.h b/Libraries/Wrapper/Example/RCTWrapperReactRootViewManager.h new file mode 100644 index 00000000000000..1a6fd506809505 --- /dev/null +++ b/Libraries/Wrapper/Example/RCTWrapperReactRootViewManager.h @@ -0,0 +1,13 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTWrapperReactRootViewManager : RCTWrapperViewManager + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Wrapper/Example/RCTWrapperReactRootViewManager.m b/Libraries/Wrapper/Example/RCTWrapperReactRootViewManager.m new file mode 100644 index 00000000000000..6150453881303d --- /dev/null +++ b/Libraries/Wrapper/Example/RCTWrapperReactRootViewManager.m @@ -0,0 +1,27 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTWrapperReactRootViewManager.h" + +#import +#import + +#import "RCTWrapperReactRootViewController.h" + +@implementation RCTWrapperReactRootViewManager + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + RCTWrapperViewControllerHostingView *contentViewControllerHostingView = + [RCTWrapperViewControllerHostingView new]; + + contentViewControllerHostingView.contentViewController = + [[RCTWrapperReactRootViewController alloc] initWithBridge:self.bridge]; + + RCTWrapperView *wrapperView = [super view]; + wrapperView.contentView = contentViewControllerHostingView; + return wrapperView; +} + +@end diff --git a/Libraries/Wrapper/RCTWrapper.h b/Libraries/Wrapper/RCTWrapper.h new file mode 100644 index 00000000000000..4fa389a03e4e3d --- /dev/null +++ b/Libraries/Wrapper/RCTWrapper.h @@ -0,0 +1,61 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +#import +#import +#import + +// Umbrella header with macros + +// RCT_WRAPPER_FOR_VIEW +#define RCT_WRAPPER_FOR_VIEW(ClassName) \ + \ +NS_ASSUME_NONNULL_BEGIN \ + \ +@interface ClassName##Manager : RCTWrapperViewManager \ + \ +@end \ + \ +NS_ASSUME_NONNULL_END \ + \ +@implementation ClassName##Manager \ + \ +RCT_EXPORT_MODULE() \ + \ +- (UIView *)view \ +{ \ + RCTWrapperView *wrapperView = [super view]; \ + wrapperView.contentView = [ClassName new]; \ + return wrapperView; \ +} \ + \ +@end + +// RCT_WRAPPER_FOR_VIEW_CONTROLLER +#define RCT_WRAPPER_FOR_VIEW_CONTROLLER(ClassName) \ + \ +NS_ASSUME_NONNULL_BEGIN \ + \ +@interface ClassName##Manager : RCTWrapperViewManager \ + \ +@end \ + \ +NS_ASSUME_NONNULL_END \ + \ +@implementation ClassName##Manager \ + \ +RCT_EXPORT_MODULE() \ + \ +- (UIView *)view \ +{ \ + RCTWrapperViewControllerHostingView *contentViewControllerHostingView = \ + [RCTWrapperViewControllerHostingView new]; \ + contentViewControllerHostingView.contentViewController = \ + [[ClassName alloc] initWithNibName:nil bundle:nil]; \ + RCTWrapperView *wrapperView = [super view]; \ + wrapperView.contentView = contentViewControllerHostingView; \ + return wrapperView; \ +} \ + \ +@end diff --git a/Libraries/Wrapper/RCTWrapperShadowView.h b/Libraries/Wrapper/RCTWrapperShadowView.h new file mode 100644 index 00000000000000..1a64b85d5e54c5 --- /dev/null +++ b/Libraries/Wrapper/RCTWrapperShadowView.h @@ -0,0 +1,17 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +#import + +@class RCTBridge; + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTWrapperShadowView : RCTShadowView + +- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Wrapper/RCTWrapperShadowView.m b/Libraries/Wrapper/RCTWrapperShadowView.m new file mode 100644 index 00000000000000..c619d0e4c85c8c --- /dev/null +++ b/Libraries/Wrapper/RCTWrapperShadowView.m @@ -0,0 +1,123 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTWrapperShadowView.h" + +#import +#import + +#import "RCTWrapperView.h" + +@implementation RCTWrapperShadowView +{ + __weak RCTBridge *_bridge; + RCTWrapperMeasureBlock _measureBlock; + CGSize _intrinsicContentSize; +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge +{ + if (self = [super init]) { + _bridge = bridge; + YGNodeSetMeasureFunc(self.yogaNode, RCTWrapperShadowViewMeasure); + } + + return self; +} + +static YGSize RCTWrapperShadowViewMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) +{ + CGSize minimumSize = CGSizeMake(0, 0); + CGSize maximumSize = CGSizeMake(INFINITY, INFINITY); + + switch (widthMode) { + case YGMeasureModeUndefined: + break; + case YGMeasureModeExactly: + minimumSize.width = width; + maximumSize.width = width; + break; + case YGMeasureModeAtMost: + maximumSize.width = width; + break; + } + + switch (heightMode) { + case YGMeasureModeUndefined: + break; + case YGMeasureModeExactly: + minimumSize.height = height; + maximumSize.height = height; + break; + case YGMeasureModeAtMost: + maximumSize.height = height; + break; + } + + RCTWrapperShadowView *shadowView = (__bridge RCTWrapperShadowView *)YGNodeGetContext(node); + CGSize size = [shadowView measureWithMinimumSize:minimumSize maximumSize:maximumSize]; + return (YGSize){size.width, size.height}; +} + +- (CGSize)measureWithMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize +{ + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC); + + if (!_measureBlock) { + RCTBridge *bridge = _bridge; + __block RCTWrapperMeasureBlock measureBlock; + NSNumber *reactTag = self.reactTag; + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + dispatch_async(dispatch_get_main_queue(), ^{ + RCTUIManager *uiManager = bridge.uiManager; + RCTWrapperView *view = (RCTWrapperView *)[uiManager viewForReactTag:reactTag]; + measureBlock = view.measureBlock; + + dispatch_semaphore_signal(semaphore); + }); + + if (dispatch_semaphore_wait(semaphore, timeout)) { + RCTLogError(@"Unable to retrieve `measureBlock` for view (%@) because the main thread is busy.", self); + } + + _measureBlock = measureBlock; + } + + if (!_measureBlock) { + return maximumSize; + } + + __block CGSize size = maximumSize; + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + dispatch_async(dispatch_get_main_queue(), ^{ + size = self->_measureBlock(minimumSize, maximumSize); + dispatch_semaphore_signal(semaphore); + }); + + if (dispatch_semaphore_wait(semaphore, timeout)) { + RCTLogError(@"Unable to compute layout for view (%@) because the main thread is busy.", self); + } + + return size; +} + +- (BOOL)isYogaLeafNode +{ + return YES; +} + +- (CGSize)intrinsicContentSize +{ + return _intrinsicContentSize; +} + +- (void)setIntrinsicContentSize:(CGSize)size +{ + _intrinsicContentSize = size; + YGNodeMarkDirty(self.yogaNode); +} + +@end diff --git a/Libraries/Wrapper/RCTWrapperView.h b/Libraries/Wrapper/RCTWrapperView.h new file mode 100644 index 00000000000000..0aa6dfc83fe8e9 --- /dev/null +++ b/Libraries/Wrapper/RCTWrapperView.h @@ -0,0 +1,31 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +typedef CGSize (^RCTWrapperMeasureBlock)(CGSize minimumSize, CGSize maximumSize); + +@class RCTBridge; + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTWrapperView : UIView + +@property (nonatomic, retain, nullable) UIView *contentView; +@property (nonatomic, readonly) RCTWrapperMeasureBlock measureBlock; + +- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; + +#pragma mark - Restrictions + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder *)decoder NS_UNAVAILABLE; + +- (void)addSubview:(UIView *)view NS_UNAVAILABLE; +- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index NS_UNAVAILABLE; +- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview NS_UNAVAILABLE; +- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Wrapper/RCTWrapperView.m b/Libraries/Wrapper/RCTWrapperView.m new file mode 100644 index 00000000000000..d9ba419498c6d5 --- /dev/null +++ b/Libraries/Wrapper/RCTWrapperView.m @@ -0,0 +1,93 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTWrapperView.h" + +#import +#import + +@implementation RCTWrapperView { + __weak RCTBridge *_bridge; +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge +{ + if (self = [super initWithFrame:CGRectZero]) { + _bridge = bridge; + __weak __typeof(self) weakSelf = self; + + _measureBlock = ^(CGSize minimumSize, CGSize maximumSize) { + __typeof(self) strongSelf = weakSelf; + + if (!strongSelf) { + return maximumSize; + } + + CGSize size = [strongSelf sizeThatFits:maximumSize]; + + return CGSizeMake( + MAX(size.width, minimumSize.width), + MAX(size.height, minimumSize.height) + ); + }; + } + + return self; +} + +#pragma mark - `contentView` + +- (nullable UIView *)contentView +{ + return self.subviews.firstObject; +} + +- (void)setContentView:(UIView *)contentView +{ + while (self.subviews.firstObject) { + [self.subviews.firstObject removeFromSuperview]; + } + + if (!contentView) { + return; + } + + [super addSubview:contentView]; + + contentView.frame = self.bounds; + contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + contentView.translatesAutoresizingMaskIntoConstraints = YES; +} + +#pragma mark - Layout + +- (void)setNeedsLayout +{ + [super setNeedsLayout]; + [self invalidateIntrinsicContentSize]; +} + +- (void)invalidateIntrinsicContentSize +{ + [super invalidateIntrinsicContentSize]; + + // Setting `intrinsicContentSize` dirties the Yoga node and + // enfoce Yoga to call `measure` function (backed to `measureBlock`). + [_bridge.uiManager setIntrinsicContentSize:self.intrinsicContentSize forView:self]; +} + +- (CGSize)intrinsicContentSize +{ + return [self sizeThatFits:CGSizeMake(INFINITY, INFINITY)]; +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + UIView *contentView = self.contentView; + if (!contentView) { + return size; + } + + return [contentView sizeThatFits:size]; +} + +@end diff --git a/Libraries/Wrapper/RCTWrapperViewControllerHostingView.h b/Libraries/Wrapper/RCTWrapperViewControllerHostingView.h new file mode 100644 index 00000000000000..cf85e8cab9f62e --- /dev/null +++ b/Libraries/Wrapper/RCTWrapperViewControllerHostingView.h @@ -0,0 +1,20 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTWrapperViewControllerHostingView : UIView + +@property (nonatomic, retain, nullable) UIViewController *contentViewController; + +#pragma mark - Restrictions + +- (void)addSubview:(UIView *)view NS_UNAVAILABLE; +- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index NS_UNAVAILABLE; +- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview NS_UNAVAILABLE; +- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Wrapper/RCTWrapperViewControllerHostingView.m b/Libraries/Wrapper/RCTWrapperViewControllerHostingView.m new file mode 100644 index 00000000000000..8503ee88e5e28d --- /dev/null +++ b/Libraries/Wrapper/RCTWrapperViewControllerHostingView.m @@ -0,0 +1,129 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTWrapperViewControllerHostingView.h" + +#import + +#pragma mark - UIViewController+Children + +@interface UIViewController (Children) + +@property (nonatomic, readonly) BOOL isAttached; +- (void)attachChildViewController:(UIViewController *)childViewController toContainerView:(UIView *)containerView; +- (void)detachChildViewController:(UIViewController *)childViewController; + +@end + +@implementation UIViewController (Children) + +- (BOOL)isAttached +{ + return self.parentViewController != nil; +} + +- (void)attachChildViewController:(UIViewController *)childViewController toContainerView:(UIView *)containerView +{ + [self addChildViewController:childViewController]; + // `[childViewController willMoveToParentViewController:self]` is calling automatically + [containerView addSubview:childViewController.view]; + childViewController.view.frame = containerView.bounds; + childViewController.view.translatesAutoresizingMaskIntoConstraints = YES; + childViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [childViewController didMoveToParentViewController:self]; + + [childViewController beginAppearanceTransition:true animated:false]; + [childViewController endAppearanceTransition]; +} + +- (void)detachChildViewController:(UIViewController *)childViewController +{ + [childViewController beginAppearanceTransition:false animated: false]; + [childViewController endAppearanceTransition]; + + [childViewController willMoveToParentViewController:nil]; + [childViewController.view removeFromSuperview]; + [childViewController removeFromParentViewController]; + // `[childViewController didMoveToParentViewController:nil]` is calling automatically +} + +@end + +@implementation RCTWrapperViewControllerHostingView { + UIViewController *_Nullable _contentViewController; +} + +#pragma mark - `contentViewController` + +- (nullable UIViewController *)contentViewController +{ + return _contentViewController; +} + +- (void)setContentViewController:(UIViewController *)contentViewController +{ + + if (_contentViewController) { + [self detachContentViewControllerIfNeeded]; + } + + _contentViewController = contentViewController; + + if (_contentViewController) { + [self attachContentViewControllerIfNeeded]; + } +} + +#pragma mark - Attaching and Detaching + +- (void)attachContentViewControllerIfNeeded +{ + if (self.contentViewController.isAttached) { + return; + } + + [self.reactViewController attachChildViewController:self.contentViewController toContainerView:self]; +} + +- (void)detachContentViewControllerIfNeeded +{ + if (!self.contentViewController.isAttached) { + return; + } + + [self.reactViewController detachChildViewController:self.contentViewController]; +} + +#pragma mark - Life cycle + +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + if (newWindow == nil) { + [self detachContentViewControllerIfNeeded]; + } +} + +- (void)didMoveToWindow +{ + [super didMoveToWindow]; + [self attachContentViewControllerIfNeeded]; +} + +#pragma mark - Layout + +- (void)setNeedsLayout +{ + [super setNeedsLayout]; + [self.superview setNeedsLayout]; +} + +- (CGSize)intrinsicContentSize +{ + return self.contentViewController.view.intrinsicContentSize; +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + return [self.contentViewController.view sizeThatFits:size]; +} + +@end diff --git a/Libraries/Wrapper/RCTWrapperViewManager.h b/Libraries/Wrapper/RCTWrapperViewManager.h new file mode 100644 index 00000000000000..bdd5d9f40c7be9 --- /dev/null +++ b/Libraries/Wrapper/RCTWrapperViewManager.h @@ -0,0 +1,15 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +@class RCTWrapperView; + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTWrapperViewManager : RCTViewManager + +- (RCTWrapperView *)view NS_REQUIRES_SUPER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Libraries/Wrapper/RCTWrapperViewManager.m b/Libraries/Wrapper/RCTWrapperViewManager.m new file mode 100644 index 00000000000000..687c7a46cf3ab8 --- /dev/null +++ b/Libraries/Wrapper/RCTWrapperViewManager.m @@ -0,0 +1,22 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTWrapperViewManager.h" + +#import "RCTWrapperShadowView.h" +#import "RCTWrapperView.h" + +@implementation RCTWrapperViewManager + +RCT_EXPORT_MODULE() + +- (RCTShadowView *)shadowView +{ + return [[RCTWrapperShadowView alloc] initWithBridge:self.bridge]; +} + +- (UIView *)view +{ + return [[RCTWrapperView alloc] initWithBridge:self.bridge]; +} + +@end diff --git a/README.md b/README.md index af445488d2653d..b8967092b32dc4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [React Native](https://facebook.github.io/react-native/) · [![Travis CI Status](https://travis-ci.org/facebook/react-native.svg?branch=master)](https://travis-ci.org/facebook/react-native) [![Circle CI Status](https://circleci.com/gh/facebook/react-native.svg?style=shield)](https://circleci.com/gh/facebook/react-native) [![npm version](https://badge.fury.io/js/react-native.svg)](https://badge.fury.io/js/react-native) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md#pull-requests) +# [React Native](https://facebook.github.io/react-native/) · [![Circle CI Status](https://circleci.com/gh/facebook/react-native.svg?style=shield)](https://circleci.com/gh/facebook/react-native) [![npm version](https://badge.fury.io/js/react-native.svg)](https://badge.fury.io/js/react-native) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md#pull-requests) Learn once, write anywhere: Build mobile apps with React. @@ -62,6 +62,6 @@ We have a list of [beginner friendly issues](https://github.com/facebook/react-n ## License -React is [BSD licensed](./LICENSE). We also provide an additional [patent grant](./PATENTS). +React Native is [BSD licensed](./LICENSE). We also provide an additional [patent grant](./PATENTS). -React documentation is [Creative Commons licensed](./LICENSE-docs). +React Native documentation is [Creative Commons licensed](./LICENSE-docs). diff --git a/RNTester/RNTesterUnitTests/RCTImageLoaderTests.m b/RNTester/RNTesterUnitTests/RCTImageLoaderTests.m index 1fe160809d7b29..59e8b30080c899 100644 --- a/RNTester/RNTesterUnitTests/RCTImageLoaderTests.m +++ b/RNTester/RNTesterUnitTests/RCTImageLoaderTests.m @@ -54,7 +54,7 @@ - (void)testImageLoading NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:^{ return @[loader]; } launchOptions:nil]; - NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://facebook.github.io/react/img/logo_og.png"]]; + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://facebook.github.io/react/logo-og.png"]]; [bridge.imageLoader loadImageWithURLRequest:urlRequest size:CGSizeMake(100, 100) scale:1.0 clipped:YES resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { XCTAssertEqual(progress, 1); XCTAssertEqual(total, 1); @@ -85,7 +85,7 @@ - (void)testImageLoaderUsesImageURLLoaderWithHighestPriority NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL moduleProvider:^{ return @[loader1, loader2]; } launchOptions:nil]; - NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://facebook.github.io/react/img/logo_og.png"]]; + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://facebook.github.io/react/logo-og.png"]]; [bridge.imageLoader loadImageWithURLRequest:urlRequest size:CGSizeMake(100, 100) scale:1.0 clipped:YES resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) { XCTAssertEqual(progress, 1); XCTAssertEqual(total, 1); diff --git a/RNTester/js/BorderExample.js b/RNTester/js/BorderExample.js index 1dea4656dcf305..3bb5de8fce78ca 100644 --- a/RNTester/js/BorderExample.js +++ b/RNTester/js/BorderExample.js @@ -124,6 +124,44 @@ var styles = StyleSheet.create({ borderBottomColor: 'transparent', borderLeftColor: 'red', }, + border12: { + borderStyle: 'solid', + overflow: 'hidden', + borderTopWidth: 10, + borderRightWidth: 20, + borderBottomWidth: 30, + borderLeftWidth: 40, + borderRadius: 20, + }, + border13: { + borderStyle: 'solid', + overflow: 'hidden', + borderTopWidth: 10, + borderRightWidth: 20, + borderBottomWidth: 30, + borderLeftWidth: 40, + borderTopColor: 'red', + borderRightColor: 'green', + borderBottomColor: 'blue', + borderLeftColor: 'magenta', + borderRadius: 20, + }, + border14: { + borderStyle: 'solid', + overflow: 'hidden', + borderTopWidth: 10, + borderRightWidth: 20, + borderBottomWidth: 30, + borderLeftWidth: 40, + borderTopColor: 'red', + borderRightColor: 'green', + borderBottomColor: 'blue', + borderLeftColor: 'magenta', + borderTopLeftRadius: 10, + borderTopRightRadius: 40, + borderBottomRightRadius: 30, + borderBottomLeftRadius: 40, + } }); exports.title = 'Border'; @@ -228,4 +266,25 @@ exports.examples = [ return ; } }, + { + title: 'Curved border(Left|Right|Bottom|Top)Width', + description: 'Make a non-uniform width curved border', + render() { + return ; + } + }, + { + title: 'Curved border(Left|Right|Bottom|Top)Color', + description: 'Make a non-uniform color curved border', + render() { + return ; + } + }, + { + title: 'Curved border(Top|Bottom)(Left|Right)Radius', + description: 'Make a non-uniform radius curved border', + render() { + return ; + } + } ]; diff --git a/RNTester/js/ImageExample.js b/RNTester/js/ImageExample.js index c4d83012725575..8d858777611f31 100644 --- a/RNTester/js/ImageExample.js +++ b/RNTester/js/ImageExample.js @@ -197,7 +197,7 @@ var MultipleSourcesExample = createReactClass({ source={[ {uri: 'https://facebook.github.io/react/img/logo_small.png', width: 38, height: 38}, {uri: 'https://facebook.github.io/react/img/logo_small_2x.png', width: 76, height: 76}, - {uri: 'https://facebook.github.io/react/img/logo_og.png', width: 400, height: 400} + {uri: 'https://facebook.github.io/react/logo-og.png', width: 400, height: 400} ]} /> @@ -237,7 +237,7 @@ exports.examples = [ render: function() { return ( ); @@ -271,7 +271,7 @@ exports.examples = [ title: 'Error Handler', render: function() { return ( - + ); }, platform: 'ios', @@ -727,7 +727,7 @@ exports.examples = [ }, ]; -var fullImage = {uri: 'https://facebook.github.io/react/img/logo_og.png'}; +var fullImage = {uri: 'https://facebook.github.io/react/logo-og.png'}; var smallImage = {uri: 'https://facebook.github.io/react/img/logo_small_2x.png'}; var styles = StyleSheet.create({ diff --git a/RNTester/js/RTLExample.js b/RNTester/js/RTLExample.js index cde83d0908d2a4..c7cf4930b454b5 100644 --- a/RNTester/js/RTLExample.js +++ b/RNTester/js/RTLExample.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format * @providesModule RTLExample */ 'use strict'; @@ -26,10 +27,10 @@ const { TouchableWithoutFeedback, Switch, View, + Button, } = ReactNative; const Platform = require('Platform'); - const RNTesterPage = require('./RNTesterPage'); const RNTesterBlock = require('./RNTesterBlock'); @@ -48,57 +49,46 @@ const IMAGE_SIZE = [IMAGE_DIMENSION, IMAGE_DIMENSION]; const IS_RTL = I18nManager.isRTL; function ListItem(props) { - return ( + return ( - - - - - - - Text - Text - Text - - - - - - - Button - - - - - ); + + + + + + Text Text Text + + + + + Button + + + + ); } function TextAlignmentExample(props) { return ( - - - - Left-to-Right language without text alignment. - - - {'\u0645\u0646 \u0627\u0644\u064A\u0645\u064A\u0646 ' + - '\u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631 ' + - '\u0627\u0644\u0644\u063A\u0629 \u062F\u0648\u0646 ' + - '\u0645\u062D\u0627\u0630\u0627\u0629 \u0627\u0644\u0646\u0635'} - - - {'\u05DE\u05D9\u05DE\u05D9\u05DF \u05DC\u05E9\u05DE\u05D0\u05DC ' + - '\u05D4\u05E9\u05E4\u05D4 \u05D1\u05DC\u05D9 ' + - '\u05D9\u05D9\u05E9\u05D5\u05E8 \u05D8\u05E7\u05E1\u05D8'} - - - - ); + + + + Left-to-Right language without text alignment. + + + {'\u0645\u0646 \u0627\u0644\u064A\u0645\u064A\u0646 ' + + '\u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631 ' + + '\u0627\u0644\u0644\u063A\u0629 \u062F\u0648\u0646 ' + + '\u0645\u062D\u0627\u0630\u0627\u0629 \u0627\u0644\u0646\u0635'} + + + {'\u05DE\u05D9\u05DE\u05D9\u05DF \u05DC\u05E9\u05DE\u05D0\u05DC ' + + '\u05D4\u05E9\u05E4\u05D4 \u05D1\u05DC\u05D9 ' + + '\u05D9\u05D9\u05E9\u05D5\u05E8 \u05D8\u05E7\u05E1\u05D8'} + + + + ); } function AnimationBlock(props) { @@ -114,6 +104,276 @@ function AnimationBlock(props) { ); } +type RTLSwitcherComponentState = {| + isRTL: boolean, +|}; + +function withRTLState(Component) { + return class extends React.Component<*, RTLSwitcherComponentState> { + constructor(...args) { + super(...args); + this.state = { + isRTL: IS_RTL, + }; + } + + render() { + const setRTL = isRTL => this.setState({isRTL: isRTL}); + return ; + } + }; +} + +const RTLToggler = ({isRTL, setRTL}) => { + if (Platform.OS === 'android') { + return {isRTL ? 'RTL' : 'LTR'}; + } + + const toggleRTL = () => setRTL(!isRTL); + return ( +