mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
984cbb3524 | ||
|
|
3a5572e0e0 | ||
|
|
62695a1351 | ||
|
|
5f3f3d463a | ||
|
|
ec2f97ac1a | ||
|
|
43d1cf8fd1 | ||
|
|
9faf324d30 | ||
|
|
8e0d54d38b | ||
|
|
2d88b63747 | ||
|
|
d93de22ad9 | ||
|
|
f3cbd4f159 | ||
|
|
a7fdca038f | ||
|
|
42879d21e8 | ||
|
|
d0bb66fbf7 | ||
|
|
c85aa82cd0 | ||
|
|
91509751ef | ||
|
|
87e524b4a4 | ||
|
|
6817f05f63 | ||
|
|
7f4fe158a2 |
+164
-107
@@ -1,35 +1,52 @@
|
||||
aliases:
|
||||
- &restore-node-cache
|
||||
keys:
|
||||
- v1-dependencies-{{ arch }}-{{ checksum "package.json" }}
|
||||
- v1-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
# Fallback in case checksum fails
|
||||
- v1-dependencies-{{ arch }}-
|
||||
- v1-dependencies-{{ arch }}-{{ .Branch }}-
|
||||
|
||||
- &save-node-cache
|
||||
paths:
|
||||
- node_modules
|
||||
key: v1-dependencies-{{ arch }}-{{ checksum "package.json" }}
|
||||
key: v1-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
|
||||
- &restore-cache-website
|
||||
keys:
|
||||
- v1-website-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "website/package.json" }}
|
||||
# Fallback in case checksum fails
|
||||
- v1-website-dependencies-{{ arch }}-{{ .Branch }}-
|
||||
|
||||
- &save-cache-website
|
||||
paths:
|
||||
- website/node_modules
|
||||
key: v1-website-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "website/package.json" }}
|
||||
|
||||
- &restore-cache-analysis
|
||||
keys:
|
||||
- v1-analysis-dependencies-{{ arch }}-{{ checksum "package.json" }}{{ checksum "danger/package.json" }}
|
||||
- v1-analysis-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }}{{ checksum "danger/package.json" }}
|
||||
# Fallback in case checksum fails
|
||||
- v1-analysis-dependencies-{{ arch }}-
|
||||
- v1-analysis-dependencies-{{ arch }}-{{ .Branch }}-
|
||||
- &save-cache-analysis
|
||||
paths:
|
||||
- danger/node_modules
|
||||
- node_modules
|
||||
key: v1-analysis-dependencies-{{ arch }}-{{ checksum "package.json" }}{{ checksum "danger/package.json" }}
|
||||
key: v1-analysis-dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }}{{ checksum "danger/package.json" }}
|
||||
|
||||
- &restore-cache-android-packages
|
||||
keys:
|
||||
- v2-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }}
|
||||
- v1-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }}
|
||||
# Fallback in case checksum fails
|
||||
- v2-android-sdkmanager-packages-{{ arch }}-
|
||||
- v1-android-sdkmanager-packages-{{ arch }}-
|
||||
- &save-cache-android-packages
|
||||
paths:
|
||||
- /opt/android/sdk
|
||||
key: v2-android-sdkmanager-packages-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }}
|
||||
- /opt/android/sdk/system-images/android-23
|
||||
- /opt/android/sdk/system-images/android-19
|
||||
- /opt/android/sdk/platforms/android-26
|
||||
- /opt/android/sdk/platforms/android-23
|
||||
- /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-{{ arch }}-{{ checksum "scripts/circle-ci-android-setup.sh" }}
|
||||
|
||||
- &restore-cache-ndk
|
||||
keys:
|
||||
@@ -44,13 +61,23 @@ aliases:
|
||||
- /opt/ndk
|
||||
key: v1-android-ndk-{{ arch }}-r10e-32-64
|
||||
|
||||
- &restore-cache-buck-downloads
|
||||
keys:
|
||||
- v1-buck-downloads-{{ arch }}-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }}
|
||||
# Fallback in case checksum fails
|
||||
- v1-buck-downloads-{{ arch }}-{{ .Branch }}-
|
||||
- &save-cache-buck-downloads
|
||||
paths:
|
||||
- "ReactAndroid/build/downloads"
|
||||
key: v1-buck-downloads-{{ arch }}-{{ .Branch }}-{{ checksum "ReactAndroid/build.gradle" }}
|
||||
|
||||
- &restore-cache-buck
|
||||
keys:
|
||||
- v2-buck-{{ arch }}-v2017.09.04.02
|
||||
- v1-buck-{{ arch }}-v2017.09.04.02
|
||||
- &save-cache-buck
|
||||
paths:
|
||||
- ~/buck
|
||||
key: v2-buck-{{ arch }}-v2017.09.04.02
|
||||
key: v1-buck-{{ arch }}-v2017.09.04.02
|
||||
|
||||
- &restore-cache-watchman
|
||||
keys:
|
||||
@@ -80,16 +107,9 @@ aliases:
|
||||
- &run-node-tests
|
||||
|
|
||||
npm test -- --maxWorkers=2
|
||||
|
||||
- &run-lint-checks
|
||||
|
|
||||
npm run lint
|
||||
|
||||
- &run-flow-checks
|
||||
|
|
||||
npm run flow -- check
|
||||
|
||||
|
||||
- &filter-only-master-stable
|
||||
branches:
|
||||
only:
|
||||
@@ -135,45 +155,14 @@ aliases:
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install ant autoconf automake g++ gcc libqt5widgets5 lib32z1 lib32stdc++6 make maven python-dev python3-dev qml-module-qtquick-controls qtdeclarative5-dev file -y
|
||||
|
||||
- &build-android-app
|
||||
name: Build Android App
|
||||
command: |
|
||||
buck build ReactAndroid/src/main/java/com/facebook/react
|
||||
buck build ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
|
||||
- &wait-for-avd
|
||||
name: Wait for Android Virtual Device
|
||||
command: source scripts/circle-ci-android-setup.sh && waitForAVD
|
||||
|
||||
- &check-js-bundle
|
||||
name: Check for JavaScript Bundle
|
||||
command: |
|
||||
if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then
|
||||
echo "JavaScript bundle missing, verify build-js-bundle step"; exit 1;
|
||||
else
|
||||
echo "JavaScript bundle found.";
|
||||
fi
|
||||
|
||||
- &compile-native-libs
|
||||
name: Compile Native Libs for Unit and Integration Tests
|
||||
command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS -Pcom.android.build.threadPoolSize=1
|
||||
no_output_timeout: 6m
|
||||
|
||||
- &run-android-unit-tests
|
||||
name: Unit Tests
|
||||
command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS
|
||||
|
||||
- &run-android-integration-tests
|
||||
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
|
||||
|
||||
- &collect-android-test-results
|
||||
name: Collect Test Results
|
||||
command: |
|
||||
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/ \;
|
||||
when: always
|
||||
- &install-android-app-dependencies
|
||||
|
|
||||
buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
|
||||
buck fetch ReactAndroid/src/main/java/com/facebook/react
|
||||
buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
buck fetch ReactAndroid/src/test/...
|
||||
buck fetch ReactAndroid/src/androidTest/...
|
||||
./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
|
||||
|
||||
defaults: &defaults
|
||||
working_directory: ~/react-native
|
||||
@@ -193,19 +182,6 @@ android_defaults: &android_defaults
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
# Runs JavaScript lint and flow checks
|
||||
run-js-checks:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- restore-cache: *restore-node-cache
|
||||
- run: *install-node-dependencies
|
||||
- save-cache: *save-node-cache
|
||||
- run: *run-lint-checks
|
||||
- run: *run-flow-checks
|
||||
|
||||
# Runs JavaScript tests on Node 8
|
||||
test-js-node-8:
|
||||
<<: *defaults
|
||||
@@ -222,7 +198,19 @@ jobs:
|
||||
test-js-node-6:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/node:6
|
||||
- image: circleci/node:6.11.0
|
||||
steps:
|
||||
- checkout
|
||||
- restore-cache: *restore-node-cache
|
||||
- run: *install-node-dependencies
|
||||
- save-cache: *save-node-cache
|
||||
- run: *run-node-tests
|
||||
|
||||
# Runs JavaScript tests on Node 4
|
||||
test-js-node-4:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/node:4.8.4
|
||||
steps:
|
||||
- checkout
|
||||
- restore-cache: *restore-node-cache
|
||||
@@ -287,6 +275,52 @@ jobs:
|
||||
- save-cache: *save-node-cache
|
||||
- run: ./scripts/process-podspecs.sh
|
||||
|
||||
# Tests website
|
||||
test-website:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
cd website
|
||||
npm install --no-package-lock --no-spin --no-progress
|
||||
- run:
|
||||
name: Test Website
|
||||
command: |
|
||||
cd website
|
||||
npm test
|
||||
- run:
|
||||
name: Test Build Static Website
|
||||
command: cd website && node ./server/generate.js
|
||||
|
||||
# Deploys website
|
||||
deploy-website:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
cd website
|
||||
npm install --no-package-lock --no-spin --no-progress
|
||||
- run:
|
||||
name: Build and Deploy Static Website
|
||||
command: |
|
||||
if [[ $CIRCLE_PROJECT_USERNAME == "facebook" && -z $CI_PULL_REQUEST && -z $CIRCLE_PR_USERNAME ]]; then
|
||||
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
|
||||
|
||||
# Publishes new version onto npm
|
||||
deploy:
|
||||
<<: *android_defaults
|
||||
@@ -310,12 +344,9 @@ jobs:
|
||||
- restore-cache: *restore-node-cache
|
||||
- run: *install-node-dependencies
|
||||
- save-cache: *save-node-cache
|
||||
- run: buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
|
||||
- run: buck fetch ReactAndroid/src/main/java/com/facebook/react
|
||||
- run: buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
- run: buck fetch ReactAndroid/src/test/...
|
||||
- run: buck fetch ReactAndroid/src/androidTest/...
|
||||
- run: ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
|
||||
- restore-cache: *restore-cache-buck-downloads
|
||||
- run: *install-android-app-dependencies
|
||||
- save-cache: *save-cache-buck-downloads
|
||||
|
||||
- run:
|
||||
name: Publish React Native Package
|
||||
@@ -323,7 +354,7 @@ jobs:
|
||||
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 "npm Deployment Script"
|
||||
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
|
||||
@@ -377,42 +408,63 @@ jobs:
|
||||
- restore-cache: *restore-cache-ndk
|
||||
- run: *install-ndk
|
||||
- save-cache: *save-cache-ndk
|
||||
|
||||
- restore-cache: *restore-cache-buck
|
||||
- run: *install-buck
|
||||
- save-cache: *save-cache-buck
|
||||
|
||||
- run: *install-node
|
||||
|
||||
- restore-cache: *restore-node-cache
|
||||
- run: *install-node-dependencies
|
||||
- save-cache: *save-node-cache
|
||||
- restore-cache: *restore-cache-buck-downloads
|
||||
- run: *install-android-app-dependencies
|
||||
- save-cache: *save-cache-buck-downloads
|
||||
|
||||
- run: buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
|
||||
- run: buck fetch ReactAndroid/src/main/java/com/facebook/react
|
||||
- run: buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
- run: buck fetch ReactAndroid/src/test/...
|
||||
- run: buck fetch ReactAndroid/src/androidTest/...
|
||||
- run: ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog :ReactAndroid:downloadJSCHeaders
|
||||
|
||||
- run: *build-android-app
|
||||
- run: *compile-native-libs
|
||||
|
||||
- run:
|
||||
name: Build Android App
|
||||
command: |
|
||||
buck build ReactAndroid/src/main/java/com/facebook/react
|
||||
buck build ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
|
||||
# Wait for AVD to finish booting before running tests
|
||||
- run:
|
||||
name: Wait for Android Virtual Device
|
||||
command: source scripts/circle-ci-android-setup.sh && waitForAVD
|
||||
|
||||
# The JavaScript Bundle is built as part of the build-js-bundle workflow,
|
||||
# and is required for instrumentation tests.
|
||||
- attach_workspace:
|
||||
at: ReactAndroid/src/androidTest/assets/
|
||||
- run: *check-js-bundle
|
||||
|
||||
# Wait for AVD to finish booting before running tests
|
||||
- run: *wait-for-avd
|
||||
|
||||
- run:
|
||||
name: Check for JavaScript Bundle
|
||||
command: |
|
||||
if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then
|
||||
echo "JavaScript bundle missing, verify build-js-bundle step"; exit 1;
|
||||
else
|
||||
echo "JavaScript bundle found.";
|
||||
fi
|
||||
|
||||
# Tests
|
||||
- run: *run-android-unit-tests
|
||||
- run: *run-android-integration-tests
|
||||
- run:
|
||||
name: Compile Native Libs for Unit and Integration Tests
|
||||
command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS -Pcom.android.build.threadPoolSize=1
|
||||
no_output_timeout: 6m
|
||||
- run:
|
||||
name: Unit Tests
|
||||
command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS
|
||||
|
||||
# Integration Tests
|
||||
- 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
|
||||
|
||||
# post (always runs)
|
||||
- run: *collect-android-test-results
|
||||
- run:
|
||||
name: Collect Test Results
|
||||
command: |
|
||||
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/ \;
|
||||
when: always
|
||||
- store_test_results:
|
||||
path: ~/junit
|
||||
- store_artifacts:
|
||||
@@ -454,8 +506,7 @@ jobs:
|
||||
else
|
||||
echo "Skipping code analysis."
|
||||
fi
|
||||
when: always
|
||||
|
||||
|
||||
# Workflows enables us to run multiple jobs in parallel
|
||||
workflows:
|
||||
version: 2
|
||||
@@ -463,11 +514,7 @@ workflows:
|
||||
build:
|
||||
jobs:
|
||||
|
||||
# Run lint and flow checks
|
||||
- run-js-checks:
|
||||
filters: *filter-ignore-gh-pages
|
||||
|
||||
# Test JavaScript on Node 8 and 6
|
||||
# Test Javascript on Node 8 and 6
|
||||
- test-js-node-8:
|
||||
filters: *filter-ignore-gh-pages
|
||||
- test-js-node-6:
|
||||
@@ -487,6 +534,10 @@ workflows:
|
||||
filters: *filter-ignore-gh-pages
|
||||
- test-objc-e2e:
|
||||
filters: *filter-ignore-gh-pages
|
||||
|
||||
# Test website
|
||||
- test-website:
|
||||
filters: *filter-ignore-gh-pages
|
||||
|
||||
# If we are on a stable branch, deploy to `npm`
|
||||
- hold:
|
||||
@@ -496,5 +547,11 @@ workflows:
|
||||
requires:
|
||||
- hold
|
||||
|
||||
# If we are on a master / stable branch, deploy docs
|
||||
- deploy-website:
|
||||
filters: *filter-only-master-stable
|
||||
requires:
|
||||
- test-website
|
||||
|
||||
- analyze-pull-request:
|
||||
filters: *filter-ignore-master-stable
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
**/main.js
|
||||
Libraries/vendor/**/*
|
||||
Libraries/Renderer/*
|
||||
website/node_modules
|
||||
pr-inactivity-bookmarklet.js
|
||||
question-bookmarklet.js
|
||||
flow/
|
||||
website/core/metadata.js
|
||||
website/core/metadata-blog.js
|
||||
website/src/react-native/docs/
|
||||
website/src/react-native/blog/
|
||||
danger/
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
|
||||
"env": {
|
||||
"es6": true,
|
||||
"jest": true,
|
||||
"jasmine": true,
|
||||
},
|
||||
|
||||
"plugins": [
|
||||
"eslint-comments",
|
||||
"flowtype",
|
||||
"prettier",
|
||||
"react",
|
||||
"jest"
|
||||
"react"
|
||||
],
|
||||
|
||||
// Map from global var to bool specifying if it can be redefined
|
||||
@@ -59,6 +57,11 @@
|
||||
},
|
||||
|
||||
"rules": {
|
||||
// Flow Plugin
|
||||
// The following rules are made available via `eslint-plugin-flowtype`
|
||||
"flowtype/define-flow-type": 1,
|
||||
"flowtype/use-flow-type": 1,
|
||||
|
||||
// General
|
||||
|
||||
// This must be disallowed in this repo because the minimum supported
|
||||
@@ -78,7 +81,7 @@
|
||||
"no-constant-condition": 0, // disallow use of constant expressions in conditions
|
||||
"no-control-regex": 1, // disallow control characters in regular expressions
|
||||
"no-debugger": 1, // disallow use of debugger
|
||||
"no-dupe-keys": 2, // disallow duplicate keys when creating object literals
|
||||
"no-dupe-keys": 1, // disallow duplicate keys when creating object literals
|
||||
"no-empty": 0, // disallow empty statements
|
||||
"no-ex-assign": 1, // disallow assigning to the exception in a catch block
|
||||
"no-extra-boolean-cast": 1, // disallow double-negation boolean casts in a boolean context
|
||||
@@ -92,7 +95,7 @@
|
||||
"no-regex-spaces": 1, // disallow multiple spaces in a regular expression literal
|
||||
"no-reserved-keys": 0, // disallow reserved words being used as object literal keys (off by default)
|
||||
"no-sparse-arrays": 1, // disallow sparse arrays
|
||||
"no-unreachable": 2, // disallow unreachable statements after a return, throw, continue, or break statement
|
||||
"no-unreachable": 1, // disallow unreachable statements after a return, throw, continue, or break statement
|
||||
"use-isnan": 1, // disallow comparisons with the value NaN
|
||||
"valid-jsdoc": 0, // Ensure JSDoc comments are valid (off by default)
|
||||
"valid-typeof": 1, // Ensure that the results of typeof are compared against a valid string
|
||||
@@ -113,7 +116,7 @@
|
||||
"no-div-regex": 1, // disallow division operators explicitly at beginning of regular expression (off by default)
|
||||
"no-else-return": 0, // disallow else after a return in an if (off by default)
|
||||
"no-eq-null": 0, // disallow comparisons to null without a type-checking operator (off by default)
|
||||
"no-eval": 2, // disallow use of eval()
|
||||
"no-eval": 1, // disallow use of eval()
|
||||
"no-extend-native": 1, // disallow adding to native types
|
||||
"no-extra-bind": 1, // disallow unnecessary function binding
|
||||
"no-fallthrough": 1, // disallow fallthrough of case statements
|
||||
@@ -126,7 +129,7 @@
|
||||
"no-multi-str": 0, // disallow use of multiline strings
|
||||
"no-native-reassign": 0, // disallow reassignments of native objects
|
||||
"no-new": 1, // disallow use of new operator when not part of the assignment or comparison
|
||||
"no-new-func": 2, // disallow use of new operator for Function object
|
||||
"no-new-func": 1, // disallow use of new operator for Function object
|
||||
"no-new-wrappers": 1, // disallows creating new instances of String,Number, and Boolean
|
||||
"no-octal": 1, // disallow use of octal literals
|
||||
"no-octal-escape": 1, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
|
||||
@@ -171,18 +174,6 @@
|
||||
"no-restricted-modules": 1, // restrict usage of specified node modules (off by default)
|
||||
"no-sync": 0, // disallow use of synchronous methods (off by default)
|
||||
|
||||
// ESLint Comments Plugin
|
||||
// The following rules are made available via `eslint-plugin-eslint-comments`
|
||||
'eslint-comments/no-aggregating-enable': 1, // disallows eslint-enable comments for multiple eslint-disable comments
|
||||
'eslint-comments/no-unlimited-disable': 1, // disallows eslint-disable comments without rule names
|
||||
'eslint-comments/no-unused-disable': 1, // disallow disables that don't cover any errors
|
||||
'eslint-comments/no-unused-enable': 1, // // disallow enables that don't enable anything or enable rules that weren't disabled
|
||||
|
||||
// Flow Plugin
|
||||
// The following rules are made available via `eslint-plugin-flowtype`
|
||||
"flowtype/define-flow-type": 1,
|
||||
"flowtype/use-flow-type": 1,
|
||||
|
||||
// Prettier Plugin
|
||||
// https://github.com/prettier/eslint-plugin-prettier
|
||||
"prettier/prettier": [2, "fb", "@format"],
|
||||
@@ -192,12 +183,12 @@
|
||||
|
||||
"key-spacing": 0,
|
||||
"keyword-spacing": 1, // enforce spacing before and after keywords
|
||||
"jsx-quotes": [1, "prefer-double"], // enforces the usage of double quotes for all JSX attribute values which doesn’t contain a double quote
|
||||
"jsx-quotes": [1, "prefer-double"],
|
||||
"comma-spacing": 0,
|
||||
"no-multi-spaces": 0,
|
||||
"brace-style": 0, // enforce one true brace style (off by default)
|
||||
"camelcase": 0, // require camel case names
|
||||
"consistent-this": 1, // enforces consistent naming when capturing the current execution context (off by default)
|
||||
"consistent-this": [1, "self"], // enforces consistent naming when capturing the current execution context (off by default)
|
||||
"eol-last": 1, // enforce newline at the end of file, with no multiple empty lines
|
||||
"func-names": 0, // require function expressions to have a name (off by default)
|
||||
"func-style": 0, // enforces use of function declarations or expressions (off by default)
|
||||
@@ -205,7 +196,6 @@
|
||||
"new-parens": 1, // disallow the omission of parentheses when invoking a constructor with no arguments
|
||||
"no-nested-ternary": 0, // disallow nested ternary expressions (off by default)
|
||||
"no-array-constructor": 1, // disallow use of the Array constructor
|
||||
'no-empty-character-class': 1, // disallow the use of empty character classes in regular expressions
|
||||
"no-lonely-if": 0, // disallow if as the only statement in an else block (off by default)
|
||||
"no-new-object": 1, // disallow use of the Object constructor
|
||||
"no-spaced-func": 1, // disallow space between function identifier and application
|
||||
@@ -254,13 +244,6 @@
|
||||
"react/prop-types": 0,
|
||||
"react/react-in-jsx-scope": 1,
|
||||
"react/self-closing-comp": 1,
|
||||
"react/wrap-multilines": 0,
|
||||
|
||||
// Jest Plugin
|
||||
// The following rules are made available via `eslint-plugin-jest`.
|
||||
"jest/no-disabled-tests": 1,
|
||||
"jest/no-focused-tests": 1,
|
||||
"jest/no-identical-title": 1,
|
||||
"jest/valid-expect": 1,
|
||||
"react/wrap-multilines": 0
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -5,6 +5,9 @@
|
||||
; Ignore templates for 'react-native init'
|
||||
.*/local-cli/templates/.*
|
||||
|
||||
; Ignore the website subdir
|
||||
<PROJECT_ROOT>/website/.*
|
||||
|
||||
; Ignore the Dangerfile
|
||||
<PROJECT_ROOT>/danger/dangerfile.js
|
||||
|
||||
@@ -22,15 +25,11 @@
|
||||
; Ignore polyfills
|
||||
.*/Libraries/polyfills/.*
|
||||
|
||||
; Ignore metro
|
||||
.*/node_modules/metro/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
Libraries/react-native/react-native-interface.js
|
||||
flow/
|
||||
flow-github/
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
@@ -45,11 +44,14 @@ suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native_oss|react_native_fb][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.63.0
|
||||
^0.57.0
|
||||
|
||||
+5
-1
@@ -1,3 +1,5 @@
|
||||
docs/* @hramos
|
||||
blog/* @hramos
|
||||
Libraries/Animated/* @janicduplessis
|
||||
Libraries/NativeAnimation/* @janicduplessis
|
||||
Libraries/Image/* @shergin
|
||||
@@ -7,8 +9,10 @@ React/Views/* @shergin
|
||||
React/Modules/* @shergin
|
||||
React/CxxBridge/* @mhorowitz
|
||||
ReactAndroid/src/main/java/com/facebook/react/animated/* @janicduplessis
|
||||
**/*.md @hramos
|
||||
website/* @hramos
|
||||
website/showcase.json @hramos
|
||||
package.json @hramos
|
||||
website/package.json @hramos
|
||||
local-cli/core/* @grabbou @kureev
|
||||
local-cli/link/* @grabbou @kureev
|
||||
local-cli/unlink/* @grabbou @kureev
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
If you answered "No":
|
||||
|
||||
We use GitHub Issues exclusively for tracking bugs in React Native. If you're looking for help,
|
||||
the Community page at http://facebook.github.io/react-native/help.html list various resources
|
||||
the Community page at http://facebook.github.io/react-native/support.html list various resources
|
||||
that should help you get started.
|
||||
|
||||
Now scroll below!
|
||||
|
||||
@@ -17,10 +17,6 @@ Happy contributing!
|
||||
|
||||
(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!)
|
||||
|
||||
## Related PRs
|
||||
|
||||
(If this PR adds or changes functionality, please take some time to update the docs at https://github.com/facebook/react-native-website, and link to your PR here.)
|
||||
|
||||
## Release Notes
|
||||
<!--
|
||||
Help reviewers and the release process by writing your own release notes
|
||||
@@ -31,7 +27,7 @@ Help reviewers and the release process by writing your own release notes
|
||||
[----------] TYPE
|
||||
[ CLI ] [-------------] LOCATION
|
||||
[ DOCS ] [ BREAKING ] [-------------]
|
||||
[ GENERAL ] [ BUGFIX ] [-{Component}-]
|
||||
[ GENERAl ] [ BUGFIX ] [-{Component}-]
|
||||
[ INTERNAL ] [ ENHANCEMENT ] [ {File} ]
|
||||
[ IOS ] [ FEATURE ] [ {Directory} ] |-----------|
|
||||
[ ANDROID ] [ MINOR ] [ {Framework} ] - | {Message} |
|
||||
|
||||
+15
-14
@@ -11,10 +11,10 @@ Facebook has adopted a Code of Conduct that we expect project participants to ad
|
||||
|
||||
There are many ways to contribute to React Native, and many of them do not involve writing any code. Here's a few ideas to get started:
|
||||
|
||||
* Simply start using React Native. Go through the [Getting Started](https://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](https://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](https://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](https://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](https://facebook.github.io/react-native/docs/getting-started.html). 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.
|
||||
* 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 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.
|
||||
|
||||
@@ -43,13 +43,13 @@ When a change made on GitHub is approved, it will first be imported into Faceboo
|
||||
|
||||
We will do our best to keep `master` in good shape, with tests passing at all times. But in order to move fast, we will make API changes that your application might not be compatible with. We will do our best to [communicate these changes](https://github.com/facebook/react-native/releases) and version appropriately so you can lock into a specific version if need be.
|
||||
|
||||
To see what changes are coming and provide better feedback to React Native contributors, use the [latest release candidate](https://facebook.github.io/react-native/versions.html) when possible. By the time a release candidate is released, the changes it contains will have been shipped in production Facebook apps for over two weeks.
|
||||
To see what changes are coming and provide better feedback to React Native contributors, use the [latest release candidate](http://facebook.github.io/react-native/versions.html) when possible. By the time a release candidate is released, the changes it contains will have been shipped in production Facebook apps for over two weeks.
|
||||
|
||||
## Bugs
|
||||
|
||||
We use [GitHub Issues](https://github.com/facebook/react-native/issues) for our public bugs. If you would like to report a problem, take a look around and see if someone already opened an issue about it. If you a are certain this is a new, unreported bug, you can submit a [bug report](https://facebook.github.io/react-native/docs/contributing.html#reporting-new-issues).
|
||||
We use [GitHub Issues](https://github.com/facebook/react-native/issues) for our public bugs. If you would like to report a problem, take a look around and see if someone already opened an issue about it. If you a are certain this is a new, unreported bug, you can submit a [bug report](http://facebook.github.io/react-native/docs/contributing.html#reporting-new-issues).
|
||||
|
||||
If you have questions about using React Native, the [Community page](https://facebook.github.io/react-native/help.html) list various resources that should help you get started.
|
||||
If you have questions about using React Native, the [Community page](http://facebook.github.io/react-native/support.html) list various resources that should help you get started.
|
||||
|
||||
We also have a [place where you can request features or enhancements](https://react-native.canny.io/feature-requests). If you see anything you'd like to be implemented, vote it up and explain your use case.
|
||||
|
||||
@@ -60,9 +60,9 @@ When [opening a new issue](https://github.com/facebook/react-native/issues/new),
|
||||
* **One issue, one bug:** Please report a single bug per issue.
|
||||
* **Provide a Snack:** The best way to get attention on your issue is to provide a reduced test case. You can use [Snack](https://snack.expo.io/) to demonstrate the issue.
|
||||
* **Provide reproduction steps:** List all the steps necessary to reproduce the issue. Provide a Snack or upload a sample project to GitHub. The person reading your bug report should be able to follow these steps to reproduce your issue with minimal effort.
|
||||
* **Try out the latest version:** Verify that the issue can be reproduced locally by updating your project to use [React Native from `master`](https://facebook.github.io/react-native/versions.html). The bug may have already been fixed!
|
||||
* **Try out the latest version:** Verify that the issue can be reproduced locally by updating your project to use [React Native from `master`](http://facebook.github.io/react-native/versions.html). The bug may have already been fixed!
|
||||
|
||||
We're not able to provide support through GitHub Issues. If you're looking for help with your code, consider asking on [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native) or reaching out to the community through [other channels](https://facebook.github.io/react-native/support.html).
|
||||
We're not able to provide support through GitHub Issues. If you're looking for help with your code, consider asking on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) or reaching out to the community through [other channels](https://facebook.github.io/react-native/support.html).
|
||||
|
||||
### Security bugs
|
||||
|
||||
@@ -96,28 +96,29 @@ 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 pull request description. Make sure to [test your changes](https://facebook.github.io/react-native/docs/testing.html)!
|
||||
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. After opening your pull request, ensure [**all tests pass**](https://facebook.github.io/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.
|
||||
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.
|
||||
|
||||
#### Test plan
|
||||
|
||||
A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI.
|
||||
A good test plan has the exact commands you ran and their output, provides screenshots or videos if the pull request changes UI or updates the website.
|
||||
|
||||
* If you've added code that should be tested, add tests!
|
||||
* If you've changed APIs, update the documentation.
|
||||
* If you've updated the docs, verify the website locally and submit screenshots if applicable (see [website/README.md](https://github.com/facebook/react-native/blob/master/website/README.md))
|
||||
|
||||
See [What is a Test Plan?](https://medium.com/@martinkonicek/what-is-a-test-plan-8bfc840ec171#.y9lcuqqi9) to learn more.
|
||||
|
||||
#### Continuous integration tests
|
||||
|
||||
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](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).
|
||||
|
||||
[circle]: https://circleci.com/gh/facebook/react-native
|
||||
[circle]: http://circleci.com/gh/facebook/react-native
|
||||
|
||||
#### Breaking changes
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM hramos/android-base:latest
|
||||
FROM containership/android-base:latest
|
||||
|
||||
# set default environment variables
|
||||
ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
|
||||
ENV GRADLE_OPTS="-Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
|
||||
ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8"
|
||||
|
||||
# add ReactAndroid directory
|
||||
@@ -46,5 +46,9 @@ RUN cd $(npm root -g)/npm && npm install fs-extra && sed -i -e s/graceful-fs/fs-
|
||||
|
||||
# build node dependencies
|
||||
RUN npm install
|
||||
RUN npm install github@0.2.4
|
||||
|
||||
WORKDIR /app/website
|
||||
RUN npm install
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
FROM library/ubuntu:16.04
|
||||
|
||||
# set default build arguments
|
||||
ARG ANDROID_TOOLS_VERSION=25.2.5
|
||||
ARG BUCK_VERSION=v2017.11.16.01
|
||||
ARG ANDROID_VERSION=25.2.3
|
||||
ARG BUCK_VERSION=f3452a6a7ab15a60e94c962e686293acbe677473
|
||||
ARG NDK_VERSION=10e
|
||||
ARG NODE_VERSION=6.2.0
|
||||
ARG WATCHMAN_VERSION=4.7.0
|
||||
@@ -12,7 +12,7 @@ ENV ADB_INSTALL_TIMEOUT=10
|
||||
ENV PATH=${PATH}:/opt/buck/bin/
|
||||
ENV ANDROID_HOME=/opt/android
|
||||
ENV ANDROID_SDK_HOME=${ANDROID_HOME}
|
||||
ENV PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools
|
||||
ENV PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
|
||||
ENV ANDROID_NDK=/opt/ndk/android-ndk-r$NDK_VERSION
|
||||
ENV PATH=${PATH}:${ANDROID_NDK}
|
||||
|
||||
@@ -28,8 +28,9 @@ RUN npm install n -g
|
||||
RUN n $NODE_VERSION
|
||||
|
||||
# download buck
|
||||
RUN git clone https://github.com/facebook/buck.git /opt/buck --branch $BUCK_VERSION --depth=1
|
||||
RUN git clone https://github.com/facebook/buck.git /opt/buck
|
||||
WORKDIR /opt/buck
|
||||
RUN git checkout $BUCK_VERSION
|
||||
|
||||
# build buck
|
||||
RUN ant
|
||||
@@ -45,11 +46,10 @@ RUN ./configure
|
||||
RUN make
|
||||
RUN make install
|
||||
|
||||
# Full reference at https://dl.google.com/android/repository/repository2-1.xml
|
||||
# download and unpack android
|
||||
RUN mkdir /opt/android
|
||||
WORKDIR /opt/android
|
||||
RUN curl --silent https://dl.google.com/android/repository/tools_r$ANDROID_TOOLS_VERSION-linux.zip > android.zip
|
||||
RUN curl --silent https://dl.google.com/android/repository/tools_r$ANDROID_VERSION-linux.zip > android.zip
|
||||
RUN unzip android.zip
|
||||
RUN rm android.zip
|
||||
|
||||
@@ -63,15 +63,39 @@ RUN unzip ndk.zip
|
||||
RUN rm ndk.zip
|
||||
|
||||
# Add android SDK tools
|
||||
RUN echo "y" | sdkmanager "system-images;android-19;google_apis;armeabi-v7a"
|
||||
RUN echo "y" | sdkmanager "platforms;android-23"
|
||||
RUN echo "y" | sdkmanager "platforms;android-19"
|
||||
RUN echo "y" | sdkmanager "build-tools;23.0.1"
|
||||
RUN echo "y" | sdkmanager "add-ons;addon-google_apis-google-23"
|
||||
RUN echo "y" | sdkmanager "extras;android;m2repository"
|
||||
|
||||
# Android SDK Platform-tools, revision 25.0.4
|
||||
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android SDK Platform-tools, revision 25.0.4" | awk '{ print $1 }' | sed 's/.$//')
|
||||
|
||||
# Android SDK Build-tools, revision 23.0.1
|
||||
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android SDK Build-tools, revision 23.0.1" | awk '{ print $1 }' | sed 's/.$//')
|
||||
|
||||
# SDK Platform Android 6.0, API 23, revision 3
|
||||
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "SDK Platform Android 6.0, API 23" | awk '{ print $1 }' | sed 's/.$//')
|
||||
|
||||
# SDK Platform Android 4.4.2, API 19, revision 4
|
||||
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "SDK Platform Android 4.4.2, API 19, revision 4" | awk '{ print $1 }' | sed 's/.$//')
|
||||
|
||||
# ARM EABI v7a System Image, Android API 19, revision 5
|
||||
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "ARM EABI v7a System Image, Android API 19, revision 5" | awk '{ print $1 }' | sed 's/.$//')
|
||||
|
||||
# Intel x86 Atom System Image, Android API 19, revision 5
|
||||
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Intel x86 Atom System Image, Android API 19, revision 5" | awk '{ print $1 }' | sed 's/.$//')
|
||||
|
||||
# Google APIs, Android API 23, revision 1
|
||||
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Google APIs, Android API 23, revision 1" | awk '{ print $1 }' | sed 's/.$//')
|
||||
|
||||
# Android Support Repository, revision 45
|
||||
RUN echo "y" | android update sdk -u -a -t $(android list sdk -a | grep "Android Support Repository" | awk '{ print $1 }' | sed 's/.$//')
|
||||
|
||||
# Link adb executable
|
||||
RUN ln -s /opt/android/platform-tools/adb /usr/bin/adb
|
||||
|
||||
# Install google-chrome
|
||||
RUN curl -fsSL https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||
&& echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y google-chrome-stable
|
||||
|
||||
# clean up unnecessary directories
|
||||
RUN rm -rf /opt/android/system-images/android-19/default/x86
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
* --package - com.facebook.react.tests
|
||||
* --retries [num] - how many times to retry possible flaky commands: npm install and running tests, default 1
|
||||
*/
|
||||
/*eslint-disable no-undef */
|
||||
|
||||
const argv = require('yargs').argv;
|
||||
const async = require('async');
|
||||
|
||||
@@ -65,7 +65,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
|
||||
<Text>Hello</Text>
|
||||
<Image
|
||||
source={{
|
||||
uri: 'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' + Date.now(),
|
||||
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
|
||||
cache: 'only-if-cached'
|
||||
}}
|
||||
onLoad={() => this.testComplete('only-if-cached', false)}
|
||||
@@ -74,7 +74,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
|
||||
/>
|
||||
<Image
|
||||
source={{
|
||||
uri: 'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' + Date.now(),
|
||||
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
|
||||
cache: 'default'
|
||||
}}
|
||||
onLoad={() => this.testComplete('default', true)}
|
||||
@@ -83,7 +83,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
|
||||
/>
|
||||
<Image
|
||||
source={{
|
||||
uri: 'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' + Date.now(),
|
||||
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
|
||||
cache: 'reload'
|
||||
}}
|
||||
onLoad={() => this.testComplete('reload', true)}
|
||||
@@ -92,7 +92,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
|
||||
/>
|
||||
<Image
|
||||
source={{
|
||||
uri: 'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' + Date.now(),
|
||||
uri: 'https://facebook.github.io/react/img/logo_small_2x.png?cacheBust=notinCache' + Date.now(),
|
||||
cache: 'force-cache'
|
||||
}}
|
||||
onLoad={() => this.testComplete('force-cache', true)}
|
||||
|
||||
@@ -29,7 +29,17 @@ function debug(...args) {
|
||||
// console.log.apply(null, arguments);
|
||||
}
|
||||
|
||||
import type {Layout, LayoutEvent} from 'CoreEventTypes';
|
||||
type Layout = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
type LayoutEvent = {
|
||||
nativeEvent: {
|
||||
layout: Layout;
|
||||
};
|
||||
};
|
||||
type Style = {
|
||||
margin?: number,
|
||||
padding?: number,
|
||||
|
||||
@@ -15,15 +15,6 @@
|
||||
|
||||
@implementation ARTSurfaceView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.opaque = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
[super insertReactSubview:subview atIndex:atIndex];
|
||||
@@ -55,4 +46,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor
|
||||
{
|
||||
self.backgroundColor = inheritedBackgroundColor;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,25 +8,18 @@
|
||||
*
|
||||
* @providesModule ActionSheetIOS
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const RCTActionSheetManager = require('NativeModules').ActionSheetManager;
|
||||
var RCTActionSheetManager = require('NativeModules').ActionSheetManager;
|
||||
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
const processColor = require('processColor');
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
var processColor = require('processColor');
|
||||
|
||||
/**
|
||||
* Display action sheets and share sheets on iOS.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/actionsheetios.html
|
||||
*/
|
||||
const ActionSheetIOS = {
|
||||
var ActionSheetIOS = {
|
||||
/**
|
||||
* Display an iOS action sheet.
|
||||
*
|
||||
* The `options` object must contain one or more of:
|
||||
* Display an iOS action sheet. The `options` object must contain one or more
|
||||
* of:
|
||||
*
|
||||
* - `options` (array of strings) - a list of button titles (required)
|
||||
* - `cancelButtonIndex` (int) - index of cancel button in `options`
|
||||
@@ -37,29 +30,32 @@ const ActionSheetIOS = {
|
||||
* The 'callback' function takes one parameter, the zero-based index
|
||||
* of the selected item.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/actionsheetios.html#showactionsheetwithoptions
|
||||
* Minimal example:
|
||||
*
|
||||
* ```
|
||||
* ActionSheetIOS.showActionSheetWithOptions({
|
||||
* options: ['Remove', 'Cancel'],
|
||||
* destructiveButtonIndex: 1,
|
||||
* cancelButtonIndex: 0,
|
||||
* },
|
||||
* (buttonIndex) => {
|
||||
* if (buttonIndex === 1) { // destructive action }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
showActionSheetWithOptions(
|
||||
options: {|
|
||||
+title?: ?string,
|
||||
+message?: ?string,
|
||||
+options: Array<string>,
|
||||
+destructiveButtonIndex?: ?number,
|
||||
+cancelButtonIndex?: ?number,
|
||||
+anchor?: ?number,
|
||||
+tintColor?: number | string,
|
||||
|},
|
||||
callback: (buttonIndex: number) => void,
|
||||
) {
|
||||
showActionSheetWithOptions(options: Object, callback: Function) {
|
||||
invariant(
|
||||
typeof options === 'object' && options !== null,
|
||||
'Options must be a valid object',
|
||||
'Options must be a valid object'
|
||||
);
|
||||
invariant(
|
||||
typeof callback === 'function',
|
||||
'Must provide a valid callback'
|
||||
);
|
||||
invariant(typeof callback === 'function', 'Must provide a valid callback');
|
||||
|
||||
RCTActionSheetManager.showActionSheetWithOptions(
|
||||
{...options, tintColor: processColor(options.tintColor)},
|
||||
callback,
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
@@ -71,9 +67,11 @@ const ActionSheetIOS = {
|
||||
* - `url` (string) - a URL to share
|
||||
* - `message` (string) - a message to share
|
||||
* - `subject` (string) - a subject for the message
|
||||
* - `excludedActivityTypes` (array) - the activities to exclude from
|
||||
* the ActionSheet
|
||||
* - `tintColor` (color) - tint color of the buttons
|
||||
* - `excludedActivityTypes` (array) - the activities to exclude from the ActionSheet
|
||||
*
|
||||
* NOTE: if `url` points to a local file, or is a base64-encoded
|
||||
* uri, the file it points to will be loaded and shared directly.
|
||||
* In this way, you can share images, videos, PDF files, etc.
|
||||
*
|
||||
* The 'failureCallback' function takes one parameter, an error object.
|
||||
* The only property defined on this object is an optional `stack` property
|
||||
@@ -83,32 +81,30 @@ const ActionSheetIOS = {
|
||||
*
|
||||
* - a boolean value signifying success or failure
|
||||
* - a string that, in the case of success, indicates the method of sharing
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/actionsheetios.html#showshareactionsheetwithoptions
|
||||
*/
|
||||
showShareActionSheetWithOptions(
|
||||
options: Object,
|
||||
failureCallback: Function,
|
||||
successCallback: Function,
|
||||
successCallback: Function
|
||||
) {
|
||||
invariant(
|
||||
typeof options === 'object' && options !== null,
|
||||
'Options must be a valid object',
|
||||
'Options must be a valid object'
|
||||
);
|
||||
invariant(
|
||||
typeof failureCallback === 'function',
|
||||
'Must provide a valid failureCallback',
|
||||
'Must provide a valid failureCallback'
|
||||
);
|
||||
invariant(
|
||||
typeof successCallback === 'function',
|
||||
'Must provide a valid successCallback',
|
||||
'Must provide a valid successCallback'
|
||||
);
|
||||
RCTActionSheetManager.showShareActionSheetWithOptions(
|
||||
{...options, tintColor: processColor(options.tintColor)},
|
||||
failureCallback,
|
||||
successCallback,
|
||||
successCallback
|
||||
);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ActionSheetIOS;
|
||||
|
||||
@@ -17,7 +17,7 @@ const Platform = require('Platform');
|
||||
|
||||
import type { AlertType, AlertButtonStyle } from 'AlertIOS';
|
||||
|
||||
export type Buttons = Array<{
|
||||
type Buttons = Array<{
|
||||
text?: string,
|
||||
onPress?: ?Function,
|
||||
style?: AlertButtonStyle,
|
||||
@@ -30,16 +30,54 @@ type Options = {
|
||||
|
||||
/**
|
||||
* Launches an alert dialog with the specified title and message.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/alert.html
|
||||
*
|
||||
* Optionally provide a list of buttons. Tapping any button will fire the
|
||||
* respective onPress callback and dismiss the alert. By default, the only
|
||||
* button will be an 'OK' button.
|
||||
*
|
||||
* This is an API that works both on iOS and Android and can show static
|
||||
* alerts. To show an alert that prompts the user to enter some information,
|
||||
* see `AlertIOS`; entering text in an alert is common on iOS only.
|
||||
*
|
||||
* ## iOS
|
||||
*
|
||||
* On iOS you can specify any number of buttons. Each button can optionally
|
||||
* specify a style, which is one of 'default', 'cancel' or 'destructive'.
|
||||
*
|
||||
* ## Android
|
||||
*
|
||||
* On Android at most three buttons can be specified. Android has a concept
|
||||
* of a neutral, negative and a positive button:
|
||||
*
|
||||
* - If you specify one button, it will be the 'positive' one (such as 'OK')
|
||||
* - Two buttons mean 'negative', 'positive' (such as 'Cancel', 'OK')
|
||||
* - Three buttons mean 'neutral', 'negative', 'positive' (such as 'Later', 'Cancel', 'OK')
|
||||
*
|
||||
* By default alerts on Android can be dismissed by tapping outside of the alert
|
||||
* box. This event can be handled by providing an optional `options` parameter,
|
||||
* with an `onDismiss` callback property `{ onDismiss: () => {} }`.
|
||||
*
|
||||
* Alternatively, the dismissing behavior can be disabled altogether by providing
|
||||
* an optional `options` parameter with the `cancelable` property set to `false`
|
||||
* i.e. `{ cancelable: false }`
|
||||
*
|
||||
* Example usage:
|
||||
* ```
|
||||
* // Works on both iOS and Android
|
||||
* Alert.alert(
|
||||
* 'Alert Title',
|
||||
* 'My Alert Msg',
|
||||
* [
|
||||
* {text: 'Ask me later', onPress: () => console.log('Ask me later pressed')},
|
||||
* {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
|
||||
* {text: 'OK', onPress: () => console.log('OK Pressed')},
|
||||
* ],
|
||||
* { cancelable: false }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
class Alert {
|
||||
|
||||
/**
|
||||
* Launches an alert dialog with the specified title and message.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/alert.html#alert
|
||||
*/
|
||||
static alert(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
|
||||
@@ -77,15 +77,60 @@ export type ButtonsArray = Array<{
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Use `AlertIOS` to display an alert dialog with a message or to create a prompt for user input on iOS. If you don't need to prompt for user input, we recommend using `Alert.alert() for cross-platform support.
|
||||
* @description
|
||||
* `AlertIOS` provides functionality to create an iOS alert dialog with a
|
||||
* message or create a prompt for user input.
|
||||
*
|
||||
* Creating an iOS alert:
|
||||
*
|
||||
* ```
|
||||
* AlertIOS.alert(
|
||||
* 'Sync Complete',
|
||||
* 'All your data are belong to us.'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* Creating an iOS prompt:
|
||||
*
|
||||
* ```
|
||||
* AlertIOS.prompt(
|
||||
* 'Enter a value',
|
||||
* null,
|
||||
* text => console.log("You entered "+text)
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* We recommend using the [`Alert.alert`](docs/alert.html) method for
|
||||
* cross-platform support if you don't need to create iOS-only prompts.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/alertios.html
|
||||
*/
|
||||
class AlertIOS {
|
||||
/**
|
||||
* Create and display a popup alert.
|
||||
* @static
|
||||
* @method alert
|
||||
* @param title The dialog's title. Passing null or '' will hide the title.
|
||||
* @param message An optional message that appears below
|
||||
* the dialog's title.
|
||||
* @param callbackOrButtons This optional argument should
|
||||
* be either a single-argument function or an array of buttons. If passed
|
||||
* a function, it will be called when the user taps 'OK'.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/alertios.html#alert
|
||||
* If passed an array of button configurations, each button should include
|
||||
* a `text` key, as well as optional `onPress` and `style` keys. `style`
|
||||
* should be one of 'default', 'cancel' or 'destructive'.
|
||||
* @param type Deprecated, do not use.
|
||||
*
|
||||
* @example <caption>Example with custom buttons</caption>
|
||||
*
|
||||
* AlertIOS.alert(
|
||||
* 'Update available',
|
||||
* 'Keep your app up to date to enjoy the latest features',
|
||||
* [
|
||||
* {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
|
||||
* {text: 'Install', onPress: () => console.log('Install Pressed')},
|
||||
* ],
|
||||
* );
|
||||
*/
|
||||
static alert(
|
||||
title: ?string,
|
||||
@@ -103,8 +148,48 @@ class AlertIOS {
|
||||
|
||||
/**
|
||||
* Create and display a prompt to enter some text.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/alertios.html#prompt
|
||||
* @static
|
||||
* @method prompt
|
||||
* @param title The dialog's title.
|
||||
* @param message An optional message that appears above the text
|
||||
* input.
|
||||
* @param callbackOrButtons This optional argument should
|
||||
* be either a single-argument function or an array of buttons. If passed
|
||||
* a function, it will be called with the prompt's value when the user
|
||||
* taps 'OK'.
|
||||
*
|
||||
* If passed an array of button configurations, each button should include
|
||||
* a `text` key, as well as optional `onPress` and `style` keys (see
|
||||
* example). `style` should be one of 'default', 'cancel' or 'destructive'.
|
||||
* @param type This configures the text input. One of 'plain-text',
|
||||
* 'secure-text' or 'login-password'.
|
||||
* @param defaultValue The default text in text input.
|
||||
* @param keyboardType The keyboard type of first text field(if exists).
|
||||
* One of 'default', 'email-address', 'numeric', 'phone-pad',
|
||||
* 'ascii-capable', 'numbers-and-punctuation', 'url', 'number-pad',
|
||||
* 'name-phone-pad', 'decimal-pad', 'twitter' or 'web-search'.
|
||||
*
|
||||
* @example <caption>Example with custom buttons</caption>
|
||||
*
|
||||
* AlertIOS.prompt(
|
||||
* 'Enter password',
|
||||
* 'Enter your password to claim your $1.5B in lottery winnings',
|
||||
* [
|
||||
* {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
|
||||
* {text: 'OK', onPress: password => console.log('OK Pressed, password: ' + password)},
|
||||
* ],
|
||||
* 'secure-text'
|
||||
* );
|
||||
*
|
||||
* @example <caption>Example with the default button and a custom callback</caption>
|
||||
*
|
||||
* AlertIOS.prompt(
|
||||
* 'Update username',
|
||||
* null,
|
||||
* text => console.log("Your username is "+text),
|
||||
* null,
|
||||
* 'default'
|
||||
* );
|
||||
*/
|
||||
static prompt(
|
||||
title: ?string,
|
||||
|
||||
@@ -510,33 +510,183 @@ const event = function(argMapping: Array<?Mapping>, config?: EventConfig): any {
|
||||
* between inputs and outputs, with configurable transforms in between, and
|
||||
* simple `start`/`stop` methods to control time-based animation execution.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html
|
||||
* The simplest workflow for creating an animation is to create an
|
||||
* `Animated.Value`, hook it up to one or more style attributes of an animated
|
||||
* component, and then drive updates via animations using `Animated.timing()`:
|
||||
*
|
||||
* ```javascript
|
||||
* Animated.timing( // Animate value over time
|
||||
* this.state.fadeAnim, // The value to drive
|
||||
* {
|
||||
* toValue: 1, // Animate to final value of 1
|
||||
* }
|
||||
* ).start(); // Start the animation
|
||||
* ```
|
||||
*
|
||||
* Refer to the [Animations](docs/animations.html#animated-api) guide to see
|
||||
* additional examples of `Animated` in action.
|
||||
*
|
||||
* ## Overview
|
||||
*
|
||||
* There are two value types you can use with `Animated`:
|
||||
*
|
||||
* - [`Animated.Value()`](docs/animated.html#value) for single values
|
||||
* - [`Animated.ValueXY()`](docs/animated.html#valuexy) for vectors
|
||||
*
|
||||
* `Animated.Value` can bind to style properties or other props, and can be
|
||||
* interpolated as well. A single `Animated.Value` can drive any number of
|
||||
* properties.
|
||||
*
|
||||
* ### Configuring animations
|
||||
*
|
||||
* `Animated` provides three types of animation types. Each animation type
|
||||
* provides a particular animation curve that controls how your values animate
|
||||
* from their initial value to the final value:
|
||||
*
|
||||
* - [`Animated.decay()`](docs/animated.html#decay) starts with an initial
|
||||
* velocity and gradually slows to a stop.
|
||||
* - [`Animated.spring()`](docs/animated.html#spring) provides a simple
|
||||
* spring physics model.
|
||||
* - [`Animated.timing()`](docs/animated.html#timing) animates a value over time
|
||||
* using [easing functions](docs/easing.html).
|
||||
*
|
||||
* In most cases, you will be using `timing()`. By default, it uses a symmetric
|
||||
* easeInOut curve that conveys the gradual acceleration of an object to full
|
||||
* speed and concludes by gradually decelerating to a stop.
|
||||
*
|
||||
* ### Working with animations
|
||||
*
|
||||
* Animations are started by calling `start()` on your animation. `start()`
|
||||
* takes a completion callback that will be called when the animation is done.
|
||||
* If the animation finished running normally, the completion callback will be
|
||||
* invoked with `{finished: true}`. If the animation is done because `stop()`
|
||||
* was called on it before it could finish (e.g. because it was interrupted by a
|
||||
* gesture or another animation), then it will receive `{finished: false}`.
|
||||
*
|
||||
* ### Using the native driver
|
||||
*
|
||||
* By using the native driver, we send everything about the animation to native
|
||||
* before starting the animation, allowing native code to perform the animation
|
||||
* on the UI thread without having to go through the bridge on every frame.
|
||||
* Once the animation has started, the JS thread can be blocked without
|
||||
* affecting the animation.
|
||||
*
|
||||
* You can use the native driver by specifying `useNativeDriver: true` in your
|
||||
* animation configuration. See the
|
||||
* [Animations](docs/animations.html#using-the-native-driver) guide to learn
|
||||
* more.
|
||||
*
|
||||
* ### Animatable components
|
||||
*
|
||||
* Only animatable components can be animated. These special components do the
|
||||
* magic of binding the animated values to the properties, and do targeted
|
||||
* native updates to avoid the cost of the react render and reconciliation
|
||||
* process on every frame. They also handle cleanup on unmount so they are safe
|
||||
* by default.
|
||||
*
|
||||
* - [`createAnimatedComponent()`](docs/animated.html#createanimatedcomponent)
|
||||
* can be used to make a component animatable.
|
||||
*
|
||||
* `Animated` exports the following animatable components using the above
|
||||
* wrapper:
|
||||
*
|
||||
* - `Animated.Image`
|
||||
* - `Animated.ScrollView`
|
||||
* - `Animated.Text`
|
||||
* - `Animated.View`
|
||||
*
|
||||
* ### Composing animations
|
||||
*
|
||||
* Animations can also be combined in complex ways using composition functions:
|
||||
*
|
||||
* - [`Animated.delay()`](docs/animated.html#delay) starts an animation after
|
||||
* a given delay.
|
||||
* - [`Animated.parallel()`](docs/animated.html#parallel) starts a number of
|
||||
* animations at the same time.
|
||||
* - [`Animated.sequence()`](docs/animated.html#sequence) starts the animations
|
||||
* in order, waiting for each to complete before starting the next.
|
||||
* - [`Animated.stagger()`](docs/animated.html#stagger) starts animations in
|
||||
* order and in parallel, but with successive delays.
|
||||
*
|
||||
* Animations can also be chained together simply by setting the `toValue` of
|
||||
* one animation to be another `Animated.Value`. See
|
||||
* [Tracking dynamic values](docs/animations.html#tracking-dynamic-values) in
|
||||
* the Animations guide.
|
||||
*
|
||||
* By default, if one animation is stopped or interrupted, then all other
|
||||
* animations in the group are also stopped.
|
||||
*
|
||||
* ### Combining animated values
|
||||
*
|
||||
* You can combine two animated values via addition, multiplication, division,
|
||||
* or modulo to make a new animated value:
|
||||
*
|
||||
* - [`Animated.add()`](docs/animated.html#add)
|
||||
* - [`Animated.divide()`](docs/animated.html#divide)
|
||||
* - [`Animated.modulo()`](docs/animated.html#modulo)
|
||||
* - [`Animated.multiply()`](docs/animated.html#multiply)
|
||||
*
|
||||
* ### Interpolation
|
||||
*
|
||||
* The `interpolate()` function allows input ranges to map to different output
|
||||
* ranges. By default, it will extrapolate the curve beyond the ranges given,
|
||||
* but you can also have it clamp the output value. It uses lineal interpolation
|
||||
* by default but also supports easing functions.
|
||||
*
|
||||
* - [`interpolate()`](docs/animated.html#interpolate)
|
||||
*
|
||||
* Read more about interpolation in the
|
||||
* [Animation](docs/animations.html#interpolation) guide.
|
||||
*
|
||||
* ### Handling gestures and other events
|
||||
*
|
||||
* Gestures, like panning or scrolling, and other events can map directly to
|
||||
* animated values using `Animated.event()`. This is done with a structured map
|
||||
* syntax so that values can be extracted from complex event objects. The first
|
||||
* level is an array to allow mapping across multiple args, and that array
|
||||
* contains nested objects.
|
||||
*
|
||||
* - [`Animated.event()`](docs/animated.html#event)
|
||||
*
|
||||
* For example, when working with horizontal scrolling gestures, you would do
|
||||
* the following in order to map `event.nativeEvent.contentOffset.x` to
|
||||
* `scrollX` (an `Animated.Value`):
|
||||
*
|
||||
* ```javascript
|
||||
* onScroll={Animated.event(
|
||||
* // scrollX = e.nativeEvent.contentOffset.x
|
||||
* [{ nativeEvent: {
|
||||
* contentOffset: {
|
||||
* x: scrollX
|
||||
* }
|
||||
* }
|
||||
* }]
|
||||
* )}
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* Standard value class for driving animations. Typically initialized with
|
||||
* `new Animated.Value(0);`
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#value
|
||||
* See also [`AnimatedValue`](docs/animated.html#animatedvalue).
|
||||
*/
|
||||
Value: AnimatedValue,
|
||||
/**
|
||||
* 2D value class for driving 2D animations, such as pan gestures.
|
||||
*
|
||||
* See https://facebook.github.io/react-native/releases/next/docs/animatedvaluexy.html
|
||||
* See also [`AnimatedValueXY`](docs/animated.html#animatedvaluexy).
|
||||
*/
|
||||
ValueXY: AnimatedValueXY,
|
||||
/**
|
||||
* Exported to use the Interpolation type in flow.
|
||||
* exported to use the Interpolation type in flow
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#interpolation
|
||||
* See also [`AnimatedInterpolation`](docs/animated.html#animatedinterpolation).
|
||||
*/
|
||||
Interpolation: AnimatedInterpolation,
|
||||
/**
|
||||
* Exported for ease of type checking. All animated values derive from this
|
||||
* class.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#node
|
||||
* Exported for ease of type checking. All animated values derive from this class.
|
||||
*/
|
||||
Node: AnimatedNode,
|
||||
|
||||
@@ -544,130 +694,185 @@ module.exports = {
|
||||
* Animates a value from an initial velocity to zero based on a decay
|
||||
* coefficient.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#decay
|
||||
* Config is an object that may have the following options:
|
||||
*
|
||||
* - `velocity`: Initial velocity. Required.
|
||||
* - `deceleration`: Rate of decay. Default 0.997.
|
||||
* - `isInteraction`: Whether or not this animation creates an "interaction handle" on the
|
||||
* `InteractionManager`. Default true.
|
||||
* - `useNativeDriver`: Uses the native driver when true. Default false.
|
||||
*/
|
||||
decay,
|
||||
/**
|
||||
* Animates a value along a timed easing curve. The Easing module has tons of
|
||||
* predefined curves, or you can use your own function.
|
||||
* Animates a value along a timed easing curve. The
|
||||
* [`Easing`](docs/easing.html) module has tons of predefined curves, or you
|
||||
* can use your own function.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#timing
|
||||
* Config is an object that may have the following options:
|
||||
*
|
||||
* - `duration`: Length of animation (milliseconds). Default 500.
|
||||
* - `easing`: Easing function to define curve.
|
||||
* Default is `Easing.inOut(Easing.ease)`.
|
||||
* - `delay`: Start the animation after delay (milliseconds). Default 0.
|
||||
* - `isInteraction`: Whether or not this animation creates an "interaction handle" on the
|
||||
* `InteractionManager`. Default true.
|
||||
* - `useNativeDriver`: Uses the native driver when true. Default false.
|
||||
*/
|
||||
timing,
|
||||
/**
|
||||
* Animates a value according to an analytical spring model based on
|
||||
* damped harmonic oscillation.
|
||||
* [damped harmonic oscillation](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator).
|
||||
* Tracks velocity state to create fluid motions as the `toValue` updates, and
|
||||
* can be chained together.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#spring
|
||||
* Config is an object that may have the following options.
|
||||
*
|
||||
* Note that you can only define one of bounciness/speed, tension/friction, or
|
||||
* stiffness/damping/mass, but not more than one:
|
||||
*
|
||||
* The friction/tension or bounciness/speed options match the spring model in
|
||||
* [Facebook Pop](https://github.com/facebook/pop), [Rebound](http://facebook.github.io/rebound/),
|
||||
* and [Origami](http://origami.design/).
|
||||
*
|
||||
* - `friction`: Controls "bounciness"/overshoot. Default 7.
|
||||
* - `tension`: Controls speed. Default 40.
|
||||
* - `speed`: Controls speed of the animation. Default 12.
|
||||
* - `bounciness`: Controls bounciness. Default 8.
|
||||
*
|
||||
* Specifying stiffness/damping/mass as parameters makes `Animated.spring` use an
|
||||
* analytical spring model based on the motion equations of a [damped harmonic
|
||||
* oscillator](https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator).
|
||||
* This behavior is slightly more precise and faithful to the physics behind
|
||||
* spring dynamics, and closely mimics the implementation in iOS's
|
||||
* CASpringAnimation primitive.
|
||||
*
|
||||
* - `stiffness`: The spring stiffness coefficient. Default 100.
|
||||
* - `damping`: Defines how the spring’s motion should be damped due to the forces of friction.
|
||||
* Default 10.
|
||||
* - `mass`: The mass of the object attached to the end of the spring. Default 1.
|
||||
*
|
||||
* Other configuration options are as follows:
|
||||
*
|
||||
* - `velocity`: The initial velocity of the object attached to the spring. Default 0 (object
|
||||
* is at rest).
|
||||
* - `overshootClamping`: Boolean indiciating whether the spring should be clamped and not
|
||||
* bounce. Default false.
|
||||
* - `restDisplacementThreshold`: The threshold of displacement from rest below which the
|
||||
* spring should be considered at rest. Default 0.001.
|
||||
* - `restSpeedThreshold`: The speed at which the spring should be considered at rest in pixels
|
||||
* per second. Default 0.001.
|
||||
* - `delay`: Start the animation after delay (milliseconds). Default 0.
|
||||
* - `isInteraction`: Whether or not this animation creates an "interaction handle" on the
|
||||
* `InteractionManager`. Default true.
|
||||
* - `useNativeDriver`: Uses the native driver when true. Default false.
|
||||
*/
|
||||
spring,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value composed from two Animated values added
|
||||
* together.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#add
|
||||
*/
|
||||
add,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value composed by dividing the first Animated value
|
||||
* by the second Animated value.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#divide
|
||||
*/
|
||||
divide,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value composed from two Animated values multiplied
|
||||
* together.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#multiply
|
||||
*/
|
||||
multiply,
|
||||
|
||||
/**
|
||||
* Creates a new Animated value that is the (non-negative) modulo of the
|
||||
* provided Animated value.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#modulo
|
||||
* provided Animated value
|
||||
*/
|
||||
modulo,
|
||||
|
||||
/**
|
||||
* Create a new Animated value that is limited between 2 values. It uses the
|
||||
* difference between the last value so even if the value is far from the
|
||||
* bounds it will start changing when the value starts getting closer again.
|
||||
* difference between the last value so even if the value is far from the bounds
|
||||
* it will start changing when the value starts getting closer again.
|
||||
* (`value = clamp(value + diff, min, max)`).
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#diffclamp
|
||||
* This is useful with scroll events, for example, to show the navbar when
|
||||
* scrolling up and to hide it when scrolling down.
|
||||
*/
|
||||
diffClamp,
|
||||
|
||||
/**
|
||||
* Starts an animation after the given delay.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#delay
|
||||
*/
|
||||
delay,
|
||||
/**
|
||||
* Starts an array of animations in order, waiting for each to complete
|
||||
* before starting the next. If the current running animation is stopped, no
|
||||
* before starting the next. If the current running animation is stopped, no
|
||||
* following animations will be started.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#sequence
|
||||
*/
|
||||
sequence,
|
||||
/**
|
||||
* Starts an array of animations all at the same time. By default, if one
|
||||
* of the animations is stopped, they will all be stopped. You can override
|
||||
* Starts an array of animations all at the same time. By default, if one
|
||||
* of the animations is stopped, they will all be stopped. You can override
|
||||
* this with the `stopTogether` flag.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#parallel
|
||||
*/
|
||||
parallel,
|
||||
/**
|
||||
* Array of animations may run in parallel (overlap), but are started in
|
||||
* sequence with successive delays. Nice for doing trailing effects.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#stagger
|
||||
*/
|
||||
stagger,
|
||||
/**
|
||||
* Loops a given animation continuously, so that each time it reaches the
|
||||
* end, it resets and begins again from the start.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#loop
|
||||
*/
|
||||
* 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: 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,
|
||||
|
||||
/**
|
||||
* Takes an array of mappings and extracts values from each arg accordingly,
|
||||
* then calls `setValue` on the mapped outputs.
|
||||
* then calls `setValue` on the mapped outputs. e.g.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#event
|
||||
*```javascript
|
||||
* onScroll={Animated.event(
|
||||
* [{nativeEvent: {contentOffset: {x: this._scrollX}}}],
|
||||
* {listener: (event) => console.log(event)}, // Optional async listener
|
||||
* )}
|
||||
* ...
|
||||
* onPanResponderMove: Animated.event([
|
||||
* null, // raw event arg ignored
|
||||
* {dx: this._panX}, // gestureState arg
|
||||
{listener: (event, gestureState) => console.log(event, gestureState)}, // Optional async listener
|
||||
* ]),
|
||||
*```
|
||||
*
|
||||
* Config is an object that may have the following options:
|
||||
*
|
||||
* - `listener`: Optional async listener.
|
||||
* - `useNativeDriver`: Uses the native driver when true. Default false.
|
||||
*/
|
||||
event,
|
||||
|
||||
/**
|
||||
* Make any React component Animatable. Used to create `Animated.View`, etc.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#createanimatedcomponent
|
||||
*/
|
||||
createAnimatedComponent,
|
||||
|
||||
/**
|
||||
* Imperative API to attach an animated value to an event on a view. Prefer
|
||||
* using `Animated.event` with `useNativeDrive: true` if possible.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#attachnativeevent
|
||||
* Imperative API to attach an animated value to an event on a view. Prefer using
|
||||
* `Animated.event` with `useNativeDrive: true` if possible.
|
||||
*/
|
||||
attachNativeEvent,
|
||||
|
||||
/**
|
||||
* Advanced imperative API for snooping on animated events that are passed in
|
||||
* through props. Use values directly where possible.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animated.html#forkevent
|
||||
* Advanced imperative API for snooping on animated events that are passed in through props. Use
|
||||
* values directly where possible.
|
||||
*/
|
||||
forkEvent,
|
||||
unforkEvent,
|
||||
|
||||
@@ -62,14 +62,14 @@ class Easing {
|
||||
/**
|
||||
* A stepping function, returns 1 for any positive value of `n`.
|
||||
*/
|
||||
static step0(n: number) {
|
||||
static step0(n) {
|
||||
return n > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A stepping function, returns 1 if `n` is greater than or equal to 1.
|
||||
*/
|
||||
static step1(n: number) {
|
||||
static step1(n) {
|
||||
return n >= 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class Easing {
|
||||
*
|
||||
* http://cubic-bezier.com/#0,0,1,1
|
||||
*/
|
||||
static linear(t: number) {
|
||||
static linear(t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ class Easing {
|
||||
*
|
||||
* http://easings.net/#easeInQuad
|
||||
*/
|
||||
static quad(t: number) {
|
||||
static quad(t) {
|
||||
return t * t;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ class Easing {
|
||||
*
|
||||
* http://easings.net/#easeInCubic
|
||||
*/
|
||||
static cubic(t: number) {
|
||||
static cubic(t) {
|
||||
return t * t * t;
|
||||
}
|
||||
|
||||
@@ -122,8 +122,8 @@ class Easing {
|
||||
* n = 4: http://easings.net/#easeInQuart
|
||||
* n = 5: http://easings.net/#easeInQuint
|
||||
*/
|
||||
static poly(n: number) {
|
||||
return (t: number) => Math.pow(t, n);
|
||||
static poly(n) {
|
||||
return (t) => Math.pow(t, n);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,7 +131,7 @@ class Easing {
|
||||
*
|
||||
* http://easings.net/#easeInSine
|
||||
*/
|
||||
static sin(t: number) {
|
||||
static sin(t) {
|
||||
return 1 - Math.cos(t * Math.PI / 2);
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ class Easing {
|
||||
*
|
||||
* http://easings.net/#easeInCirc
|
||||
*/
|
||||
static circle(t: number) {
|
||||
static circle(t) {
|
||||
return 1 - Math.sqrt(1 - t * t);
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ class Easing {
|
||||
*
|
||||
* http://easings.net/#easeInExpo
|
||||
*/
|
||||
static exp(t: number) {
|
||||
static exp(t) {
|
||||
return Math.pow(2, 10 * (t - 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -147,9 +147,6 @@ const API = {
|
||||
const STYLES_WHITELIST = {
|
||||
opacity: true,
|
||||
transform: true,
|
||||
/* ios styles */
|
||||
shadowOpacity: true,
|
||||
shadowRadius: true,
|
||||
/* legacy android transform properties */
|
||||
scaleX: true,
|
||||
scaleY: true,
|
||||
@@ -169,33 +166,11 @@ const TRANSFORM_WHITELIST = {
|
||||
perspective: true,
|
||||
};
|
||||
|
||||
const SUPPORTED_INTERPOLATION_PARAMS = {
|
||||
inputRange: true,
|
||||
outputRange: true,
|
||||
extrapolate: true,
|
||||
extrapolateRight: true,
|
||||
extrapolateLeft: true,
|
||||
};
|
||||
|
||||
function addWhitelistedStyleProp(prop: string): void {
|
||||
STYLES_WHITELIST[prop] = true;
|
||||
}
|
||||
|
||||
function addWhitelistedTransformProp(prop: string): void {
|
||||
TRANSFORM_WHITELIST[prop] = true;
|
||||
}
|
||||
|
||||
function addWhitelistedInterpolationParam(param: string): void {
|
||||
SUPPORTED_INTERPOLATION_PARAMS[param] = true;
|
||||
}
|
||||
|
||||
function validateTransform(configs: Array<Object>): void {
|
||||
configs.forEach(config => {
|
||||
if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) {
|
||||
throw new Error(
|
||||
`Property '${
|
||||
config.property
|
||||
}' is not supported by native animated module`,
|
||||
`Property '${config.property}' is not supported by native animated module`,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -212,6 +187,13 @@ function validateStyles(styles: Object): void {
|
||||
}
|
||||
|
||||
function validateInterpolation(config: Object): void {
|
||||
var SUPPORTED_INTERPOLATION_PARAMS = {
|
||||
inputRange: true,
|
||||
outputRange: true,
|
||||
extrapolate: true,
|
||||
extrapolateRight: true,
|
||||
extrapolateLeft: true,
|
||||
};
|
||||
for (var key in config) {
|
||||
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
|
||||
throw new Error(
|
||||
@@ -255,9 +237,6 @@ function shouldUseNativeDriver(config: AnimationConfig | EventConfig): boolean {
|
||||
|
||||
module.exports = {
|
||||
API,
|
||||
addWhitelistedStyleProp,
|
||||
addWhitelistedTransformProp,
|
||||
addWhitelistedInterpolationParam,
|
||||
validateStyles,
|
||||
validateTransform,
|
||||
validateInterpolation,
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@@ -371,7 +369,7 @@ describe('Animated tests', () => {
|
||||
loop.start(cb);
|
||||
|
||||
expect(animation.start).not.toBeCalled();
|
||||
expect(cb).toBeCalledWith({finished: true});
|
||||
expect(cb).toBeCalledWith({ finished: true });
|
||||
});
|
||||
|
||||
it('supports interrupting an indefinite loop', () => {
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@@ -80,7 +78,7 @@ describe('Native Animated', () => {
|
||||
|
||||
anim.setValue(0.5);
|
||||
|
||||
expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(expect.any(Number), 0.5);
|
||||
expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(jasmine.any(Number), 0.5);
|
||||
expect(c.refs.node.setNativeProps).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -95,12 +93,12 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'value', value: 0, offset: 10},
|
||||
);
|
||||
anim.setOffset(20);
|
||||
expect(nativeAnimatedModule.setAnimatedNodeOffset)
|
||||
.toBeCalledWith(expect.any(Number), 20);
|
||||
.toBeCalledWith(jasmine.any(Number), 20);
|
||||
});
|
||||
|
||||
it('should flatten offset', () => {
|
||||
@@ -113,12 +111,12 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'value', value: 0, offset: 0},
|
||||
);
|
||||
anim.flattenOffset();
|
||||
expect(nativeAnimatedModule.flattenAnimatedNodeOffset)
|
||||
.toBeCalledWith(expect.any(Number));
|
||||
.toBeCalledWith(jasmine.any(Number));
|
||||
});
|
||||
|
||||
it('should extract offset', () => {
|
||||
@@ -131,12 +129,12 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'value', value: 0, offset: 0},
|
||||
);
|
||||
anim.extractOffset();
|
||||
expect(nativeAnimatedModule.extractAnimatedNodeOffset)
|
||||
.toBeCalledWith(expect.any(Number));
|
||||
.toBeCalledWith(jasmine.any(Number));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -214,14 +212,14 @@ describe('Native Animated', () => {
|
||||
);
|
||||
const c = createAndMountComponent(Animated.View, {onTouchMove: event});
|
||||
expect(nativeAnimatedModule.addAnimatedEventToView).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
'onTouchMove',
|
||||
{nativeEventPath: ['state', 'foo'], animatedValueTag: value.__getNativeTag()},
|
||||
);
|
||||
|
||||
c.componentWillUnmount();
|
||||
expect(nativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
'onTouchMove',
|
||||
value.__getNativeTag(),
|
||||
);
|
||||
@@ -271,10 +269,10 @@ describe('Native Animated', () => {
|
||||
expect(nativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1},
|
||||
expect.any(Function)
|
||||
jasmine.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1},
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
expect(nativeAnimatedModule.disconnectAnimatedNodes).toHaveBeenCalledTimes(2);
|
||||
@@ -292,11 +290,11 @@ describe('Native Animated', () => {
|
||||
Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start();
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode)
|
||||
.toBeCalledWith(expect.any(Number), {type: 'value', value: 0, offset: 0});
|
||||
.toBeCalledWith(jasmine.any(Number), {type: 'value', value: 0, offset: 0});
|
||||
expect(nativeAnimatedModule.createAnimatedNode)
|
||||
.toBeCalledWith(expect.any(Number), {type: 'style', style: {opacity: expect.any(Number)}});
|
||||
.toBeCalledWith(jasmine.any(Number), {type: 'style', style: {opacity: jasmine.any(Number)}});
|
||||
expect(nativeAnimatedModule.createAnimatedNode)
|
||||
.toBeCalledWith(expect.any(Number), {type: 'props', props: {style: expect.any(Number)}});
|
||||
.toBeCalledWith(jasmine.any(Number), {type: 'props', props: {style: jasmine.any(Number)}});
|
||||
});
|
||||
|
||||
it('sends a valid graph description for Animated.add nodes', () => {
|
||||
@@ -312,8 +310,8 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'addition', input: expect.any(Array)},
|
||||
jasmine.any(Number),
|
||||
{type: 'addition', input: jasmine.any(Array)},
|
||||
);
|
||||
const additionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
(call) => call[1].type === 'addition'
|
||||
@@ -344,8 +342,8 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'multiplication', input: expect.any(Array)},
|
||||
jasmine.any(Number),
|
||||
{type: 'multiplication', input: jasmine.any(Array)},
|
||||
);
|
||||
const multiplicationCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
(call) => call[1].type === 'multiplication'
|
||||
@@ -376,7 +374,7 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode)
|
||||
.toBeCalledWith(expect.any(Number), {type: 'division', input: expect.any(Array)});
|
||||
.toBeCalledWith(jasmine.any(Number), {type: 'division', input: jasmine.any(Array)});
|
||||
const divisionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
(call) => call[1].type === 'division'
|
||||
);
|
||||
@@ -404,8 +402,8 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'modulus', modulus: 4, input: expect.any(Number)},
|
||||
jasmine.any(Number),
|
||||
{type: 'modulus', modulus: 4, input: jasmine.any(Number)},
|
||||
);
|
||||
const moduloCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
(call) => call[1].type === 'modulus'
|
||||
@@ -435,11 +433,11 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'value', value: 10, offset: 0}
|
||||
);
|
||||
expect(nativeAnimatedModule.createAnimatedNode)
|
||||
.toBeCalledWith(expect.any(Number), {
|
||||
.toBeCalledWith(jasmine.any(Number), {
|
||||
type: 'interpolation',
|
||||
inputRange: [10, 20],
|
||||
outputRange: [0, 1],
|
||||
@@ -466,11 +464,11 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
{
|
||||
type: 'transform',
|
||||
transforms: [{
|
||||
nodeTag: expect.any(Number),
|
||||
nodeTag: jasmine.any(Number),
|
||||
property: 'translateX',
|
||||
type: 'animated',
|
||||
}, {
|
||||
@@ -493,8 +491,8 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
{type: 'diffclamp', input: expect.any(Number), max: 20, min: 0},
|
||||
jasmine.any(Number),
|
||||
{type: 'diffclamp', input: jasmine.any(Number), max: 20, min: 0},
|
||||
);
|
||||
const diffClampCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
|
||||
(call) => call[1].type === 'diffclamp'
|
||||
@@ -570,9 +568,9 @@ describe('Native Animated', () => {
|
||||
});
|
||||
|
||||
expect(nativeAnimatedModule.createAnimatedNode)
|
||||
.toBeCalledWith(expect.any(Number), { type: 'style', style: { opacity: expect.any(Number) }});
|
||||
.toBeCalledWith(jasmine.any(Number), { type: 'style', style: { opacity: jasmine.any(Number) }});
|
||||
expect(nativeAnimatedModule.createAnimatedNode)
|
||||
.toBeCalledWith(expect.any(Number), { type: 'props', props: { style: expect.any(Number) }});
|
||||
.toBeCalledWith(jasmine.any(Number), { type: 'props', props: { style: jasmine.any(Number) }});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -582,10 +580,10 @@ describe('Native Animated', () => {
|
||||
Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start();
|
||||
|
||||
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1},
|
||||
expect.any(Function)
|
||||
jasmine.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1},
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -593,8 +591,8 @@ describe('Native Animated', () => {
|
||||
const anim = new Animated.Value(0);
|
||||
Animated.spring(anim, {toValue: 10, friction: 5, tension: 164, useNativeDriver: true}).start();
|
||||
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
jasmine.any(Number),
|
||||
{
|
||||
type: 'spring',
|
||||
stiffness: 679.08,
|
||||
@@ -607,7 +605,7 @@ describe('Native Animated', () => {
|
||||
toValue: 10,
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function)
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
Animated.spring(anim, {
|
||||
@@ -618,8 +616,8 @@ describe('Native Animated', () => {
|
||||
useNativeDriver: true
|
||||
}).start();
|
||||
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
jasmine.any(Number),
|
||||
{
|
||||
type: 'spring',
|
||||
stiffness: 1000,
|
||||
@@ -632,13 +630,13 @@ describe('Native Animated', () => {
|
||||
toValue: 10,
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function)
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
Animated.spring(anim, {toValue: 10, bounciness: 8, speed: 10, useNativeDriver: true}).start();
|
||||
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
jasmine.any(Number),
|
||||
{
|
||||
type: 'spring',
|
||||
damping: 23.05223140901191,
|
||||
@@ -651,7 +649,7 @@ describe('Native Animated', () => {
|
||||
toValue: 10,
|
||||
iterations: 1,
|
||||
},
|
||||
expect.any(Function)
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -660,10 +658,10 @@ describe('Native Animated', () => {
|
||||
Animated.decay(anim, {velocity: 10, deceleration: 0.1, useNativeDriver: true}).start();
|
||||
|
||||
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 1},
|
||||
expect.any(Function)
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -675,10 +673,10 @@ describe('Native Animated', () => {
|
||||
).start();
|
||||
|
||||
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
jasmine.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 10},
|
||||
expect.any(Function)
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -688,10 +686,10 @@ describe('Native Animated', () => {
|
||||
|
||||
animation.start();
|
||||
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
|
||||
expect.any(Number),
|
||||
expect.any(Number),
|
||||
{type: 'frames', frames: expect.any(Array), toValue: expect.any(Number), iterations: 1},
|
||||
expect.any(Function)
|
||||
jasmine.any(Number),
|
||||
jasmine.any(Number),
|
||||
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number), iterations: 1},
|
||||
jasmine.any(Function)
|
||||
);
|
||||
const animationId = nativeAnimatedModule.startAnimatingNode.mock.calls[0][0];
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -2,135 +2,102 @@
|
||||
* BezierEasing - use bezier curve for transition easing function
|
||||
* https://github.com/gre/bezier-easing
|
||||
*
|
||||
* @copyright 2014-2015 Gaetan Renaudeau. MIT License.
|
||||
* @copyright 2014-2015 Gaëtan Renaudeau. MIT License.
|
||||
* @noflow
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
'use strict';
|
||||
|
||||
var bezier = require('bezier');
|
||||
|
||||
var identity = function(x) {
|
||||
return x;
|
||||
};
|
||||
var identity = function (x) { return x; };
|
||||
|
||||
function assertClose(a, b, precision = 3) {
|
||||
function assertClose (a, b, precision = 3) {
|
||||
expect(a).toBeCloseTo(b, precision);
|
||||
}
|
||||
|
||||
function makeAssertCloseWithPrecision(precision) {
|
||||
return function(a, b) {
|
||||
function makeAssertCloseWithPrecision (precision) {
|
||||
return function (a, b) {
|
||||
assertClose(a, b, precision);
|
||||
};
|
||||
}
|
||||
|
||||
function allEquals(be1, be2, samples, assertion) {
|
||||
if (!assertion) {
|
||||
assertion = assertClose;
|
||||
}
|
||||
for (var i = 0; i <= samples; ++i) {
|
||||
function allEquals (be1, be2, samples, assertion) {
|
||||
if (!assertion) assertion = assertClose;
|
||||
for (var i=0; i<=samples; ++i) {
|
||||
var x = i / samples;
|
||||
assertion(be1(x), be2(x));
|
||||
}
|
||||
}
|
||||
|
||||
function repeat(n) {
|
||||
return function(f) {
|
||||
for (var i = 0; i < n; ++i) {
|
||||
f(i);
|
||||
}
|
||||
function repeat (n) {
|
||||
return function (f) {
|
||||
for (var i=0; i<n; ++i) f(i);
|
||||
};
|
||||
}
|
||||
|
||||
describe('bezier', function() {
|
||||
it('should be a function', function() {
|
||||
describe('bezier', function(){
|
||||
it('should be a function', function(){
|
||||
expect(typeof bezier === 'function').toBe(true);
|
||||
});
|
||||
it('should creates an object', function() {
|
||||
it('should creates an object', function(){
|
||||
expect(typeof bezier(0, 0, 1, 1) === 'function').toBe(true);
|
||||
});
|
||||
it('should fail with wrong arguments', function() {
|
||||
expect(function() {
|
||||
bezier(0.5, 0.5, -5, 0.5);
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
bezier(0.5, 0.5, 5, 0.5);
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
bezier(-2, 0.5, 0.5, 0.5);
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
bezier(2, 0.5, 0.5, 0.5);
|
||||
}).toThrow();
|
||||
it('should fail with wrong arguments', function () {
|
||||
expect(function () { bezier(0.5, 0.5, -5, 0.5); }).toThrow();
|
||||
expect(function () { bezier(0.5, 0.5, 5, 0.5); }).toThrow();
|
||||
expect(function () { bezier(-2, 0.5, 0.5, 0.5); }).toThrow();
|
||||
expect(function () { bezier(2, 0.5, 0.5, 0.5); }).toThrow();
|
||||
});
|
||||
describe('linear curves', function() {
|
||||
it('should be linear', function() {
|
||||
describe('linear curves', function () {
|
||||
it('should be linear', function () {
|
||||
allEquals(bezier(0, 0, 1, 1), bezier(1, 1, 0, 0), 100);
|
||||
allEquals(bezier(0, 0, 1, 1), identity, 100);
|
||||
});
|
||||
});
|
||||
describe('common properties', function() {
|
||||
it('should be the right value at extremes', function() {
|
||||
repeat(10)(function() {
|
||||
var a = Math.random(),
|
||||
b = 2 * Math.random() - 0.5,
|
||||
c = Math.random(),
|
||||
d = 2 * Math.random() - 0.5;
|
||||
describe('common properties', function () {
|
||||
it('should be the right value at extremes', function () {
|
||||
repeat(10)(function () {
|
||||
var a = Math.random(), b = 2*Math.random()-0.5, c = Math.random(), d = 2*Math.random()-0.5;
|
||||
var easing = bezier(a, b, c, d);
|
||||
expect(easing(0)).toBe(0);
|
||||
expect(easing(1)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should approach the projected value of its x=y projected curve', function() {
|
||||
repeat(10)(function() {
|
||||
var a = Math.random(),
|
||||
b = Math.random(),
|
||||
c = Math.random(),
|
||||
d = Math.random();
|
||||
it('should approach the projected value of its x=y projected curve', function () {
|
||||
repeat(10)(function () {
|
||||
var a = Math.random(), b = Math.random(), c = Math.random(), d = Math.random();
|
||||
var easing = bezier(a, b, c, d);
|
||||
var projected = bezier(b, a, d, c);
|
||||
var composed = function(x) {
|
||||
return projected(easing(x));
|
||||
};
|
||||
var composed = function (x) { return projected(easing(x)); };
|
||||
allEquals(identity, composed, 100, makeAssertCloseWithPrecision(2));
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('two same instances', function() {
|
||||
it('should be strictly equals', function() {
|
||||
repeat(10)(function() {
|
||||
var a = Math.random(),
|
||||
b = 2 * Math.random() - 0.5,
|
||||
c = Math.random(),
|
||||
d = 2 * Math.random() - 0.5;
|
||||
describe('two same instances', function () {
|
||||
it('should be strictly equals', function () {
|
||||
repeat(10)(function () {
|
||||
var a = Math.random(), b = 2*Math.random()-0.5, c = Math.random(), d = 2*Math.random()-0.5;
|
||||
allEquals(bezier(a, b, c, d), bezier(a, b, c, d), 100, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('symetric curves', function() {
|
||||
it('should have a central value y~=0.5 at x=0.5', function() {
|
||||
repeat(10)(function() {
|
||||
var a = Math.random(),
|
||||
b = 2 * Math.random() - 0.5,
|
||||
c = 1 - a,
|
||||
d = 1 - b;
|
||||
describe('symetric curves', function () {
|
||||
it('should have a central value y~=0.5 at x=0.5', function () {
|
||||
repeat(10)(function () {
|
||||
var a = Math.random(), b = 2*Math.random()-0.5, c = 1-a, d = 1-b;
|
||||
var easing = bezier(a, b, c, d);
|
||||
assertClose(easing(0.5), 0.5, 2);
|
||||
});
|
||||
});
|
||||
it('should be symetrical', function() {
|
||||
repeat(10)(function() {
|
||||
var a = Math.random(),
|
||||
b = 2 * Math.random() - 0.5,
|
||||
c = 1 - a,
|
||||
d = 1 - b;
|
||||
it('should be symetrical', function () {
|
||||
repeat(10)(function () {
|
||||
var a = Math.random(), b = 2*Math.random()-0.5, c = 1-a, d = 1-b;
|
||||
var easing = bezier(a, b, c, d);
|
||||
var sym = function(x) {
|
||||
return 1 - easing(1 - x);
|
||||
};
|
||||
var sym = function (x) { return 1 - easing(1-x); };
|
||||
allEquals(easing, sym, 100, makeAssertCloseWithPrecision(2));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,6 @@ const ViewStylePropTypes = require('ViewStylePropTypes');
|
||||
function createAnimatedComponent(Component: any): any {
|
||||
class AnimatedComponent extends React.Component<Object> {
|
||||
_component: any;
|
||||
_invokeAnimatedPropsCallbackOnMount: boolean = false;
|
||||
_prevComponent: any;
|
||||
_propsAnimated: AnimatedProps;
|
||||
_eventDetachers: Array<Function> = [];
|
||||
@@ -47,11 +46,6 @@ function createAnimatedComponent(Component: any): any {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this._invokeAnimatedPropsCallbackOnMount) {
|
||||
this._invokeAnimatedPropsCallbackOnMount = false;
|
||||
this._animatedPropsCallback();
|
||||
}
|
||||
|
||||
this._propsAnimated.setNativeView(this._component);
|
||||
this._attachNativeEvents();
|
||||
}
|
||||
@@ -77,44 +71,37 @@ function createAnimatedComponent(Component: any): any {
|
||||
this._eventDetachers = [];
|
||||
}
|
||||
|
||||
// The system is best designed when setNativeProps is implemented. It is
|
||||
// able to avoid re-rendering and directly set the attributes that changed.
|
||||
// However, setNativeProps can only be implemented on leaf native
|
||||
// components. If you want to animate a composite component, you need to
|
||||
// re-render it. In this case, we have a fallback that uses forceUpdate.
|
||||
_animatedPropsCallback = () => {
|
||||
if (this._component == null) {
|
||||
// AnimatedProps is created in will-mount because it's used in render.
|
||||
// But this callback may be invoked before mount in async mode,
|
||||
// In which case we should defer the setNativeProps() call.
|
||||
// React may throw away uncommitted work in async mode,
|
||||
// So a deferred call won't always be invoked.
|
||||
this._invokeAnimatedPropsCallbackOnMount = true;
|
||||
} else if (
|
||||
AnimatedComponent.__skipSetNativeProps_FOR_TESTS_ONLY ||
|
||||
typeof this._component.setNativeProps !== 'function'
|
||||
) {
|
||||
this.forceUpdate();
|
||||
} else if (!this._propsAnimated.__isNative) {
|
||||
this._component.setNativeProps(
|
||||
this._propsAnimated.__getAnimatedValue(),
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Attempting to run JS driven animation on animated ' +
|
||||
'node that has been moved to "native" earlier by starting an ' +
|
||||
'animation with `useNativeDriver: true`',
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
_attachProps(nextProps) {
|
||||
const oldPropsAnimated = this._propsAnimated;
|
||||
|
||||
this._propsAnimated = new AnimatedProps(
|
||||
nextProps,
|
||||
this._animatedPropsCallback,
|
||||
);
|
||||
// The system is best designed when setNativeProps is implemented. It is
|
||||
// able to avoid re-rendering and directly set the attributes that
|
||||
// changed. However, setNativeProps can only be implemented on leaf
|
||||
// native components. If you want to animate a composite component, you
|
||||
// need to re-render it. In this case, we have a fallback that uses
|
||||
// forceUpdate.
|
||||
const callback = () => {
|
||||
if (
|
||||
!AnimatedComponent.__skipSetNativeProps_FOR_TESTS_ONLY &&
|
||||
this._component.setNativeProps
|
||||
) {
|
||||
if (!this._propsAnimated.__isNative) {
|
||||
this._component.setNativeProps(
|
||||
this._propsAnimated.__getAnimatedValue(),
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Attempting to run JS driven animation on animated ' +
|
||||
'node that has been moved to "native" earlier by starting an ' +
|
||||
'animation with `useNativeDriver: true`',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
this._propsAnimated = new AnimatedProps(nextProps, callback);
|
||||
|
||||
// When you call detach, it removes the element from the parent list
|
||||
// of children. If it goes to 0, then the parent also detaches itself
|
||||
@@ -170,7 +157,13 @@ function createAnimatedComponent(Component: any): any {
|
||||
}
|
||||
}
|
||||
|
||||
const propTypes = Component.propTypes;
|
||||
// ReactNative `View.propTypes` have been deprecated in favor of
|
||||
// `ViewPropTypes`. In their place a temporary getter has been added with a
|
||||
// deprecated warning message. Avoid triggering that warning here by using
|
||||
// temporary workaround, __propTypesSecretDontUseThesePlease.
|
||||
// TODO (bvaughn) Revert this particular change any time after April 1
|
||||
const propTypes =
|
||||
Component.__propTypesSecretDontUseThesePlease || Component.propTypes;
|
||||
|
||||
AnimatedComponent.propTypes = {
|
||||
style: function(props, propName, componentName) {
|
||||
|
||||
@@ -68,8 +68,6 @@ function _flush(rootNode: AnimatedValue): void {
|
||||
* multiple properties in a synchronized fashion, but can only be driven by one
|
||||
* mechanism at a time. Using a new mechanism (e.g. starting a new animation,
|
||||
* or calling `setValue`) will stop any previous ones.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html
|
||||
*/
|
||||
class AnimatedValue extends AnimatedWithChildren {
|
||||
_value: number;
|
||||
@@ -108,8 +106,6 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
/**
|
||||
* Directly set the value. This will stop any animations running on the value
|
||||
* and update all the bound properties.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#setvalue
|
||||
*/
|
||||
setValue(value: number): void {
|
||||
if (this._animation) {
|
||||
@@ -129,8 +125,6 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
* Sets an offset that is applied on top of whatever value is set, whether via
|
||||
* `setValue`, an animation, or `Animated.event`. Useful for compensating
|
||||
* things like the start of a pan gesture.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#setoffset
|
||||
*/
|
||||
setOffset(offset: number): void {
|
||||
this._offset = offset;
|
||||
@@ -142,8 +136,6 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
/**
|
||||
* Merges the offset value into the base value and resets the offset to zero.
|
||||
* The final output of the value is unchanged.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#flattenoffset
|
||||
*/
|
||||
flattenOffset(): void {
|
||||
this._value += this._offset;
|
||||
@@ -156,8 +148,6 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
/**
|
||||
* Sets the offset value to the base value, and resets the base value to zero.
|
||||
* The final output of the value is unchanged.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#extractoffset
|
||||
*/
|
||||
extractOffset(): void {
|
||||
this._offset += this._value;
|
||||
@@ -171,8 +161,6 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
* Adds an asynchronous listener to the value so you can observe updates from
|
||||
* animations. This is useful because there is no way to
|
||||
* synchronously read the value because it might be driven natively.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#addlistener
|
||||
*/
|
||||
addListener(callback: ValueListenerCallback): string {
|
||||
const id = String(_uniqueId++);
|
||||
@@ -183,12 +171,6 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a listener. The `id` param shall match the identifier
|
||||
* previously returned by `addListener()`.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#removelistener
|
||||
*/
|
||||
removeListener(id: string): void {
|
||||
delete this._listeners[id];
|
||||
if (this.__isNative && Object.keys(this._listeners).length === 0) {
|
||||
@@ -196,11 +178,6 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all registered listeners.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#removealllisteners
|
||||
*/
|
||||
removeAllListeners(): void {
|
||||
this._listeners = {};
|
||||
if (this.__isNative) {
|
||||
@@ -236,11 +213,9 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any running animation or tracking. `callback` is invoked with the
|
||||
* Stops any running animation or tracking. `callback` is invoked with the
|
||||
* final value after stopping the animation, which is useful for updating
|
||||
* state to match the animation position with layout.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#stopanimation
|
||||
*/
|
||||
stopAnimation(callback?: ?(value: number) => void): void {
|
||||
this.stopTracking();
|
||||
@@ -250,10 +225,8 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any animation and resets the value to its original.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#resetanimation
|
||||
*/
|
||||
* Stops any animation and resets the value to its original
|
||||
*/
|
||||
resetAnimation(callback?: ?(value: number) => void): void {
|
||||
this.stopAnimation(callback);
|
||||
this._value = this._startingValue;
|
||||
@@ -270,8 +243,6 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||
/**
|
||||
* Typically only used internally, but could be used by a custom Animation
|
||||
* class.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvalue.html#animate
|
||||
*/
|
||||
animate(animation: Animation, callback: ?EndCallback): void {
|
||||
let handle = null;
|
||||
|
||||
@@ -22,10 +22,44 @@ type ValueXYListenerCallback = (value: {x: number, y: number}) => void;
|
||||
let _uniqueId = 1;
|
||||
|
||||
/**
|
||||
* 2D Value for driving 2D animations, such as pan gestures. Almost identical
|
||||
* API to normal `Animated.Value`, but multiplexed.
|
||||
* 2D Value for driving 2D animations, such as pan gestures. Almost identical
|
||||
* API to normal `Animated.Value`, but multiplexed. Contains two regular
|
||||
* `Animated.Value`s under the hood.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html
|
||||
* #### Example
|
||||
*
|
||||
*```javascript
|
||||
* class DraggableView extends React.Component {
|
||||
* constructor(props) {
|
||||
* super(props);
|
||||
* this.state = {
|
||||
* pan: new Animated.ValueXY(), // inits to zero
|
||||
* };
|
||||
* this.state.panResponder = PanResponder.create({
|
||||
* onStartShouldSetPanResponder: () => true,
|
||||
* onPanResponderMove: Animated.event([null, {
|
||||
* dx: this.state.pan.x, // x,y are Animated.Value
|
||||
* dy: this.state.pan.y,
|
||||
* }]),
|
||||
* onPanResponderRelease: () => {
|
||||
* Animated.spring(
|
||||
* this.state.pan, // Auto-multiplexed
|
||||
* {toValue: {x: 0, y: 0}} // Back to zero
|
||||
* ).start();
|
||||
* },
|
||||
* });
|
||||
* }
|
||||
* render() {
|
||||
* return (
|
||||
* <Animated.View
|
||||
* {...this.state.panResponder.panHandlers}
|
||||
* style={this.state.pan.getLayout()}>
|
||||
* {this.props.children}
|
||||
* </Animated.View>
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
*```
|
||||
*/
|
||||
class AnimatedValueXY extends AnimatedWithChildren {
|
||||
x: AnimatedValue;
|
||||
@@ -52,46 +86,21 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
this._listeners = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly set the value. This will stop any animations running on the value
|
||||
* and update all the bound properties.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#setvalue
|
||||
*/
|
||||
setValue(value: {x: number, y: number}) {
|
||||
this.x.setValue(value.x);
|
||||
this.y.setValue(value.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an offset that is applied on top of whatever value is set, whether
|
||||
* via `setValue`, an animation, or `Animated.event`. Useful for compensating
|
||||
* things like the start of a pan gesture.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#setoffset
|
||||
*/
|
||||
setOffset(offset: {x: number, y: number}) {
|
||||
this.x.setOffset(offset.x);
|
||||
this.y.setOffset(offset.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the offset value into the base value and resets the offset to zero.
|
||||
* The final output of the value is unchanged.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#flattenoffset
|
||||
*/
|
||||
flattenOffset(): void {
|
||||
this.x.flattenOffset();
|
||||
this.y.flattenOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the offset value to the base value, and resets the base value to
|
||||
* zero. The final output of the value is unchanged.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#extractoffset
|
||||
*/
|
||||
extractOffset(): void {
|
||||
this.x.extractOffset();
|
||||
this.y.extractOffset();
|
||||
@@ -104,39 +113,18 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any animation and resets the value to its original.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#resetanimation
|
||||
*/
|
||||
resetAnimation(callback?: (value: {x: number, y: number}) => void): void {
|
||||
this.x.resetAnimation();
|
||||
this.y.resetAnimation();
|
||||
callback && callback(this.__getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any running animation or tracking. `callback` is invoked with the
|
||||
* final value after stopping the animation, which is useful for updating
|
||||
* state to match the animation position with layout.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#stopanimation
|
||||
*/
|
||||
stopAnimation(callback?: (value: {x: number, y: number}) => void): void {
|
||||
this.x.stopAnimation();
|
||||
this.y.stopAnimation();
|
||||
callback && callback(this.__getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an asynchronous listener to the value so you can observe updates from
|
||||
* animations. This is useful because there is no way to synchronously read
|
||||
* the value because it might be driven natively.
|
||||
*
|
||||
* Returns a string that serves as an identifier for the listener.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#addlistener
|
||||
*/
|
||||
addListener(callback: ValueXYListenerCallback): string {
|
||||
const id = String(_uniqueId++);
|
||||
const jointCallback = ({value: number}) => {
|
||||
@@ -149,23 +137,12 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a listener. The `id` param shall match the identifier
|
||||
* previously returned by `addListener()`.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#removelistener
|
||||
*/
|
||||
removeListener(id: string): void {
|
||||
this.x.removeListener(this._listeners[id].x);
|
||||
this.y.removeListener(this._listeners[id].y);
|
||||
delete this._listeners[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all registered listeners.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#removealllisteners
|
||||
*/
|
||||
removeAllListeners(): void {
|
||||
this.x.removeAllListeners();
|
||||
this.y.removeAllListeners();
|
||||
@@ -173,9 +150,11 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `{x, y}` into `{left, top}` for use in style.
|
||||
* Converts `{x, y}` into `{left, top}` for use in style, e.g.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#getlayout
|
||||
*```javascript
|
||||
* style={this.state.anim.getLayout()}
|
||||
*```
|
||||
*/
|
||||
getLayout(): {[key: string]: AnimatedValue} {
|
||||
return {
|
||||
@@ -185,9 +164,13 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `{x, y}` into a useable translation transform.
|
||||
* Converts `{x, y}` into a useable translation transform, e.g.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/animatedvaluexy.html#gettranslatetransform
|
||||
*```javascript
|
||||
* style={{
|
||||
* transform: this.state.anim.getTranslateTransform()
|
||||
* }}
|
||||
*```
|
||||
*/
|
||||
getTranslateTransform(): Array<{[key: string]: AnimatedValue}> {
|
||||
return [{translateX: this.x}, {translateY: this.y}];
|
||||
|
||||
@@ -23,8 +23,66 @@ const invariant = require('fbjs/lib/invariant');
|
||||
* `AppState` can tell you if the app is in the foreground or background,
|
||||
* and notify you when the state changes.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/appstate.html
|
||||
* AppState is frequently used to determine the intent and proper behavior when
|
||||
* handling push notifications.
|
||||
*
|
||||
* ### App States
|
||||
*
|
||||
* - `active` - The app is running in the foreground
|
||||
* - `background` - The app is running in the background. The user is either
|
||||
* in another app or on the home screen
|
||||
* - `inactive` - This is a state that occurs when transitioning between
|
||||
* foreground & background, and during periods of inactivity such as
|
||||
* entering the Multitasking view or in the event of an incoming call
|
||||
*
|
||||
* For more information, see
|
||||
* [Apple's documentation](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html)
|
||||
*
|
||||
* ### Basic Usage
|
||||
*
|
||||
* To see the current state, you can check `AppState.currentState`, which
|
||||
* will be kept up-to-date. However, `currentState` will be null at launch
|
||||
* while `AppState` retrieves it over the bridge.
|
||||
*
|
||||
* ```
|
||||
* import React, {Component} from 'react'
|
||||
* import {AppState, Text} from 'react-native'
|
||||
*
|
||||
* class AppStateExample extends Component {
|
||||
*
|
||||
* state = {
|
||||
* appState: AppState.currentState
|
||||
* }
|
||||
*
|
||||
* componentDidMount() {
|
||||
* AppState.addEventListener('change', this._handleAppStateChange);
|
||||
* }
|
||||
*
|
||||
* componentWillUnmount() {
|
||||
* AppState.removeEventListener('change', this._handleAppStateChange);
|
||||
* }
|
||||
*
|
||||
* _handleAppStateChange = (nextAppState) => {
|
||||
* if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
|
||||
* console.log('App has come to the foreground!')
|
||||
* }
|
||||
* this.setState({appState: nextAppState});
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* return (
|
||||
* <Text>Current state is: {this.state.appState}</Text>
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This example will only ever appear to say "Current state is: active" because
|
||||
* the app is only visible to the user when in the `active` state, and the null
|
||||
* state will happen only momentarily.
|
||||
*/
|
||||
|
||||
class AppState extends NativeEventEmitter {
|
||||
|
||||
_eventHandlers: Object;
|
||||
@@ -46,10 +104,10 @@ class AppState extends NativeEventEmitter {
|
||||
|
||||
let eventUpdated = false;
|
||||
|
||||
// TODO: this is a terrible solution - in order to ensure `currentState`
|
||||
// prop is up to date, we have to register an observer that updates it
|
||||
// whenever the state changes, even if nobody cares. We should just
|
||||
// deprecate the `currentState` property and get rid of this.
|
||||
// TODO: this is a terrible solution - in order to ensure `currentState` prop
|
||||
// is up to date, we have to register an observer that updates it whenever
|
||||
// the state changes, even if nobody cares. We should just deprecate the
|
||||
// `currentState` property and get rid of this.
|
||||
this.addListener(
|
||||
'appStateDidChange',
|
||||
(appStateData) => {
|
||||
@@ -59,8 +117,8 @@ class AppState extends NativeEventEmitter {
|
||||
);
|
||||
|
||||
// TODO: see above - this request just populates the value of `currentState`
|
||||
// when the module is first initialized. Would be better to get rid of the
|
||||
// prop and expose `getCurrentAppState` method directly.
|
||||
// when the module is first initialized. Would be better to get rid of the prop
|
||||
// and expose `getCurrentAppState` method directly.
|
||||
RCTAppState.getCurrentAppState(
|
||||
(appStateData) => {
|
||||
if (!eventUpdated) {
|
||||
@@ -71,16 +129,15 @@ class AppState extends NativeEventEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: now that AppState is a subclass of NativeEventEmitter, we could
|
||||
// deprecate `addEventListener` and `removeEventListener` and just use
|
||||
// addListener` and `listener.remove()` directly. That will be a breaking
|
||||
// change though, as both the method and event names are different
|
||||
// (addListener events are currently required to be globally unique).
|
||||
/**
|
||||
/**
|
||||
* Add a handler to AppState changes by listening to the `change` event type
|
||||
* and providing the handler.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/appstate.html#addeventlistener
|
||||
* and providing the handler
|
||||
*
|
||||
* TODO: now that AppState is a subclass of NativeEventEmitter, we could deprecate
|
||||
* `addEventListener` and `removeEventListener` and just use `addListener` and
|
||||
* `listener.remove()` directly. That will be a breaking change though, as both
|
||||
* the method and event names are different (addListener events are currently
|
||||
* required to be globally unique).
|
||||
*/
|
||||
addEventListener(
|
||||
type: string,
|
||||
@@ -106,9 +163,7 @@ class AppState extends NativeEventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a handler by passing the `change` event type and the handler.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/appstate.html#removeeventlistener
|
||||
* Remove a handler by passing the `change` event type and the handler
|
||||
*/
|
||||
removeEventListener(
|
||||
type: string,
|
||||
@@ -145,10 +200,9 @@ if (__DEV__ && !RCTAppState) {
|
||||
}
|
||||
}
|
||||
|
||||
// This module depends on the native `RCTAppState` module. If you don't
|
||||
// include it, `AppState.isAvailable` will return `false`, and any method
|
||||
// calls will throw. We reassign the class variable to keep the autodoc
|
||||
// generator happy.
|
||||
// This module depends on the native `RCTAppState` module. If you don't include it,
|
||||
// `AppState.isAvailable` will return `false`, and any method calls will throw.
|
||||
// We reassign the class variable to keep the autodoc generator happy.
|
||||
AppState = new MissingNativeAppStateShim();
|
||||
} else {
|
||||
AppState = new AppState();
|
||||
|
||||
@@ -12,21 +12,7 @@
|
||||
'use strict';
|
||||
|
||||
const MessageQueue = require('MessageQueue');
|
||||
|
||||
// MessageQueue can install a global handler to catch all exceptions where JS users can register their own behavior
|
||||
// This handler makes all exceptions to be handled inside MessageQueue rather than by the VM at its origin
|
||||
// This makes stacktraces to be placed at MessageQueue rather than at where they were launched
|
||||
// The parameter __fbUninstallRNGlobalErrorHandler is passed to MessageQueue to prevent the handler from being installed
|
||||
//
|
||||
// __fbUninstallRNGlobalErrorHandler is conditionally set by the Inspector while the VM is paused for intialization
|
||||
// If the Inspector isn't present it defaults to undefined and the global error handler is installed
|
||||
// The Inspector can still call MessageQueue#uninstallGlobalErrorHandler to uninstalled on attach
|
||||
|
||||
const BatchedBridge = new MessageQueue(
|
||||
// $FlowFixMe
|
||||
typeof __fbUninstallRNGlobalErrorHandler !== 'undefined' &&
|
||||
__fbUninstallRNGlobalErrorHandler === true, // eslint-disable-line no-undef
|
||||
);
|
||||
const BatchedBridge = new MessageQueue();
|
||||
|
||||
// Wire up the batched bridge on the global object so that we can call into it.
|
||||
// Ideally, this would be the inverse relationship. I.e. the native environment
|
||||
|
||||
@@ -59,9 +59,7 @@ class MessageQueue {
|
||||
|
||||
__spy: ?(data: SpyData) => void;
|
||||
|
||||
__guard: (() => void) => void;
|
||||
|
||||
constructor(shouldUninstallGlobalErrorHandler: boolean = false) {
|
||||
constructor() {
|
||||
this._lazyCallableModules = {};
|
||||
this._queue = [[], [], [], 0];
|
||||
this._successCallbacks = [];
|
||||
@@ -69,11 +67,6 @@ class MessageQueue {
|
||||
this._callID = 0;
|
||||
this._lastFlush = 0;
|
||||
this._eventLoopStartTime = new Date().getTime();
|
||||
if (shouldUninstallGlobalErrorHandler) {
|
||||
this.uninstallGlobalErrorHandler();
|
||||
} else {
|
||||
this.installGlobalErrorHandler();
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
this._debugInfo = {};
|
||||
@@ -215,40 +208,8 @@ class MessageQueue {
|
||||
this._queue[METHOD_IDS].push(methodID);
|
||||
|
||||
if (__DEV__) {
|
||||
// Validate that parameters passed over the bridge are
|
||||
// folly-convertible. As a special case, if a prop value is a
|
||||
// function it is permitted here, and special-cased in the
|
||||
// conversion.
|
||||
const isValidArgument = val => {
|
||||
const t = typeof val;
|
||||
if (
|
||||
t === 'undefined' ||
|
||||
t === 'null' ||
|
||||
t === 'boolean' ||
|
||||
t === 'number' ||
|
||||
t === 'string'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (t === 'function' || t !== 'object') {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
return val.every(isValidArgument);
|
||||
}
|
||||
for (const k in val) {
|
||||
if (typeof val[k] !== 'function' && !isValidArgument(val[k])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
invariant(
|
||||
isValidArgument(params),
|
||||
'%s is not usable as a native method argument',
|
||||
params,
|
||||
);
|
||||
// Any params sent over the bridge should be encodable as JSON
|
||||
JSON.stringify(params);
|
||||
|
||||
// The params object should not be mutated after being queued
|
||||
deepFreezeAndThrowOnMutationInDev((params: any));
|
||||
@@ -291,26 +252,11 @@ class MessageQueue {
|
||||
}
|
||||
}
|
||||
|
||||
uninstallGlobalErrorHandler() {
|
||||
this.__guard = this.__guardUnsafe;
|
||||
}
|
||||
|
||||
installGlobalErrorHandler() {
|
||||
this.__guard = this.__guardSafe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private methods
|
||||
*/
|
||||
|
||||
// Lets exceptions propagate to be handled by the VM at the origin
|
||||
__guardUnsafe(fn: () => void) {
|
||||
this._inCall++;
|
||||
fn();
|
||||
this._inCall--;
|
||||
}
|
||||
|
||||
__guardSafe(fn: () => void) {
|
||||
__guard(fn: () => void) {
|
||||
this._inCall++;
|
||||
try {
|
||||
fn();
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
* @format
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@@ -39,12 +37,10 @@ describe('MessageQueue', function() {
|
||||
queue = new MessageQueue();
|
||||
queue.registerCallableModule(
|
||||
'MessageQueueTestModule',
|
||||
MessageQueueTestModule,
|
||||
MessageQueueTestModule
|
||||
);
|
||||
queue.createDebugLookup(0, 'MessageQueueTestModule', [
|
||||
'testHook1',
|
||||
'testHook2',
|
||||
]);
|
||||
queue.createDebugLookup(0, 'MessageQueueTestModule',
|
||||
['testHook1', 'testHook2']);
|
||||
});
|
||||
|
||||
it('should enqueue native calls', () => {
|
||||
@@ -54,10 +50,10 @@ describe('MessageQueue', function() {
|
||||
});
|
||||
|
||||
it('should call a local function with the function name', () => {
|
||||
MessageQueueTestModule.testHook2 = jest.fn();
|
||||
expect(MessageQueueTestModule.testHook2.mock.calls.length).toEqual(0);
|
||||
MessageQueueTestModule.testHook2 = jasmine.createSpy();
|
||||
expect(MessageQueueTestModule.testHook2.calls.count()).toEqual(0);
|
||||
queue.__callFunction('MessageQueueTestModule', 'testHook2', [2]);
|
||||
expect(MessageQueueTestModule.testHook2.mock.calls.length).toEqual(1);
|
||||
expect(MessageQueueTestModule.testHook2.calls.count()).toEqual(1);
|
||||
});
|
||||
|
||||
it('should store callbacks', () => {
|
||||
@@ -68,15 +64,7 @@ describe('MessageQueue', function() {
|
||||
|
||||
it('should call the stored callback', () => {
|
||||
let done = false;
|
||||
queue.enqueueNativeCall(
|
||||
0,
|
||||
1,
|
||||
[],
|
||||
() => {},
|
||||
() => {
|
||||
done = true;
|
||||
},
|
||||
);
|
||||
queue.enqueueNativeCall(0, 1, [], () => {}, () => { done = true; });
|
||||
queue.__invokeCallback(1, []);
|
||||
expect(done).toEqual(true);
|
||||
});
|
||||
@@ -94,92 +82,32 @@ describe('MessageQueue', function() {
|
||||
});
|
||||
|
||||
it('should throw when calling with unknown module', () => {
|
||||
const unknownModule = 'UnknownModule',
|
||||
unknownMethod = 'UnknownMethod';
|
||||
const unknownModule = 'UnknownModule', unknownMethod = 'UnknownMethod';
|
||||
expect(() => queue.__callFunction(unknownModule, unknownMethod)).toThrow(
|
||||
`Module ${unknownModule} is not a registered callable module (calling ${unknownMethod})`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return lazily registered module', () => {
|
||||
const dummyModule = {},
|
||||
name = 'modulesName';
|
||||
const dummyModule = {}, name = 'modulesName';
|
||||
queue.registerLazyCallableModule(name, () => dummyModule);
|
||||
|
||||
expect(queue.getCallableModule(name)).toEqual(dummyModule);
|
||||
});
|
||||
|
||||
it('should not initialize lazily registered module before it was used for the first time', () => {
|
||||
const dummyModule = {},
|
||||
name = 'modulesName';
|
||||
const dummyModule = {}, name = 'modulesName';
|
||||
const factory = jest.fn(() => dummyModule);
|
||||
queue.registerLazyCallableModule(name, factory);
|
||||
expect(factory).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should initialize lazily registered module only once', () => {
|
||||
const dummyModule = {},
|
||||
name = 'modulesName';
|
||||
const dummyModule = {}, name = 'modulesName';
|
||||
const factory = jest.fn(() => dummyModule);
|
||||
queue.registerLazyCallableModule(name, factory);
|
||||
queue.getCallableModule(name);
|
||||
queue.getCallableModule(name);
|
||||
expect(factory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should catch all exceptions if the global error handler is installed', () => {
|
||||
const errorMessage = 'intentional error';
|
||||
const errorModule = {
|
||||
explode: function() {
|
||||
throw new Error(errorMessage);
|
||||
},
|
||||
};
|
||||
const name = 'errorModuleName';
|
||||
const factory = jest.fn(() => errorModule);
|
||||
queue.__guardSafe = jest.fn(() => {});
|
||||
queue.__guardUnsafe = jest.fn(() => {});
|
||||
queue.installGlobalErrorHandler();
|
||||
queue.registerLazyCallableModule(name, factory);
|
||||
queue.callFunctionReturnFlushedQueue(name, 'explode', []);
|
||||
expect(queue.__guardUnsafe).toHaveBeenCalledTimes(0);
|
||||
expect(queue.__guardSafe).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should propagate exceptions if the global error handler is uninstalled', () => {
|
||||
queue.uninstallGlobalErrorHandler();
|
||||
const errorMessage = 'intentional error';
|
||||
const errorModule = {
|
||||
explode: function() {
|
||||
throw new Error(errorMessage);
|
||||
},
|
||||
};
|
||||
const name = 'errorModuleName';
|
||||
const factory = jest.fn(() => errorModule);
|
||||
queue.__guardUnsafe = jest.fn(() => {});
|
||||
queue.__guardSafe = jest.fn(() => {});
|
||||
queue.registerLazyCallableModule(name, factory);
|
||||
queue.uninstallGlobalErrorHandler();
|
||||
queue.callFunctionReturnFlushedQueue(name, 'explode');
|
||||
expect(queue.__guardUnsafe).toHaveBeenCalledTimes(2);
|
||||
expect(queue.__guardSafe).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should catch all exceptions if the global error handler is re-installed', () => {
|
||||
const errorMessage = 'intentional error';
|
||||
const errorModule = {
|
||||
explode: function() {
|
||||
throw new Error(errorMessage);
|
||||
},
|
||||
};
|
||||
const name = 'errorModuleName';
|
||||
const factory = jest.fn(() => errorModule);
|
||||
queue.__guardUnsafe = jest.fn(() => {});
|
||||
queue.__guardSafe = jest.fn(() => {});
|
||||
queue.registerLazyCallableModule(name, factory);
|
||||
queue.uninstallGlobalErrorHandler();
|
||||
queue.installGlobalErrorHandler();
|
||||
queue.callFunctionReturnFlushedQueue(name, 'explode');
|
||||
expect(queue.__guardUnsafe).toHaveBeenCalledTimes(0);
|
||||
expect(queue.__guardSafe).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@@ -57,8 +56,8 @@ describe('MessageQueue', function() {
|
||||
});
|
||||
|
||||
it('should make round trip and clear memory', function() {
|
||||
const onFail = jest.fn();
|
||||
const onSucc = jest.fn();
|
||||
const onFail = jasmine.createSpy();
|
||||
const onSucc = jasmine.createSpy();
|
||||
|
||||
// Perform communication
|
||||
NativeModules.RemoteModule1.promiseMethod('paloAlto', 'menloPark', onFail, onSucc);
|
||||
@@ -98,8 +97,8 @@ describe('MessageQueue', function() {
|
||||
expect(function() {
|
||||
BatchedBridge.__invokeCallback(firstSuccCBID, ['firstSucc']);
|
||||
}).toThrow();
|
||||
expect(onFail.mock.calls.length).toBe(1);
|
||||
expect(onSucc.mock.calls.length).toBe(0);
|
||||
expect(onFail.calls.count()).toBe(1);
|
||||
expect(onSucc.calls.count()).toBe(0);
|
||||
|
||||
// Handle the second remote invocation by signaling success.
|
||||
BatchedBridge.__invokeCallback(secondSuccCBID, ['secondSucc']);
|
||||
@@ -107,7 +106,7 @@ describe('MessageQueue', function() {
|
||||
expect(function() {
|
||||
BatchedBridge.__invokeCallback(secondFailCBID, ['secondFail']);
|
||||
}).toThrow();
|
||||
expect(onFail.mock.calls.length).toBe(1);
|
||||
expect(onSucc.mock.calls.length).toBe(1);
|
||||
expect(onFail.calls.count()).toBe(1);
|
||||
expect(onSucc.calls.count()).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -59,9 +59,7 @@ class URL {
|
||||
if (BLOB_URL_PREFIX === null) {
|
||||
throw new Error('Cannot create URL for blob!');
|
||||
}
|
||||
return `${BLOB_URL_PREFIX}${blob.blobId}?offset=${blob.offset}&size=${
|
||||
blob.size
|
||||
}`;
|
||||
return `${BLOB_URL_PREFIX}${blob.blobId}?offset=${blob.offset}&size=${blob.size}`;
|
||||
}
|
||||
|
||||
static revokeObjectURL(url: string) {
|
||||
|
||||
@@ -35,7 +35,6 @@ class BugReporting {
|
||||
static _extraSources: Map<string, SourceCallback> = new Map();
|
||||
static _fileSources: Map<string, SourceCallback> = new Map();
|
||||
static _subscription: ?EmitterSubscription = null;
|
||||
static _redboxSubscription: ?EmitterSubscription = null;
|
||||
|
||||
static _maybeInit() {
|
||||
if (!BugReporting._subscription) {
|
||||
@@ -43,11 +42,6 @@ class BugReporting {
|
||||
.addListener('collectBugExtraData', BugReporting.collectExtraData, null);
|
||||
defaultExtras();
|
||||
}
|
||||
|
||||
if (!BugReporting._redboxSubscription) {
|
||||
BugReporting._redboxSubscription = RCTDeviceEventEmitter
|
||||
.addListener('collectRedBoxExtraData', BugReporting.collectExtraData, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,11 +98,6 @@ class BugReporting {
|
||||
BugReportingNativeModule.setExtraData &&
|
||||
BugReportingNativeModule.setExtraData(extraData, fileData);
|
||||
|
||||
const RedBoxNativeModule = require('NativeModules').RedBox;
|
||||
RedBoxNativeModule &&
|
||||
RedBoxNativeModule.setExtraData &&
|
||||
RedBoxNativeModule.setExtraData(extraData, 'From BugReporting.js');
|
||||
|
||||
return { extras: extraData, files: fileData };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,15 +35,6 @@ const ASSET_TYPE_OPTIONS = {
|
||||
Photos: 'Photos',
|
||||
};
|
||||
|
||||
type GetPhotosParams = {
|
||||
first: number,
|
||||
after?: string,
|
||||
groupTypes?: $Keys<typeof GROUP_TYPES_OPTIONS>,
|
||||
groupName?: string,
|
||||
assetType?: $Keys<typeof ASSET_TYPE_OPTIONS>,
|
||||
mimeTypes?: Array<string>,
|
||||
};
|
||||
|
||||
/**
|
||||
* Shape of the param arg for the `getPhotos` function.
|
||||
*/
|
||||
@@ -72,8 +63,8 @@ const getPhotosParamChecker = createStrictShapeTypeChecker({
|
||||
groupName: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Specifies filter on asset type
|
||||
*/
|
||||
* Specifies filter on asset type
|
||||
*/
|
||||
assetType: PropTypes.oneOf(Object.keys(ASSET_TYPE_OPTIONS)),
|
||||
|
||||
/**
|
||||
@@ -82,35 +73,6 @@ const getPhotosParamChecker = createStrictShapeTypeChecker({
|
||||
mimeTypes: PropTypes.arrayOf(PropTypes.string),
|
||||
});
|
||||
|
||||
type GetPhotosReturn = Promise<{
|
||||
edges: Array<{
|
||||
node: {
|
||||
type: string,
|
||||
group_name: string,
|
||||
image: {
|
||||
uri: string,
|
||||
height: number,
|
||||
width: number,
|
||||
isStored?: boolean,
|
||||
playableDuration: number,
|
||||
},
|
||||
timestamp: number,
|
||||
location?: {
|
||||
latitude?: number,
|
||||
longitude?: number,
|
||||
altitude?: number,
|
||||
heading?: number,
|
||||
speed?: number,
|
||||
},
|
||||
},
|
||||
}>,
|
||||
page_info: {
|
||||
has_next_page: boolean,
|
||||
start_cursor?: string,
|
||||
end_cursor?: string,
|
||||
},
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Shape of the return value of the `getPhotos` function.
|
||||
*/
|
||||
@@ -171,10 +133,6 @@ class CameraRoll {
|
||||
return this.saveToCameraRoll(tag, 'photo');
|
||||
}
|
||||
|
||||
static deletePhotos(photos: Array<string>) {
|
||||
return RCTCameraRollManager.deletePhotos(photos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the photo or video to the camera roll / gallery.
|
||||
*
|
||||
@@ -200,8 +158,8 @@ class CameraRoll {
|
||||
|
||||
invariant(
|
||||
type === 'photo' || type === 'video' || type === undefined,
|
||||
`The second argument to saveToCameraRoll must be 'photo' or 'video'. You passed ${type ||
|
||||
'unknown'}`,
|
||||
// $FlowFixMe(>=0.28.0)
|
||||
`The second argument to saveToCameraRoll must be 'photo' or 'video'. You passed ${type}`,
|
||||
);
|
||||
|
||||
let mediaType = 'photo';
|
||||
@@ -257,8 +215,8 @@ class CameraRoll {
|
||||
* - `speed`: {number}
|
||||
* - `page_info` : {object} : An object with the following shape:
|
||||
* - `has_next_page`: {boolean}
|
||||
* - `start_cursor`: {string}
|
||||
* - `end_cursor`: {string}
|
||||
* - `start_cursor`: {boolean}
|
||||
* - `end_cursor`: {boolean}
|
||||
*
|
||||
* Loading images:
|
||||
* ```
|
||||
@@ -297,7 +255,7 @@ class CameraRoll {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
static getPhotos(params: GetPhotosParams): GetPhotosReturn {
|
||||
static getPhotos(params) {
|
||||
if (__DEV__) {
|
||||
checkPropTypes(
|
||||
{params: getPhotosParamChecker},
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Photos/Photos.h>
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTConvert.h>
|
||||
@@ -235,27 +234,6 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deletePhotos:(NSArray<NSString *>*)assets
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSArray<NSURL *> *assets_ = [RCTConvert NSURLArray:assets];
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
PHFetchResult<PHAsset *> *fetched =
|
||||
[PHAsset fetchAssetsWithALAssetURLs:assets_ options:nil];
|
||||
[PHAssetChangeRequest deleteAssets:fetched];
|
||||
}
|
||||
completionHandler:^(BOOL success, NSError *error) {
|
||||
if (success == YES) {
|
||||
resolve(@(success));
|
||||
}
|
||||
else {
|
||||
reject(@"Couldn't delete", @"Couldn't delete assets", error);
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
static void checkPhotoLibraryConfig()
|
||||
{
|
||||
#if RCT_DEV
|
||||
|
||||
@@ -24,16 +24,6 @@ type ChangeEventName = $Enum<{
|
||||
|
||||
var _subscriptions = new Map();
|
||||
|
||||
/**
|
||||
* Sometimes it's useful to know whether or not the device has a screen reader
|
||||
* that is currently active. The `AccessibilityInfo` API is designed for this
|
||||
* purpose. You can use it to query the current state of the screen reader as
|
||||
* well as to register to be notified when the state of the screen reader
|
||||
* changes.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/accessibilityinfo.html
|
||||
*/
|
||||
|
||||
var AccessibilityInfo = {
|
||||
|
||||
fetch: function(): Promise {
|
||||
|
||||
@@ -28,23 +28,61 @@ type ChangeEventName = $Enum<{
|
||||
var _subscriptions = new Map();
|
||||
|
||||
/**
|
||||
* Sometimes it's useful to know whether or not the device has a screen reader
|
||||
* that is currently active. The `AccessibilityInfo` API is designed for this
|
||||
* purpose. You can use it to query the current state of the screen reader as
|
||||
* well as to register to be notified when the state of the screen reader
|
||||
* changes.
|
||||
* Sometimes it's useful to know whether or not the device has a screen reader that is currently active. The
|
||||
* `AccessibilityInfo` API is designed for this purpose. You can use it to query the current state of the
|
||||
* screen reader as well as to register to be notified when the state of the screen reader changes.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/accessibilityinfo.html
|
||||
* Here's a small example illustrating how to use `AccessibilityInfo`:
|
||||
*
|
||||
* ```javascript
|
||||
* class ScreenReaderStatusExample extends React.Component {
|
||||
* state = {
|
||||
* screenReaderEnabled: false,
|
||||
* }
|
||||
*
|
||||
* componentDidMount() {
|
||||
* AccessibilityInfo.addEventListener(
|
||||
* 'change',
|
||||
* this._handleScreenReaderToggled
|
||||
* );
|
||||
* AccessibilityInfo.fetch().done((isEnabled) => {
|
||||
* this.setState({
|
||||
* screenReaderEnabled: isEnabled
|
||||
* });
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* componentWillUnmount() {
|
||||
* AccessibilityInfo.removeEventListener(
|
||||
* 'change',
|
||||
* this._handleScreenReaderToggled
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* _handleScreenReaderToggled = (isEnabled) => {
|
||||
* this.setState({
|
||||
* screenReaderEnabled: isEnabled,
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* return (
|
||||
* <View>
|
||||
* <Text>
|
||||
* The screen reader is {this.state.screenReaderEnabled ? 'enabled' : 'disabled'}.
|
||||
* </Text>
|
||||
* </View>
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
var AccessibilityInfo = {
|
||||
|
||||
/**
|
||||
* Query whether a screen reader is currently enabled.
|
||||
*
|
||||
* Returns a promise which resolves to a boolean.
|
||||
* The result is `true` when a screen reader is enabledand `false` otherwise.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/accessibilityinfo.html#fetch
|
||||
* Query whether a screen reader is currently enabled. Returns a promise which
|
||||
* resolves to a boolean. The result is `true` when a screen reader is enabled
|
||||
* and `false` otherwise.
|
||||
*/
|
||||
fetch: function(): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -62,13 +100,10 @@ var AccessibilityInfo = {
|
||||
* to the event handler is a boolean. The boolean is `true` when a screen
|
||||
* reader is enabled and `false` otherwise.
|
||||
* - `announcementFinished`: iOS-only event. Fires when the screen reader has
|
||||
* finished making an announcement. The argument to the event handler is a
|
||||
* dictionary with these keys:
|
||||
* finished making an announcement. The argument to the event handler is a dictionary
|
||||
* with these keys:
|
||||
* - `announcement`: The string announced by the screen reader.
|
||||
* - `success`: A boolean indicating whether the announcement was
|
||||
* successfully made.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/accessibilityinfo.html#addeventlistener
|
||||
* - `success`: A boolean indicating whether the announcement was successfully made.
|
||||
*/
|
||||
addEventListener: function (
|
||||
eventName: ChangeEventName,
|
||||
@@ -95,11 +130,7 @@ var AccessibilityInfo = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Set accessibility focus to a react component.
|
||||
*
|
||||
* @platform ios
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/accessibilityinfo.html#setaccessibilityfocus
|
||||
* iOS-Only. Set accessibility focus to a react component.
|
||||
*/
|
||||
setAccessibilityFocus: function(
|
||||
reactTag: number
|
||||
@@ -108,11 +139,7 @@ var AccessibilityInfo = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Post a string to be announced by the screen reader.
|
||||
*
|
||||
* @platform ios
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/accessibilityinfo.html#announceforaccessibility
|
||||
* iOS-Only. Post a string to be announced by the screen reader.
|
||||
*/
|
||||
announceForAccessibility: function(
|
||||
announcement: string
|
||||
@@ -122,8 +149,6 @@ var AccessibilityInfo = {
|
||||
|
||||
/**
|
||||
* Remove an event handler.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/accessibilityinfo.html#removeeventlistener
|
||||
*/
|
||||
removeEventListener: function(
|
||||
eventName: ChangeEventName,
|
||||
|
||||
@@ -38,7 +38,45 @@ type DefaultProps = {
|
||||
/**
|
||||
* Displays a circular loading indicator.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/activityindicator.html
|
||||
* ### Example
|
||||
*
|
||||
* ```ReactNativeWebPlayer
|
||||
* import React, { Component } from 'react'
|
||||
* import {
|
||||
* ActivityIndicator,
|
||||
* AppRegistry,
|
||||
* StyleSheet,
|
||||
* Text,
|
||||
* View,
|
||||
* } from 'react-native'
|
||||
*
|
||||
* class App extends Component {
|
||||
* render() {
|
||||
* return (
|
||||
* <View style={[styles.container, styles.horizontal]}>
|
||||
* <ActivityIndicator size="large" color="#0000ff" />
|
||||
* <ActivityIndicator size="small" color="#00ff00" />
|
||||
* <ActivityIndicator size="large" color="#0000ff" />
|
||||
* <ActivityIndicator size="small" color="#00ff00" />
|
||||
* </View>
|
||||
* )
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const styles = StyleSheet.create({
|
||||
* container: {
|
||||
* flex: 1,
|
||||
* justifyContent: 'center'
|
||||
* },
|
||||
* horizontal: {
|
||||
* flexDirection: 'row',
|
||||
* justifyContent: 'space-around',
|
||||
* padding: 10
|
||||
* }
|
||||
* })
|
||||
*
|
||||
* AppRegistry.registerComponent('App', () => App)
|
||||
* ```
|
||||
*/
|
||||
const ActivityIndicator = createReactClass({
|
||||
displayName: 'ActivityIndicator',
|
||||
@@ -48,21 +86,15 @@ const ActivityIndicator = createReactClass({
|
||||
...ViewPropTypes,
|
||||
/**
|
||||
* Whether to show the indicator (true, the default) or hide it (false).
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/activityindicator.html#animating
|
||||
*/
|
||||
animating: PropTypes.bool,
|
||||
/**
|
||||
* The foreground color of the spinner (default is gray).
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/activityindicator.html#color
|
||||
*/
|
||||
color: ColorPropType,
|
||||
/**
|
||||
* Size of the indicator (default is 'small').
|
||||
* Passing a number to the size prop is only supported on Android.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/activityindicator.html#size
|
||||
*/
|
||||
size: PropTypes.oneOfType([
|
||||
PropTypes.oneOf([ 'small', 'large' ]),
|
||||
@@ -72,8 +104,6 @@ const ActivityIndicator = createReactClass({
|
||||
* Whether the indicator should hide when not animating (true by default).
|
||||
*
|
||||
* @platform ios
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/activityindicator.html#hideswhenstopped
|
||||
*/
|
||||
hidesWhenStopped: PropTypes.bool,
|
||||
},
|
||||
|
||||
@@ -34,50 +34,6 @@ type DefaultProps = {
|
||||
* If the `value` prop is not updated, the component will continue to render
|
||||
* the supplied `value` prop instead of the expected result of any user actions.
|
||||
*
|
||||
* ```
|
||||
* import React from 'react';
|
||||
* import { AppRegistry, StyleSheet, Text, View, CheckBox } from 'react-native';
|
||||
*
|
||||
* export default class App extends React.Component {
|
||||
* constructor(props) {
|
||||
* super(props);
|
||||
* this.state = {
|
||||
* checked: false
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* toggle() {
|
||||
* this.setState(({checked}) => {
|
||||
* return {
|
||||
* checked: !checked
|
||||
* };
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* const {checked} = this.state;
|
||||
* return (
|
||||
* <View style={styles.container}>
|
||||
* <Text>Checked</Text>
|
||||
* <CheckBox value={checked} onChange={this.toggle.bind(this)} />
|
||||
* </View>
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const styles = StyleSheet.create({
|
||||
* container: {
|
||||
* flex: 1,
|
||||
* flexDirection: 'row',
|
||||
* alignItems: 'center',
|
||||
* justifyContent: 'center',
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* // skip this line if using Create React Native App
|
||||
* AppRegistry.registerComponent('App', () => App);
|
||||
* ```
|
||||
*
|
||||
* @keyword checkbox
|
||||
* @keyword toggle
|
||||
*/
|
||||
|
||||
@@ -78,11 +78,6 @@ const DatePickerIOS = createReactClass({
|
||||
*/
|
||||
mode: PropTypes.oneOf(['date', 'time', 'datetime']),
|
||||
|
||||
/**
|
||||
* The date picker locale.
|
||||
*/
|
||||
locale: PropTypes.string,
|
||||
|
||||
/**
|
||||
* The interval at which minutes can be selected.
|
||||
*/
|
||||
@@ -132,7 +127,6 @@ const DatePickerIOS = createReactClass({
|
||||
ref={ picker => { this._picker = picker; } }
|
||||
style={styles.datePickerIOS}
|
||||
date={props.date.getTime()}
|
||||
locale={props.locale ? props.locale : undefined}
|
||||
maximumDate={
|
||||
props.maximumDate ? props.maximumDate.getTime() : undefined
|
||||
}
|
||||
@@ -161,7 +155,6 @@ const RCTDatePickerIOS = requireNativeComponent('RCTDatePicker', {
|
||||
propTypes: {
|
||||
...DatePickerIOS.propTypes,
|
||||
date: PropTypes.number,
|
||||
locale: PropTypes.string,
|
||||
minimumDate: PropTypes.number,
|
||||
maximumDate: PropTypes.number,
|
||||
onDateChange: () => null,
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const LayoutAnimation = require('LayoutAnimation');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
const NativeEventEmitter = require('NativeEventEmitter');
|
||||
const KeyboardObserver = require('NativeModules').KeyboardObserver;
|
||||
@@ -26,18 +25,16 @@ type KeyboardEventName =
|
||||
| 'keyboardWillChangeFrame'
|
||||
| 'keyboardDidChangeFrame';
|
||||
|
||||
export type KeyboardEvent = {|
|
||||
+duration?: number,
|
||||
+easing?: string,
|
||||
+endCoordinates: {|
|
||||
+width: number,
|
||||
+height: number,
|
||||
+screenX: number,
|
||||
+screenY: number,
|
||||
|},
|
||||
|};
|
||||
type KeyboardEventData = {
|
||||
endCoordinates: {
|
||||
width: number,
|
||||
height: number,
|
||||
screenX: number,
|
||||
screenY: number,
|
||||
},
|
||||
};
|
||||
|
||||
type KeyboardEventListener = (e: KeyboardEvent) => void;
|
||||
type KeyboardEventListener = (e: KeyboardEventData) => void;
|
||||
|
||||
// The following object exists for documentation purposes
|
||||
// Actual work happens in
|
||||
@@ -137,31 +134,11 @@ let Keyboard = {
|
||||
*/
|
||||
dismiss() {
|
||||
invariant(false, 'Dummy method used for documentation');
|
||||
},
|
||||
|
||||
/**
|
||||
* Useful for syncing TextInput (or other keyboard accessory view) size of
|
||||
* position changes with keyboard movements.
|
||||
*/
|
||||
scheduleLayoutAnimation(event: KeyboardEvent) {
|
||||
invariant(false, 'Dummy method used for documentation');
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Throw away the dummy object and reassign it to original module
|
||||
Keyboard = KeyboardEventEmitter;
|
||||
Keyboard.dismiss = dismissKeyboard;
|
||||
Keyboard.scheduleLayoutAnimation = function(event: KeyboardEvent) {
|
||||
const {duration, easing} = event;
|
||||
if (duration) {
|
||||
LayoutAnimation.configureNext({
|
||||
duration: duration,
|
||||
update: {
|
||||
duration: duration,
|
||||
type: (easing && LayoutAnimation.Types[easing]) || 'keyboard',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Keyboard;
|
||||
|
||||
@@ -43,8 +43,8 @@ type KeyboardChangeEvent = {
|
||||
const viewRef = 'VIEW';
|
||||
|
||||
/**
|
||||
* This is a component to solve the common problem of views that need to move out of the way of the virtual keyboard.
|
||||
* It can automatically adjust either its height, position or bottom padding based on the position of the keyboard.
|
||||
* It is a component to solve the common problem of views that need to move out of the way of the virtual keyboard.
|
||||
* It can automatically adjust either its position or bottom padding based on the position of the keyboard.
|
||||
*/
|
||||
const KeyboardAvoidingView = createReactClass({
|
||||
displayName: 'KeyboardAvoidingView',
|
||||
@@ -52,10 +52,6 @@ const KeyboardAvoidingView = createReactClass({
|
||||
|
||||
propTypes: {
|
||||
...ViewPropTypes,
|
||||
/**
|
||||
* Specify how the `KeyboardAvoidingView` will react to the presence of
|
||||
* the keyboard. It can adjust the height, position or bottom padding of the view
|
||||
*/
|
||||
behavior: PropTypes.oneOf(['height', 'position', 'padding']),
|
||||
|
||||
/**
|
||||
@@ -65,7 +61,7 @@ const KeyboardAvoidingView = createReactClass({
|
||||
|
||||
/**
|
||||
* This is the distance between the top of the user screen and the react native view,
|
||||
* may be non-zero in some use cases. The default value is 0.
|
||||
* may be non-zero in some use cases.
|
||||
*/
|
||||
keyboardVerticalOffset: PropTypes.number.isRequired,
|
||||
},
|
||||
@@ -85,7 +81,7 @@ const KeyboardAvoidingView = createReactClass({
|
||||
subscriptions: ([]: Array<EmitterSubscription>),
|
||||
frame: (null: ?ViewLayout),
|
||||
|
||||
_relativeKeyboardHeight(keyboardFrame: ScreenRect): number {
|
||||
relativeKeyboardHeight(keyboardFrame: ScreenRect): number {
|
||||
const frame = this.frame;
|
||||
if (!frame || !keyboardFrame) {
|
||||
return 0;
|
||||
@@ -98,18 +94,14 @@ const KeyboardAvoidingView = createReactClass({
|
||||
return Math.max(frame.y + frame.height - keyboardY, 0);
|
||||
},
|
||||
|
||||
_onKeyboardChange(event: ?KeyboardChangeEvent) {
|
||||
onKeyboardChange(event: ?KeyboardChangeEvent) {
|
||||
if (!event) {
|
||||
this.setState({bottom: 0});
|
||||
return;
|
||||
}
|
||||
|
||||
const {duration, easing, endCoordinates} = event;
|
||||
const height = this._relativeKeyboardHeight(endCoordinates);
|
||||
|
||||
if (this.state.bottom === height) {
|
||||
return;
|
||||
}
|
||||
const height = this.relativeKeyboardHeight(endCoordinates);
|
||||
|
||||
if (duration && easing) {
|
||||
LayoutAnimation.configureNext({
|
||||
@@ -123,7 +115,7 @@ const KeyboardAvoidingView = createReactClass({
|
||||
this.setState({bottom: height});
|
||||
},
|
||||
|
||||
_onLayout(event: ViewLayoutEvent) {
|
||||
onLayout(event: ViewLayoutEvent) {
|
||||
this.frame = event.nativeEvent.layout;
|
||||
},
|
||||
|
||||
@@ -140,12 +132,12 @@ const KeyboardAvoidingView = createReactClass({
|
||||
componentWillMount() {
|
||||
if (Platform.OS === 'ios') {
|
||||
this.subscriptions = [
|
||||
Keyboard.addListener('keyboardWillChangeFrame', this._onKeyboardChange),
|
||||
Keyboard.addListener('keyboardWillChangeFrame', this.onKeyboardChange),
|
||||
];
|
||||
} else {
|
||||
this.subscriptions = [
|
||||
Keyboard.addListener('keyboardDidHide', this._onKeyboardChange),
|
||||
Keyboard.addListener('keyboardDidShow', this._onKeyboardChange),
|
||||
Keyboard.addListener('keyboardDidHide', this.onKeyboardChange),
|
||||
Keyboard.addListener('keyboardDidShow', this.onKeyboardChange),
|
||||
];
|
||||
}
|
||||
},
|
||||
@@ -169,7 +161,7 @@ const KeyboardAvoidingView = createReactClass({
|
||||
heightStyle = {height: this.frame.height - this.state.bottom, flex: 0};
|
||||
}
|
||||
return (
|
||||
<View ref={viewRef} style={[style, heightStyle]} onLayout={this._onLayout} {...props}>
|
||||
<View ref={viewRef} style={[style, heightStyle]} onLayout={this.onLayout} {...props}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
@@ -179,7 +171,7 @@ const KeyboardAvoidingView = createReactClass({
|
||||
const { contentContainerStyle } = this.props;
|
||||
|
||||
return (
|
||||
<View ref={viewRef} style={style} onLayout={this._onLayout} {...props}>
|
||||
<View ref={viewRef} style={style} onLayout={this.onLayout} {...props}>
|
||||
<View style={[contentContainerStyle, positionStyle]}>
|
||||
{children}
|
||||
</View>
|
||||
@@ -189,14 +181,14 @@ const KeyboardAvoidingView = createReactClass({
|
||||
case 'padding':
|
||||
const paddingStyle = {paddingBottom: this.state.bottom};
|
||||
return (
|
||||
<View ref={viewRef} style={[style, paddingStyle]} onLayout={this._onLayout} {...props}>
|
||||
<View ref={viewRef} style={[style, paddingStyle]} onLayout={this.onLayout} {...props}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<View ref={viewRef} onLayout={this._onLayout} style={style} {...props}>
|
||||
<View ref={viewRef} onLayout={this.onLayout} style={style} {...props}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ type Props = ViewProps & {
|
||||
/**
|
||||
* Renders nested content and automatically applies paddings reflect the portion of the view
|
||||
* that is not covered by navigation bars, tab bars, toolbars, and other ancestor views.
|
||||
* Moreover, and most importantly, Safe Area's paddings reflect physical limitation of the screen,
|
||||
* Moreover, and most importantly, Safe Area's paddings feflect physical limitation of the screen,
|
||||
* such as rounded corners or camera notches (aka sensor housing area on iPhone X).
|
||||
*/
|
||||
class SafeAreaView extends React.Component<Props> {
|
||||
|
||||
@@ -11,21 +11,27 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const Dimensions = require('Dimensions');
|
||||
const FrameRateLogger = require('FrameRateLogger');
|
||||
const Keyboard = require('Keyboard');
|
||||
const ReactNative = require('ReactNative');
|
||||
const Subscribable = require('Subscribable');
|
||||
const TextInputState = require('TextInputState');
|
||||
const UIManager = require('UIManager');
|
||||
var Dimensions = require('Dimensions');
|
||||
var FrameRateLogger = require('FrameRateLogger');
|
||||
var Keyboard = require('Keyboard');
|
||||
var ReactNative = require('ReactNative');
|
||||
var Subscribable = require('Subscribable');
|
||||
var TextInputState = require('TextInputState');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
const nullthrows = require('fbjs/lib/nullthrows');
|
||||
const performanceNow = require('fbjs/lib/performanceNow');
|
||||
const warning = require('fbjs/lib/warning');
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
var nullthrows = require('fbjs/lib/nullthrows');
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||||
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
||||
* run Flow. */
|
||||
var performanceNow = require('fbjs/lib/performanceNow');
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||||
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
||||
* run Flow. */
|
||||
var warning = require('fbjs/lib/warning');
|
||||
|
||||
const { ScrollViewManager } = require('NativeModules');
|
||||
const { getInstanceFromNode } = require('ReactNativeComponentTree');
|
||||
var { ScrollViewManager } = require('NativeModules');
|
||||
var { getInstanceFromNode } = require('ReactNativeComponentTree');
|
||||
|
||||
/**
|
||||
* Mixin that can be integrated in order to handle scrolling that plays well
|
||||
@@ -105,7 +111,7 @@ const { getInstanceFromNode } = require('ReactNativeComponentTree');
|
||||
* this.props.onKeyboardDidHide
|
||||
*/
|
||||
|
||||
const IS_ANIMATING_TOUCH_START_THRESHOLD_MS = 16;
|
||||
var IS_ANIMATING_TOUCH_START_THRESHOLD_MS = 16;
|
||||
|
||||
type State = {
|
||||
isTouching: boolean,
|
||||
@@ -117,15 +123,15 @@ type State = {
|
||||
type Event = Object;
|
||||
|
||||
function isTagInstanceOfTextInput(tag) {
|
||||
const instance = getInstanceFromNode(tag);
|
||||
var instance = getInstanceFromNode(tag);
|
||||
return instance && instance.viewConfig && (
|
||||
instance.viewConfig.uiViewClassName === 'AndroidTextInput' ||
|
||||
instance.viewConfig.uiViewClassName === 'RCTMultilineTextInputView' ||
|
||||
instance.viewConfig.uiViewClassName === 'RCTSinglelineTextInputView'
|
||||
instance.viewConfig.uiViewClassName === 'RCTTextView' ||
|
||||
instance.viewConfig.uiViewClassName === 'RCTTextField'
|
||||
);
|
||||
}
|
||||
|
||||
const ScrollResponderMixin = {
|
||||
var ScrollResponderMixin = {
|
||||
mixins: [Subscribable.Mixin],
|
||||
scrollResponderMixinGetInitialState: function(): State {
|
||||
return {
|
||||
@@ -176,7 +182,7 @@ const ScrollResponderMixin = {
|
||||
*
|
||||
*/
|
||||
scrollResponderHandleStartShouldSetResponder: function(e: Event): boolean {
|
||||
const currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
|
||||
var currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
|
||||
|
||||
if (this.props.keyboardShouldPersistTaps === 'handled' &&
|
||||
currentlyFocusedTextInput != null &&
|
||||
@@ -199,9 +205,9 @@ const ScrollResponderMixin = {
|
||||
*/
|
||||
scrollResponderHandleStartShouldSetResponderCapture: function(e: Event): boolean {
|
||||
// First see if we want to eat taps while the keyboard is up
|
||||
const currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
|
||||
const {keyboardShouldPersistTaps} = this.props;
|
||||
const keyboardNeverPersistTaps = !keyboardShouldPersistTaps ||
|
||||
var currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
|
||||
var {keyboardShouldPersistTaps} = this.props;
|
||||
var keyboardNeverPersistTaps = !keyboardShouldPersistTaps ||
|
||||
keyboardShouldPersistTaps === 'never';
|
||||
if (keyboardNeverPersistTaps &&
|
||||
currentlyFocusedTextInput != null &&
|
||||
@@ -249,7 +255,7 @@ const ScrollResponderMixin = {
|
||||
* @param {SyntheticEvent} e Event.
|
||||
*/
|
||||
scrollResponderHandleTouchEnd: function(e: Event) {
|
||||
const nativeEvent = e.nativeEvent;
|
||||
var nativeEvent = e.nativeEvent;
|
||||
this.state.isTouching = nativeEvent.touches.length !== 0;
|
||||
this.props.onTouchEnd && this.props.onTouchEnd(e);
|
||||
},
|
||||
@@ -272,7 +278,7 @@ const ScrollResponderMixin = {
|
||||
|
||||
// By default scroll views will unfocus a textField
|
||||
// if another touch occurs outside of it
|
||||
const currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
|
||||
var currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
|
||||
if (this.props.keyboardShouldPersistTaps !== true &&
|
||||
this.props.keyboardShouldPersistTaps !== 'always' &&
|
||||
currentlyFocusedTextInput != null &&
|
||||
@@ -382,9 +388,9 @@ const ScrollResponderMixin = {
|
||||
* a touch has just started or ended.
|
||||
*/
|
||||
scrollResponderIsAnimating: function(): boolean {
|
||||
const now = performanceNow();
|
||||
const timeSinceLastMomentumScrollEnd = now - this.state.lastMomentumScrollEndTime;
|
||||
const isAnimating = timeSinceLastMomentumScrollEnd < IS_ANIMATING_TOUCH_START_THRESHOLD_MS ||
|
||||
var now = performanceNow();
|
||||
var timeSinceLastMomentumScrollEnd = now - this.state.lastMomentumScrollEndTime;
|
||||
var isAnimating = timeSinceLastMomentumScrollEnd < IS_ANIMATING_TOUCH_START_THRESHOLD_MS ||
|
||||
this.state.lastMomentumScrollEndTime < this.state.lastMomentumScrollBeginTime;
|
||||
return isAnimating;
|
||||
},
|
||||
@@ -463,13 +469,12 @@ const ScrollResponderMixin = {
|
||||
* @platform ios
|
||||
*/
|
||||
scrollResponderZoomTo: function(
|
||||
rect: {| x: number, y: number, width: number, height: number, animated?: boolean |},
|
||||
rect: { x: number, y: number, width: number, height: number, animated?: boolean },
|
||||
animated?: boolean // deprecated, put this inside the rect argument instead
|
||||
) {
|
||||
invariant(ScrollViewManager && ScrollViewManager.zoomToRect, 'zoomToRect is not implemented');
|
||||
if ('animated' in rect) {
|
||||
animated = rect.animated;
|
||||
delete rect.animated;
|
||||
var { animated, ...rect } = rect;
|
||||
} else if (typeof animated !== 'undefined') {
|
||||
console.warn('`scrollResponderZoomTo` `animated` argument is deprecated. Use `options.animated` instead');
|
||||
}
|
||||
@@ -519,11 +524,11 @@ const ScrollResponderMixin = {
|
||||
* @param {number} height Height of the text input.
|
||||
*/
|
||||
scrollResponderInputMeasureAndScrollToKeyboard: function(left: number, top: number, width: number, height: number) {
|
||||
let keyboardScreenY = Dimensions.get('window').height;
|
||||
var keyboardScreenY = Dimensions.get('window').height;
|
||||
if (this.keyboardWillOpenTo) {
|
||||
keyboardScreenY = this.keyboardWillOpenTo.endCoordinates.screenY;
|
||||
}
|
||||
let scrollOffsetY = top - keyboardScreenY + height + this.additionalScrollOffset;
|
||||
var scrollOffsetY = top - keyboardScreenY + height + this.additionalScrollOffset;
|
||||
|
||||
// By default, this can scroll with negative offset, pulling the content
|
||||
// down so that the target component's bottom meets the keyboard's top.
|
||||
@@ -549,7 +554,7 @@ const ScrollResponderMixin = {
|
||||
* The `keyboardWillShow` is called before input focus.
|
||||
*/
|
||||
componentWillMount: function() {
|
||||
const {keyboardShouldPersistTaps} = this.props;
|
||||
var {keyboardShouldPersistTaps} = this.props;
|
||||
warning(
|
||||
typeof keyboardShouldPersistTaps !== 'boolean',
|
||||
`'keyboardShouldPersistTaps={${keyboardShouldPersistTaps}}' is deprecated. `
|
||||
@@ -618,7 +623,7 @@ const ScrollResponderMixin = {
|
||||
|
||||
};
|
||||
|
||||
const ScrollResponder = {
|
||||
var ScrollResponder = {
|
||||
Mixin: ScrollResponderMixin,
|
||||
};
|
||||
|
||||
|
||||
@@ -256,15 +256,6 @@ const ScrollView = createReactClass({
|
||||
* events can be controlled using the `scrollEventThrottle` prop.
|
||||
*/
|
||||
onScroll: PropTypes.func,
|
||||
/**
|
||||
* Called when the user begins to drag the scroll view.
|
||||
*/
|
||||
onScrollBeginDrag: PropTypes.func,
|
||||
/**
|
||||
* Called when the user stops dragging the scroll view and it either stops
|
||||
* or begins to glide.
|
||||
*/
|
||||
onScrollEndDrag: PropTypes.func,
|
||||
/**
|
||||
* Called when scrollable content view of the ScrollView changes.
|
||||
*
|
||||
@@ -344,10 +335,10 @@ const ScrollView = createReactClass({
|
||||
* When set, causes the scroll view to stop at multiples of the value of
|
||||
* `snapToInterval`. This can be used for paginating through children
|
||||
* that have lengths smaller than the scroll view. Typically used in
|
||||
* combination with `snapToAlignment` and `decelerationRate="fast"` on ios.
|
||||
* combination with `snapToAlignment` and `decelerationRate="fast"`.
|
||||
* Overrides less configurable `pagingEnabled` prop.
|
||||
*
|
||||
* Supported for horizontal scrollview on android.
|
||||
* @platform ios
|
||||
*/
|
||||
snapToInterval: PropTypes.number,
|
||||
/**
|
||||
@@ -560,6 +551,9 @@ const ScrollView = createReactClass({
|
||||
},
|
||||
|
||||
_getKeyForIndex: function(index, childArray) {
|
||||
/* $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. */
|
||||
const child = childArray[index];
|
||||
return child && child.key;
|
||||
},
|
||||
@@ -589,6 +583,9 @@ const ScrollView = createReactClass({
|
||||
if (!this.props.stickyHeaderIndices) {
|
||||
return;
|
||||
}
|
||||
/* $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. */
|
||||
const childArray = React.Children.toArray(this.props.children);
|
||||
if (key !== this._getKeyForIndex(index, childArray)) {
|
||||
// ignore stale layout update
|
||||
@@ -598,7 +595,13 @@ const ScrollView = createReactClass({
|
||||
const layoutY = event.nativeEvent.layout.y;
|
||||
this._headerLayoutYs.set(key, layoutY);
|
||||
|
||||
/* $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. */
|
||||
const indexOfIndex = this.props.stickyHeaderIndices.indexOf(index);
|
||||
/* $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. */
|
||||
const previousHeaderIndex = this.props.stickyHeaderIndices[indexOfIndex - 1];
|
||||
if (previousHeaderIndex != null) {
|
||||
const previousHeader = this._stickyHeaderRefs.get(
|
||||
@@ -611,7 +614,7 @@ const ScrollView = createReactClass({
|
||||
_handleScroll: function(e: Object) {
|
||||
if (__DEV__) {
|
||||
if (this.props.onScroll && this.props.scrollEventThrottle == null && Platform.OS === 'ios') {
|
||||
console.log(
|
||||
console.log( // eslint-disable-line no-console
|
||||
'You specified `onScroll` on a <ScrollView> but not ' +
|
||||
'`scrollEventThrottle`. You will only receive one event. ' +
|
||||
'Using `16` you get all the events but be aware that it may ' +
|
||||
@@ -698,18 +701,34 @@ const ScrollView = createReactClass({
|
||||
|
||||
const {stickyHeaderIndices} = this.props;
|
||||
const hasStickyHeaders = stickyHeaderIndices && stickyHeaderIndices.length > 0;
|
||||
/* $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. */
|
||||
const childArray = hasStickyHeaders && React.Children.toArray(this.props.children);
|
||||
const children = hasStickyHeaders ?
|
||||
/* $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. */
|
||||
childArray.map((child, index) => {
|
||||
/* $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. */
|
||||
const indexOfIndex = child ? stickyHeaderIndices.indexOf(index) : -1;
|
||||
if (indexOfIndex > -1) {
|
||||
const key = child.key;
|
||||
/* $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. */
|
||||
const nextIndex = stickyHeaderIndices[indexOfIndex + 1];
|
||||
return (
|
||||
<ScrollViewStickyHeader
|
||||
key={key}
|
||||
ref={(ref) => this._setStickyHeaderRef(key, ref)}
|
||||
nextHeaderLayoutY={
|
||||
/* $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._headerLayoutYs.get(this._getKeyForIndex(nextIndex, childArray))
|
||||
}
|
||||
onLayout={(event) => this._onStickyHeaderLayout(index, event, key)}
|
||||
@@ -721,10 +740,16 @@ const ScrollView = createReactClass({
|
||||
return child;
|
||||
}
|
||||
}) :
|
||||
/* $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.props.children;
|
||||
const contentContainer =
|
||||
<ScrollContentContainerViewClass
|
||||
{...contentSizeChangeProps}
|
||||
/* $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. */
|
||||
ref={this._setInnerViewRef}
|
||||
style={contentContainerStyle}
|
||||
removeClippedSubviews={
|
||||
@@ -765,6 +790,9 @@ const ScrollView = createReactClass({
|
||||
onResponderGrant: this.scrollResponderHandleResponderGrant,
|
||||
onResponderReject: this.scrollResponderHandleResponderReject,
|
||||
onResponderRelease: this.scrollResponderHandleResponderRelease,
|
||||
/* $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. */
|
||||
onResponderTerminate: this.scrollResponderHandleTerminate,
|
||||
onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest,
|
||||
onScroll: this._handleScroll,
|
||||
@@ -795,6 +823,9 @@ const ScrollView = createReactClass({
|
||||
// On iOS the RefreshControl is a child of the ScrollView.
|
||||
// tvOS lacks native support for RefreshControl, so don't include it in that case
|
||||
return (
|
||||
/* $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. */
|
||||
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
|
||||
{Platform.isTVOS ? null : refreshControl}
|
||||
{contentContainer}
|
||||
@@ -810,6 +841,9 @@ const ScrollView = createReactClass({
|
||||
return React.cloneElement(
|
||||
refreshControl,
|
||||
{style: props.style},
|
||||
/* $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. */
|
||||
<ScrollViewClass {...props} style={baseStyle} ref={this._setScrollViewRef}>
|
||||
{contentContainer}
|
||||
</ScrollViewClass>
|
||||
@@ -817,6 +851,9 @@ const ScrollView = createReactClass({
|
||||
}
|
||||
}
|
||||
return (
|
||||
/* $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. */
|
||||
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
|
||||
{contentContainer}
|
||||
</ScrollViewClass>
|
||||
|
||||
@@ -8,13 +8,8 @@
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
/* eslint-env jest */
|
||||
|
||||
'use strict';
|
||||
|
||||
declare var jest: any;
|
||||
|
||||
const React = require('React');
|
||||
const View = require('View');
|
||||
|
||||
@@ -22,9 +17,7 @@ const requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
const RCTScrollView = requireNativeComponent('RCTScrollView');
|
||||
|
||||
const ScrollViewComponent = jest.genMockFromModule('ScrollView');
|
||||
|
||||
class ScrollViewMock extends ScrollViewComponent {
|
||||
class ScrollViewMock extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<RCTScrollView {...this.props}>
|
||||
|
||||
@@ -28,63 +28,6 @@ type Event = Object;
|
||||
|
||||
/**
|
||||
* A component used to select a single value from a range of values.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* The example below shows how to use `Slider` to change
|
||||
* a value used by `Text`. The value is stored using
|
||||
* the state of the root component (`App`). The same component
|
||||
* subscribes to the `onValueChange` of `Slider` and changes
|
||||
* the value using `setState`.
|
||||
*
|
||||
*```
|
||||
* import React from 'react';
|
||||
* import { StyleSheet, Text, View, Slider } from 'react-native';
|
||||
*
|
||||
* export default class App extends React.Component {
|
||||
* constructor(props) {
|
||||
* super(props);
|
||||
* this.state = {
|
||||
* value: 50
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* change(value) {
|
||||
* this.setState(() => {
|
||||
* return {
|
||||
* value: parseFloat(value)
|
||||
* };
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* const {value} = this.state;
|
||||
* return (
|
||||
* <View style={styles.container}>
|
||||
* <Text style={styles.text}>{String(value)}</Text>
|
||||
* <Slider
|
||||
* step={1}
|
||||
* maximumValue={100}
|
||||
* onValueChange={this.change.bind(this)}
|
||||
* value={value} />
|
||||
* </View>
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const styles = StyleSheet.create({
|
||||
* container: {
|
||||
* flex: 1,
|
||||
* flexDirection: 'column',
|
||||
* justifyContent: 'center'
|
||||
* },
|
||||
* text: {
|
||||
* fontSize: 50,
|
||||
* textAlign: 'center'
|
||||
* }
|
||||
* });
|
||||
*```
|
||||
*
|
||||
*/
|
||||
var Slider = createReactClass({
|
||||
displayName: 'Slider',
|
||||
|
||||
@@ -324,6 +324,7 @@ class StatusBar extends React.Component<{
|
||||
componentWillUnmount() {
|
||||
// When a StatusBar is unmounted, remove itself from the stack and update
|
||||
// the native bar with the next props.
|
||||
// $FlowFixMe found when converting React.createClass to ES6
|
||||
const index = StatusBar._propsStack.indexOf(this._stackEntry);
|
||||
StatusBar._propsStack.splice(index, 1);
|
||||
|
||||
@@ -331,6 +332,7 @@ class StatusBar extends React.Component<{
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// $FlowFixMe found when converting React.createClass to ES6
|
||||
const index = StatusBar._propsStack.indexOf(this._stackEntry);
|
||||
this._stackEntry = createStackEntry(this.props);
|
||||
StatusBar._propsStack[index] = this._stackEntry;
|
||||
|
||||
@@ -20,22 +20,19 @@ const ViewPropTypes = require('ViewPropTypes');
|
||||
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
import type {StyleObj} from 'StyleSheetTypes';
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
class TabBarIOS extends React.Component<ViewProps & {
|
||||
style?: StyleObj,
|
||||
unselectedTintColor?: string,
|
||||
tintColor?: string,
|
||||
unselectedItemTintColor?: string,
|
||||
barTintColor?: string,
|
||||
class TabBarIOS extends React.Component<{
|
||||
style?: $FlowFixMe,
|
||||
unselectedTintColor?: $FlowFixMe,
|
||||
tintColor?: $FlowFixMe,
|
||||
unselectedItemTintColor?: $FlowFixMe,
|
||||
barTintColor?: $FlowFixMe,
|
||||
barStyle?: 'default' | 'black',
|
||||
translucent?: boolean,
|
||||
itemPositioning?: 'fill' | 'center' | 'auto',
|
||||
children: React.Node,
|
||||
}> {
|
||||
static Item = TabBarItemIOS;
|
||||
|
||||
// $FlowFixMe(>=0.41.0)
|
||||
static propTypes = {
|
||||
...ViewPropTypes,
|
||||
style: ViewPropTypes.style,
|
||||
@@ -88,7 +85,9 @@ class TabBarIOS extends React.Component<ViewProps & {
|
||||
barStyle={this.props.barStyle}
|
||||
itemPositioning={this.props.itemPositioning}
|
||||
translucent={this.props.translucent !== false}>
|
||||
{this.props.children}
|
||||
{
|
||||
// $FlowFixMe found when converting React.createClass to ES6
|
||||
this.props.children}
|
||||
</RCTTabBar>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ const onlyMultiline = {
|
||||
if (Platform.OS === 'android') {
|
||||
var AndroidTextInput = requireNativeComponent('AndroidTextInput', null);
|
||||
} else if (Platform.OS === 'ios') {
|
||||
var RCTMultilineTextInputView = requireNativeComponent('RCTMultilineTextInputView', null);
|
||||
var RCTSinglelineTextInputView = requireNativeComponent('RCTSinglelineTextInputView', null);
|
||||
var RCTTextView = requireNativeComponent('RCTTextView', null);
|
||||
var RCTTextField = requireNativeComponent('RCTTextField', null);
|
||||
}
|
||||
|
||||
type Event = Object;
|
||||
@@ -219,6 +219,13 @@ const TextInput = createReactClass({
|
||||
* The default value is `false`.
|
||||
*/
|
||||
autoFocus: PropTypes.bool,
|
||||
/**
|
||||
* If true, will increase the height of the textbox if need be. If false,
|
||||
* the textbox will become scrollable once the height is reached. The
|
||||
* default value is false.
|
||||
* @platform android
|
||||
*/
|
||||
autoGrow: PropTypes.bool,
|
||||
/**
|
||||
* Specifies whether fonts should scale to respect Text Size accessibility settings. The
|
||||
* default is `true`.
|
||||
@@ -344,6 +351,11 @@ const TextInput = createReactClass({
|
||||
* instead of implementing the logic in JS to avoid flicker.
|
||||
*/
|
||||
maxLength: PropTypes.number,
|
||||
/**
|
||||
* If autogrow is `true`, limits the height that the TextInput box can grow
|
||||
* to. Once it reaches this height, the TextInput becomes scrollable.
|
||||
*/
|
||||
maxHeight: PropTypes.number,
|
||||
/**
|
||||
* Sets the number of lines for a `TextInput`. Use it with multiline set to
|
||||
* `true` to be able to fill the lines.
|
||||
@@ -422,6 +434,7 @@ const TextInput = createReactClass({
|
||||
* where `keyValue` is `'Enter'` or `'Backspace'` for respective keys and
|
||||
* the typed-in character otherwise including `' '` for space.
|
||||
* Fires before `onChange` callbacks.
|
||||
* @platform ios
|
||||
*/
|
||||
onKeyPress: PropTypes.func,
|
||||
/**
|
||||
@@ -599,6 +612,10 @@ const TextInput = createReactClass({
|
||||
*/
|
||||
mixins: [NativeMethodsMixin, TimerMixin],
|
||||
|
||||
getInitialState: function() {
|
||||
return {layoutHeight: this._layoutHeight};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns `true` if the input is currently focused; `false` otherwise.
|
||||
*/
|
||||
@@ -616,6 +633,7 @@ const TextInput = createReactClass({
|
||||
_focusSubscription: (undefined: ?Function),
|
||||
_lastNativeText: (undefined: ?string),
|
||||
_lastNativeSelection: (undefined: ?Selection),
|
||||
_layoutHeight: (-1: number),
|
||||
|
||||
componentDidMount: function() {
|
||||
this._lastNativeText = this.props.value;
|
||||
@@ -706,7 +724,7 @@ const TextInput = createReactClass({
|
||||
}
|
||||
}
|
||||
textContainer =
|
||||
<RCTSinglelineTextInputView
|
||||
<RCTTextField
|
||||
ref={this._setNativeRef}
|
||||
{...props}
|
||||
onFocus={this._onFocus}
|
||||
@@ -732,7 +750,7 @@ const TextInput = createReactClass({
|
||||
}
|
||||
props.style.unshift(styles.multilineInput);
|
||||
textContainer =
|
||||
<RCTMultilineTextInputView
|
||||
<RCTTextView
|
||||
ref={this._setNativeRef}
|
||||
{...props}
|
||||
children={children}
|
||||
@@ -748,7 +766,6 @@ const TextInput = createReactClass({
|
||||
onScroll={this._onScroll}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback
|
||||
onLayout={props.onLayout}
|
||||
@@ -766,7 +783,10 @@ const TextInput = createReactClass({
|
||||
|
||||
_renderAndroid: function() {
|
||||
const props = Object.assign({}, this.props);
|
||||
props.style = [this.props.style];
|
||||
props.style = this.props.style;
|
||||
if (this.state.layoutHeight >= 0) {
|
||||
props.style = [props.style, {height: this.state.layoutHeight}];
|
||||
}
|
||||
props.autoCapitalize =
|
||||
UIManager.AndroidTextInput.Constants.AutoCapitalizationType[
|
||||
props.autoCapitalize || 'sentences'
|
||||
@@ -784,11 +804,9 @@ const TextInput = createReactClass({
|
||||
if (childCount > 1) {
|
||||
children = <Text>{children}</Text>;
|
||||
}
|
||||
|
||||
if (props.selection && props.selection.end == null) {
|
||||
props.selection = {start: props.selection.start, end: props.selection.start};
|
||||
}
|
||||
|
||||
const textContainer =
|
||||
<AndroidTextInput
|
||||
ref={this._setNativeRef}
|
||||
@@ -797,6 +815,7 @@ const TextInput = createReactClass({
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
onContentSizeChange={this._onContentSizeChange}
|
||||
onSelectionChange={this._onSelectionChange}
|
||||
onTextInput={this._onTextInput}
|
||||
text={this._getText()}
|
||||
@@ -859,6 +878,26 @@ const TextInput = createReactClass({
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
_onContentSizeChange: function(event: Event) {
|
||||
let contentHeight = event.nativeEvent.contentSize.height;
|
||||
if (this.props.autoGrow) {
|
||||
if (this.props.maxHeight) {
|
||||
contentHeight = Math.min(this.props.maxHeight, contentHeight);
|
||||
}
|
||||
this.setState({layoutHeight: Math.max(this._layoutHeight, contentHeight)});
|
||||
}
|
||||
|
||||
this.props.onContentSizeChange && this.props.onContentSizeChange(event);
|
||||
},
|
||||
|
||||
_onLayout: function(event: Event) {
|
||||
const height = event.nativeEvent.layout.height;
|
||||
if (height) {
|
||||
this._layoutHeight = event.nativeEvent.layout.height;
|
||||
}
|
||||
this.props.onLayout && this.props.onLayout(event);
|
||||
},
|
||||
|
||||
_onSelectionChange: function(event: Event) {
|
||||
this.props.onSelectionChange && this.props.onSelectionChange(event);
|
||||
|
||||
@@ -915,6 +954,9 @@ const TextInput = createReactClass({
|
||||
},
|
||||
|
||||
_onTextInput: function(event: Event) {
|
||||
/* $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.props.onTextInput && this.props.onTextInput(event);
|
||||
},
|
||||
|
||||
@@ -925,8 +967,8 @@ const TextInput = createReactClass({
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
multilineInput: {
|
||||
// This default top inset makes RCTMultilineTextInputView seem as close as possible
|
||||
// to single-line RCTSinglelineTextInputView defaults, using the system defaults
|
||||
// This default top inset makes RCTTextView seem as close as possible
|
||||
// to single-line RCTTextField defaults, using the system defaults
|
||||
// of font size 17 and a height of 31 points.
|
||||
paddingTop: 5,
|
||||
},
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*
|
||||
* @providesModule TouchableHighlight
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@@ -18,19 +17,28 @@ const PropTypes = require('prop-types');
|
||||
const React = require('React');
|
||||
const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||||
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
||||
* run Flow. */
|
||||
const TimerMixin = require('react-timer-mixin');
|
||||
const Touchable = require('Touchable');
|
||||
const TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
||||
const View = require('View');
|
||||
const ViewPropTypes = require('ViewPropTypes');
|
||||
|
||||
const createReactClass = require('create-react-class');
|
||||
const ensureComponentIsNative = require('ensureComponentIsNative');
|
||||
const ensurePositiveDelayProps = require('ensurePositiveDelayProps');
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||||
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
||||
* run Flow. */
|
||||
const keyOf = require('fbjs/lib/keyOf');
|
||||
const merge = require('merge');
|
||||
|
||||
import type {PressEvent} from 'CoreEventTypes';
|
||||
import type {Event} from 'TouchableWithoutFeedback';
|
||||
|
||||
const DEFAULT_PROPS = {
|
||||
activeOpacity: 0.85,
|
||||
delayPressOut: 100,
|
||||
underlayColor: 'black',
|
||||
};
|
||||
|
||||
@@ -133,7 +141,7 @@ const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
*
|
||||
*/
|
||||
|
||||
const TouchableHighlight = createReactClass({
|
||||
var TouchableHighlight = createReactClass({
|
||||
displayName: 'TouchableHighlight',
|
||||
propTypes: {
|
||||
...TouchableWithoutFeedback.propTypes,
|
||||
@@ -147,10 +155,6 @@ const TouchableHighlight = createReactClass({
|
||||
* active.
|
||||
*/
|
||||
underlayColor: ColorPropType,
|
||||
/**
|
||||
* Style to apply to the container/underlay. Most commonly used to make sure
|
||||
* rounded corners match the wrapped component.
|
||||
*/
|
||||
style: ViewPropTypes.style,
|
||||
/**
|
||||
* Called immediately after the underlay is shown
|
||||
@@ -180,67 +184,108 @@ const TouchableHighlight = createReactClass({
|
||||
tvParallaxProperties: PropTypes.object,
|
||||
},
|
||||
|
||||
mixins: [NativeMethodsMixin, Touchable.Mixin],
|
||||
mixins: [NativeMethodsMixin, TimerMixin, Touchable.Mixin],
|
||||
|
||||
getDefaultProps: () => DEFAULT_PROPS,
|
||||
|
||||
getInitialState: function() {
|
||||
this._isMounted = false;
|
||||
// Performance optimization to avoid constantly re-generating these objects.
|
||||
_computeSyntheticState: function(props) {
|
||||
return {
|
||||
...this.touchableGetInitialState(),
|
||||
extraChildStyle: null,
|
||||
extraUnderlayStyle: null,
|
||||
activeProps: {
|
||||
style: {
|
||||
opacity: props.activeOpacity,
|
||||
}
|
||||
},
|
||||
activeUnderlayProps: {
|
||||
style: {
|
||||
backgroundColor: props.underlayColor,
|
||||
}
|
||||
},
|
||||
underlayStyle: [
|
||||
INACTIVE_UNDERLAY_PROPS.style,
|
||||
props.style,
|
||||
],
|
||||
hasTVPreferredFocus: props.hasTVPreferredFocus
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
/* $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._isMounted = false;
|
||||
return merge(
|
||||
this.touchableGetInitialState(), this._computeSyntheticState(this.props)
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
/* $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._isMounted = true;
|
||||
ensurePositiveDelayProps(this.props);
|
||||
ensureComponentIsNative(this.refs[CHILD_REF]);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
/* $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._isMounted = false;
|
||||
clearTimeout(this._hideTimeout);
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
ensureComponentIsNative(this.refs[CHILD_REF]);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
ensurePositiveDelayProps(nextProps);
|
||||
if (nextProps.activeOpacity !== this.props.activeOpacity ||
|
||||
nextProps.underlayColor !== this.props.underlayColor ||
|
||||
nextProps.style !== this.props.style) {
|
||||
this.setState(this._computeSyntheticState(nextProps));
|
||||
}
|
||||
},
|
||||
|
||||
viewConfig: {
|
||||
uiViewClassName: 'RCTView',
|
||||
validAttributes: ReactNativeViewAttributes.RCTView,
|
||||
validAttributes: ReactNativeViewAttributes.RCTView
|
||||
},
|
||||
|
||||
/**
|
||||
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
|
||||
* defined on your component.
|
||||
*/
|
||||
touchableHandleActivePressIn: function(e: PressEvent) {
|
||||
clearTimeout(this._hideTimeout);
|
||||
touchableHandleActivePressIn: function(e: Event) {
|
||||
this.clearTimeout(this._hideTimeout);
|
||||
/* $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._hideTimeout = null;
|
||||
this._showUnderlay();
|
||||
this.props.onPressIn && this.props.onPressIn(e);
|
||||
},
|
||||
|
||||
touchableHandleActivePressOut: function(e: PressEvent) {
|
||||
touchableHandleActivePressOut: function(e: Event) {
|
||||
if (!this._hideTimeout) {
|
||||
this._hideUnderlay();
|
||||
}
|
||||
this.props.onPressOut && this.props.onPressOut(e);
|
||||
},
|
||||
|
||||
touchableHandlePress: function(e: PressEvent) {
|
||||
clearTimeout(this._hideTimeout);
|
||||
touchableHandlePress: function(e: Event) {
|
||||
this.clearTimeout(this._hideTimeout);
|
||||
this._showUnderlay();
|
||||
this._hideTimeout = setTimeout(
|
||||
this._hideUnderlay,
|
||||
this.props.delayPressOut,
|
||||
);
|
||||
/* $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._hideTimeout = this.setTimeout(this._hideUnderlay,
|
||||
this.props.delayPressOut || 100);
|
||||
this.props.onPress && this.props.onPress(e);
|
||||
},
|
||||
|
||||
touchableHandleLongPress: function(e: PressEvent) {
|
||||
touchableHandleLongPress: function(e: Event) {
|
||||
this.props.onLongPress && this.props.onLongPress(e);
|
||||
},
|
||||
|
||||
@@ -268,24 +313,23 @@ const TouchableHighlight = createReactClass({
|
||||
if (!this._isMounted || !this._hasPressHandler()) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
extraChildStyle: {
|
||||
opacity: this.props.activeOpacity,
|
||||
},
|
||||
extraUnderlayStyle: {
|
||||
backgroundColor: this.props.underlayColor,
|
||||
},
|
||||
});
|
||||
|
||||
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
|
||||
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
|
||||
this.props.onShowUnderlay && this.props.onShowUnderlay();
|
||||
},
|
||||
|
||||
_hideUnderlay: function() {
|
||||
clearTimeout(this._hideTimeout);
|
||||
this.clearTimeout(this._hideTimeout);
|
||||
/* $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._hideTimeout = null;
|
||||
if (this._hasPressHandler()) {
|
||||
this.setState({
|
||||
extraChildStyle: null,
|
||||
extraUnderlayStyle: null,
|
||||
if (this._hasPressHandler() && this.refs[UNDERLAY_REF]) {
|
||||
this.refs[CHILD_REF].setNativeProps(INACTIVE_CHILD_PROPS);
|
||||
this.refs[UNDERLAY_REF].setNativeProps({
|
||||
...INACTIVE_UNDERLAY_PROPS,
|
||||
style: this.state.underlayStyle,
|
||||
});
|
||||
this.props.onHideUnderlay && this.props.onHideUnderlay();
|
||||
}
|
||||
@@ -301,45 +345,58 @@ const TouchableHighlight = createReactClass({
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const child = React.Children.only(this.props.children);
|
||||
return (
|
||||
<View
|
||||
accessible={this.props.accessible !== false}
|
||||
/* $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. */
|
||||
accessibilityLabel={this.props.accessibilityLabel}
|
||||
accessibilityComponentType={this.props.accessibilityComponentType}
|
||||
accessibilityTraits={this.props.accessibilityTraits}
|
||||
style={StyleSheet.compose(
|
||||
this.props.style,
|
||||
this.state.extraUnderlayStyle,
|
||||
)}
|
||||
ref={UNDERLAY_REF}
|
||||
style={this.state.underlayStyle}
|
||||
onLayout={this.props.onLayout}
|
||||
hitSlop={this.props.hitSlop}
|
||||
isTVSelectable={true}
|
||||
tvParallaxProperties={this.props.tvParallaxProperties}
|
||||
hasTVPreferredFocus={this.props.hasTVPreferredFocus}
|
||||
hasTVPreferredFocus={this.state.hasTVPreferredFocus}
|
||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
onResponderTerminationRequest={
|
||||
this.touchableHandleResponderTerminationRequest
|
||||
}
|
||||
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
||||
onResponderGrant={this.touchableHandleResponderGrant}
|
||||
onResponderMove={this.touchableHandleResponderMove}
|
||||
onResponderRelease={this.touchableHandleResponderRelease}
|
||||
onResponderTerminate={this.touchableHandleResponderTerminate}
|
||||
/* $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. */
|
||||
nativeID={this.props.nativeID}
|
||||
/* $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. */
|
||||
testID={this.props.testID}>
|
||||
{React.cloneElement(child, {
|
||||
style: StyleSheet.compose(
|
||||
child.props.style,
|
||||
this.state.extraChildStyle,
|
||||
),
|
||||
})}
|
||||
{Touchable.renderDebugView({
|
||||
color: 'green',
|
||||
hitSlop: this.props.hitSlop,
|
||||
})}
|
||||
{React.cloneElement(
|
||||
/* $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. */
|
||||
React.Children.only(this.props.children),
|
||||
{
|
||||
ref: CHILD_REF,
|
||||
}
|
||||
)}
|
||||
{Touchable.renderDebugView({color: 'green', hitSlop: this.props.hitSlop})}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
var CHILD_REF = keyOf({childRef: null});
|
||||
var UNDERLAY_REF = keyOf({underlayRef: null});
|
||||
var INACTIVE_CHILD_PROPS = {
|
||||
style: StyleSheet.create({x: {opacity: 1.0}}).x,
|
||||
};
|
||||
var INACTIVE_UNDERLAY_PROPS = {
|
||||
style: StyleSheet.create({x: {backgroundColor: 'transparent'}}).x,
|
||||
};
|
||||
|
||||
module.exports = TouchableHighlight;
|
||||
|
||||
@@ -32,7 +32,7 @@ const {
|
||||
AccessibilityTraits,
|
||||
} = require('ViewAccessibility');
|
||||
|
||||
import type {PressEvent} from 'CoreEventTypes';
|
||||
export type Event = Object;
|
||||
|
||||
const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
|
||||
@@ -130,19 +130,19 @@ const TouchableWithoutFeedback = createReactClass({
|
||||
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
|
||||
* defined on your component.
|
||||
*/
|
||||
touchableHandlePress: function(e: PressEvent) {
|
||||
touchableHandlePress: function(e: Event) {
|
||||
this.props.onPress && this.props.onPress(e);
|
||||
},
|
||||
|
||||
touchableHandleActivePressIn: function(e: PressEvent) {
|
||||
touchableHandleActivePressIn: function(e: Event) {
|
||||
this.props.onPressIn && this.props.onPressIn(e);
|
||||
},
|
||||
|
||||
touchableHandleActivePressOut: function(e: PressEvent) {
|
||||
touchableHandleActivePressOut: function(e: Event) {
|
||||
this.props.onPressOut && this.props.onPressOut(e);
|
||||
},
|
||||
|
||||
touchableHandleLongPress: function(e: PressEvent) {
|
||||
touchableHandleLongPress: function(e: Event) {
|
||||
this.props.onLongPress && this.props.onLongPress(e);
|
||||
},
|
||||
|
||||
@@ -186,10 +186,13 @@ const TouchableWithoutFeedback = createReactClass({
|
||||
child.props.style;
|
||||
return (React: any).cloneElement(child, {
|
||||
accessible: this.props.accessible !== false,
|
||||
// $FlowFixMe(>=0.41.0)
|
||||
accessibilityLabel: this.props.accessibilityLabel,
|
||||
accessibilityComponentType: this.props.accessibilityComponentType,
|
||||
accessibilityTraits: this.props.accessibilityTraits,
|
||||
// $FlowFixMe(>=0.41.0)
|
||||
nativeID: this.props.nativeID,
|
||||
// $FlowFixMe(>=0.41.0)
|
||||
testID: this.props.testID,
|
||||
onLayout: this.props.onLayout,
|
||||
hitSlop: this.props.hitSlop,
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
+8
-2
@@ -17,7 +17,14 @@ exports[`TouchableHighlight renders correctly 1`] = `
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={Object {}}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
},
|
||||
Object {},
|
||||
]
|
||||
}
|
||||
testID={undefined}
|
||||
tvParallaxProperties={undefined}
|
||||
>
|
||||
@@ -25,7 +32,6 @@ exports[`TouchableHighlight renders correctly 1`] = `
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
style={null}
|
||||
>
|
||||
Touchable
|
||||
</Text>
|
||||
|
||||
@@ -18,7 +18,6 @@ var ReactNativeViewAttributes = {};
|
||||
ReactNativeViewAttributes.UIView = {
|
||||
pointerEvents: true,
|
||||
accessible: true,
|
||||
accessibilityActions: true,
|
||||
accessibilityLabel: true,
|
||||
accessibilityComponentType: true,
|
||||
accessibilityLiveRegion: true,
|
||||
@@ -29,7 +28,6 @@ ReactNativeViewAttributes.UIView = {
|
||||
renderToHardwareTextureAndroid: true,
|
||||
shouldRasterizeIOS: true,
|
||||
onLayout: true,
|
||||
onAccessibilityAction: true,
|
||||
onAccessibilityTap: true,
|
||||
onMagicTap: true,
|
||||
collapsable: true,
|
||||
|
||||
@@ -28,9 +28,50 @@ import type {ViewProps} from 'ViewPropTypes';
|
||||
export type Props = ViewProps;
|
||||
|
||||
/**
|
||||
* The most fundamental component for building a UI.
|
||||
* The most fundamental component for building a UI, `View` is a container that supports layout with
|
||||
* [flexbox](docs/flexbox.html), [style](docs/style.html),
|
||||
* [some touch handling](docs/handling-touches.html), and
|
||||
* [accessibility](docs/accessibility.html) controls. `View` maps directly to the
|
||||
* native view equivalent on whatever platform React Native is running on, whether that is a
|
||||
* `UIView`, `<div>`, `android.view`, etc.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html
|
||||
* `View` is designed to be nested inside other views and can have 0 to many children of any type.
|
||||
*
|
||||
* This example creates a `View` that wraps two colored boxes and a text component in a row with
|
||||
* padding.
|
||||
*
|
||||
* ```javascript
|
||||
* class ViewColoredBoxesWithText extends Component {
|
||||
* render() {
|
||||
* return (
|
||||
* <View style={{flexDirection: 'row', height: 100, padding: 20}}>
|
||||
* <View style={{backgroundColor: 'blue', flex: 0.3}} />
|
||||
* <View style={{backgroundColor: 'red', flex: 0.5}} />
|
||||
* <Text>Hello World!</Text>
|
||||
* </View>
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* > `View`s are designed to be used with [`StyleSheet`](docs/style.html) for clarity
|
||||
* > and performance, although inline styles are also supported.
|
||||
*
|
||||
* ### Synthetic Touch Events
|
||||
*
|
||||
* For `View` responder props (e.g., `onResponderMove`), the synthetic touch event passed to them
|
||||
* are of the following form:
|
||||
*
|
||||
* - `nativeEvent`
|
||||
* - `changedTouches` - Array of all touch events that have changed since the last event.
|
||||
* - `identifier` - The ID of the touch.
|
||||
* - `locationX` - The X position of the touch, relative to the element.
|
||||
* - `locationY` - The Y position of the touch, relative to the element.
|
||||
* - `pageX` - The X position of the touch, relative to the root element.
|
||||
* - `pageY` - The Y position of the touch, relative to the root element.
|
||||
* - `target` - The node id of the element receiving the touch event.
|
||||
* - `timestamp` - A time identifier for the touch, useful for velocity calculation.
|
||||
* - `touches` - Array of all current touches on the screen.
|
||||
*/
|
||||
const View = createReactClass({
|
||||
displayName: 'View',
|
||||
|
||||
@@ -44,18 +44,16 @@ export type ViewLayoutEvent = {
|
||||
}
|
||||
}
|
||||
|
||||
// There's no easy way to create a different type if (Platform.isTVOS):
|
||||
// There's no easy way to create a different type if(Platform.isTVOS):
|
||||
// so we must include TVViewProps
|
||||
export type ViewProps = {
|
||||
accessible?: bool,
|
||||
accessibilityLabel?: React$PropType$Primitive<any>,
|
||||
accessibilityActions?: Array<string>,
|
||||
accessibilityComponentType?: AccessibilityComponentType,
|
||||
accessibilityLiveRegion?: 'none' | 'polite' | 'assertive',
|
||||
importantForAccessibility?: 'auto'| 'yes'| 'no'| 'no-hide-descendants',
|
||||
accessibilityTraits?: AccessibilityTrait | Array<AccessibilityTrait>,
|
||||
accessibilityViewIsModal?: bool,
|
||||
onAccessibilityAction?: Function,
|
||||
onAccessibilityTap?: Function,
|
||||
onMagicTap?: Function,
|
||||
testID?: string,
|
||||
@@ -85,46 +83,46 @@ module.exports = {
|
||||
...PlatformViewPropTypes,
|
||||
|
||||
/**
|
||||
* When `true`, indicates that the view is an accessibility element.
|
||||
* By default, all the touchable elements are accessible.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#accessible
|
||||
* When `true`, indicates that the view is an accessibility element. By default,
|
||||
* all the touchable elements are accessible.
|
||||
*/
|
||||
accessible: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Overrides the text that's read by the screen reader when the user interacts
|
||||
* with the element. By default, the label is constructed by traversing all
|
||||
* the children and accumulating all the `Text` nodes separated by space.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#accessibilitylabel
|
||||
* with the element. By default, the label is constructed by traversing all the
|
||||
* children and accumulating all the `Text` nodes separated by space.
|
||||
*/
|
||||
accessibilityLabel: PropTypes.node,
|
||||
|
||||
/**
|
||||
* Provides an array of custom actions available for accessibility.
|
||||
*
|
||||
* @platform ios
|
||||
*/
|
||||
accessibilityActions: PropTypes.arrayOf(PropTypes.string),
|
||||
|
||||
/**
|
||||
* Indicates to accessibility services to treat UI component like a
|
||||
* native one. Works for Android only.
|
||||
*
|
||||
* Possible values are one of:
|
||||
*
|
||||
* - `'none'`
|
||||
* - `'button'`
|
||||
* - `'radiobutton_checked'`
|
||||
* - `'radiobutton_unchecked'`
|
||||
*
|
||||
* @platform android
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#accessibilitycomponenttype
|
||||
*/
|
||||
accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentTypes),
|
||||
|
||||
/**
|
||||
* Indicates to accessibility services whether the user should be notified
|
||||
* when this view changes. Works for Android API >= 19 only.
|
||||
* Possible values:
|
||||
*
|
||||
* - `'none'` - Accessibility services should not announce changes to this view.
|
||||
* - `'polite'`- Accessibility services should announce changes to this view.
|
||||
* - `'assertive'` - Accessibility services should interrupt ongoing speech to immediately announce changes to this view.
|
||||
*
|
||||
* See the [Android `View` docs](http://developer.android.com/reference/android/view/View.html#attr_android:accessibilityLiveRegion)
|
||||
* for reference.
|
||||
*
|
||||
* @platform android
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#accessibilityliveregion
|
||||
*/
|
||||
accessibilityLiveRegion: PropTypes.oneOf([
|
||||
'none',
|
||||
@@ -137,9 +135,19 @@ module.exports = {
|
||||
* fires accessibility events and if it is reported to accessibility services
|
||||
* that query the screen. Works for Android only.
|
||||
*
|
||||
* Possible values:
|
||||
*
|
||||
* - `'auto'` - The system determines whether the view is important for accessibility -
|
||||
* default (recommended).
|
||||
* - `'yes'` - The view is important for accessibility.
|
||||
* - `'no'` - The view is not important for accessibility.
|
||||
* - `'no-hide-descendants'` - The view is not important for accessibility,
|
||||
* nor are any of its descendant views.
|
||||
*
|
||||
* See the [Android `importantForAccessibility` docs](http://developer.android.com/reference/android/R.attr.html#importantForAccessibility)
|
||||
* for reference.
|
||||
*
|
||||
* @platform android
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#importantforaccessibility
|
||||
*/
|
||||
importantForAccessibility: PropTypes.oneOf([
|
||||
'auto',
|
||||
@@ -154,9 +162,30 @@ module.exports = {
|
||||
*
|
||||
* You can provide one trait or an array of many traits.
|
||||
*
|
||||
* Possible values for `AccessibilityTraits` are:
|
||||
*
|
||||
* - `'none'` - The element has no traits.
|
||||
* - `'button'` - The element should be treated as a button.
|
||||
* - `'link'` - The element should be treated as a link.
|
||||
* - `'header'` - The element is a header that divides content into sections.
|
||||
* - `'search'` - The element should be treated as a search field.
|
||||
* - `'image'` - The element should be treated as an image.
|
||||
* - `'selected'` - The element is selected.
|
||||
* - `'plays'` - The element plays sound.
|
||||
* - `'key'` - The element should be treated like a keyboard key.
|
||||
* - `'text'` - The element should be treated as text.
|
||||
* - `'summary'` - The element provides app summary information.
|
||||
* - `'disabled'` - The element is disabled.
|
||||
* - `'frequentUpdates'` - The element frequently changes its value.
|
||||
* - `'startsMedia'` - The element starts a media session.
|
||||
* - `'adjustable'` - The element allows adjustment over a range of values.
|
||||
* - `'allowsDirectInteraction'` - The element allows direct touch interaction for VoiceOver users.
|
||||
* - `'pageTurn'` - Informs VoiceOver that it should scroll to the next page when it finishes reading the contents of the element.
|
||||
*
|
||||
* See the [Accessibility guide](docs/accessibility.html#accessibilitytraits-ios)
|
||||
* for more information.
|
||||
*
|
||||
* @platform ios
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#accessibilitytraits
|
||||
*/
|
||||
accessibilityTraits: PropTypes.oneOfType([
|
||||
PropTypes.oneOf(AccessibilityTraits),
|
||||
@@ -168,33 +197,22 @@ module.exports = {
|
||||
* within views that are siblings of the receiver.
|
||||
* Default is `false`.
|
||||
*
|
||||
* See the [Accessibility guide](docs/accessibility.html#accessibilitytraits-ios)
|
||||
* for more information.
|
||||
*
|
||||
* @platform ios
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#accessibilityviewismodal
|
||||
*/
|
||||
accessibilityViewIsModal: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* When `accessible` is true, the system will try to invoke this function
|
||||
* when the user performs an accessibility custom action.
|
||||
*
|
||||
* @platform ios
|
||||
*/
|
||||
onAccessibilityAction: PropTypes.func,
|
||||
|
||||
/**
|
||||
* When `accessible` is true, the system will try to invoke this function
|
||||
* when the user performs accessibility tap gesture.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onaccessibilitytap
|
||||
*/
|
||||
onAccessibilityTap: PropTypes.func,
|
||||
|
||||
/**
|
||||
* When `accessible` is `true`, the system will invoke this function when the
|
||||
* user performs the magic tap gesture.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onmagictap
|
||||
*/
|
||||
onMagicTap: PropTypes.func,
|
||||
|
||||
@@ -202,8 +220,6 @@ module.exports = {
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*
|
||||
* > This disables the 'layout-only view removal' optimization for this view!
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#testid
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
|
||||
@@ -211,8 +227,6 @@ module.exports = {
|
||||
* Used to locate this view from native classes.
|
||||
*
|
||||
* > This disables the 'layout-only view removal' optimization for this view!
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#nativeid
|
||||
*/
|
||||
nativeID: PropTypes.string,
|
||||
|
||||
@@ -223,111 +237,90 @@ module.exports = {
|
||||
*/
|
||||
|
||||
/**
|
||||
* The View is now responding for touch events. This is the time to highlight
|
||||
* and show the user what is happening.
|
||||
* The View is now responding for touch events. This is the time to highlight and show the user
|
||||
* what is happening.
|
||||
*
|
||||
* `View.props.onResponderGrant: (event) => {}`, where `event` is a synthetic
|
||||
* touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onrespondergrant
|
||||
* `View.props.onResponderGrant: (event) => {}`, where `event` is a synthetic touch event as
|
||||
* described above.
|
||||
*/
|
||||
onResponderGrant: PropTypes.func,
|
||||
|
||||
/**
|
||||
* The user is moving their finger.
|
||||
*
|
||||
* `View.props.onResponderMove: (event) => {}`, where `event` is a synthetic
|
||||
* touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onrespondermove
|
||||
* `View.props.onResponderMove: (event) => {}`, where `event` is a synthetic touch event as
|
||||
* described above.
|
||||
*/
|
||||
onResponderMove: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Another responder is already active and will not release it to that `View`
|
||||
* asking to be the responder.
|
||||
* Another responder is already active and will not release it to that `View` asking to be
|
||||
* the responder.
|
||||
*
|
||||
* `View.props.onResponderReject: (event) => {}`, where `event` is a
|
||||
* synthetic touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onresponderreject
|
||||
* `View.props.onResponderReject: (event) => {}`, where `event` is a synthetic touch event as
|
||||
* described above.
|
||||
*/
|
||||
onResponderReject: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Fired at the end of the touch.
|
||||
*
|
||||
* `View.props.onResponderRelease: (event) => {}`, where `event` is a
|
||||
* synthetic touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onresponderrelease
|
||||
* `View.props.onResponderRelease: (event) => {}`, where `event` is a synthetic touch event as
|
||||
* described above.
|
||||
*/
|
||||
onResponderRelease: PropTypes.func,
|
||||
|
||||
/**
|
||||
* The responder has been taken from the `View`. Might be taken by other
|
||||
* views after a call to `onResponderTerminationRequest`, or might be taken
|
||||
* by the OS without asking (e.g., happens with control center/ notification
|
||||
* center on iOS)
|
||||
* The responder has been taken from the `View`. Might be taken by other views after a call to
|
||||
* `onResponderTerminationRequest`, or might be taken by the OS without asking (e.g., happens
|
||||
* with control center/ notification center on iOS)
|
||||
*
|
||||
* `View.props.onResponderTerminate: (event) => {}`, where `event` is a
|
||||
* synthetic touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onresponderterminate
|
||||
* `View.props.onResponderTerminate: (event) => {}`, where `event` is a synthetic touch event as
|
||||
* described above.
|
||||
*/
|
||||
onResponderTerminate: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Some other `View` wants to become responder and is asking this `View` to
|
||||
* release its responder. Returning `true` allows its release.
|
||||
* Some other `View` wants to become responder and is asking this `View` to release its
|
||||
* responder. Returning `true` allows its release.
|
||||
*
|
||||
* `View.props.onResponderTerminationRequest: (event) => {}`, where `event`
|
||||
* is a synthetic touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onresponderterminationrequest
|
||||
* `View.props.onResponderTerminationRequest: (event) => {}`, where `event` is a synthetic touch
|
||||
* event as described above.
|
||||
*/
|
||||
onResponderTerminationRequest: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Does this view want to become responder on the start of a touch?
|
||||
*
|
||||
* `View.props.onStartShouldSetResponder: (event) => [true | false]`, where
|
||||
* `event` is a synthetic touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onstartshouldsetresponder
|
||||
* `View.props.onStartShouldSetResponder: (event) => [true | false]`, where `event` is a
|
||||
* synthetic touch event as described above.
|
||||
*/
|
||||
onStartShouldSetResponder: PropTypes.func,
|
||||
|
||||
/**
|
||||
* If a parent `View` wants to prevent a child `View` from becoming responder
|
||||
* on a touch start, it should have this handler which returns `true`.
|
||||
* If a parent `View` wants to prevent a child `View` from becoming responder on a touch start,
|
||||
* it should have this handler which returns `true`.
|
||||
*
|
||||
* `View.props.onStartShouldSetResponderCapture: (event) => [true | false]`,
|
||||
* where `event` is a synthetic touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onstartshouldsetrespondercapture
|
||||
* `View.props.onStartShouldSetResponderCapture: (event) => [true | false]`, where `event` is a
|
||||
* synthetic touch event as described above.
|
||||
*/
|
||||
onStartShouldSetResponderCapture: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Does this view want to "claim" touch responsiveness? This is called for
|
||||
* every touch move on the `View` when it is not the responder.
|
||||
* Does this view want to "claim" touch responsiveness? This is called for every touch move on
|
||||
* the `View` when it is not the responder.
|
||||
*
|
||||
* `View.props.onMoveShouldSetResponder: (event) => [true | false]`, where
|
||||
* `event` is a synthetic touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onmoveshouldsetresponder
|
||||
* `View.props.onMoveShouldSetResponder: (event) => [true | false]`, where `event` is a
|
||||
* synthetic touch event as described above.
|
||||
*/
|
||||
onMoveShouldSetResponder: PropTypes.func,
|
||||
|
||||
/**
|
||||
* If a parent `View` wants to prevent a child `View` from becoming responder
|
||||
* on a move, it should have this handler which returns `true`.
|
||||
*
|
||||
* `View.props.onMoveShouldSetResponderCapture: (event) => [true | false]`,
|
||||
* where `event` is a synthetic touch event as described above.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onMoveShouldsetrespondercapture
|
||||
* If a parent `View` wants to prevent a child `View` from becoming responder on a move,
|
||||
* it should have this handler which returns `true`.
|
||||
*
|
||||
* `View.props.onMoveShouldSetResponderCapture: (event) => [true | false]`, where `event` is a
|
||||
* synthetic touch event as described above.
|
||||
*/
|
||||
onMoveShouldSetResponderCapture: PropTypes.func,
|
||||
|
||||
@@ -336,11 +329,12 @@ module.exports = {
|
||||
* Typical interface guidelines recommend touch targets that are at least
|
||||
* 30 - 40 points/density-independent pixels.
|
||||
*
|
||||
* For example, if a touchable view has a height of 20 the touchable height can be extended to
|
||||
* 40 with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}`
|
||||
*
|
||||
* > The touch area never extends past the parent view bounds and the Z-index
|
||||
* > of sibling views always takes precedence if a touch hits two overlapping
|
||||
* > views.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#hitslop
|
||||
*/
|
||||
hitSlop: EdgeInsetsPropType,
|
||||
|
||||
@@ -352,15 +346,41 @@ module.exports = {
|
||||
* This event is fired immediately once the layout has been calculated, but
|
||||
* the new layout may not yet be reflected on the screen at the time the
|
||||
* event is received, especially if a layout animation is in progress.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#onlayout
|
||||
*/
|
||||
onLayout: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Controls whether the `View` can be the target of touch events.
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#pointerevents
|
||||
*
|
||||
* - `'auto'`: The View can be the target of touch events.
|
||||
* - `'none'`: The View is never the target of touch events.
|
||||
* - `'box-none'`: The View is never the target of touch events but it's
|
||||
* subviews can be. It behaves like if the view had the following classes
|
||||
* in CSS:
|
||||
* ```
|
||||
* .box-none {
|
||||
* pointer-events: none;
|
||||
* }
|
||||
* .box-none * {
|
||||
* pointer-events: all;
|
||||
* }
|
||||
* ```
|
||||
* - `'box-only'`: The view can be the target of touch events but it's
|
||||
* subviews cannot be. It behaves like if the view had the following classes
|
||||
* in CSS:
|
||||
* ```
|
||||
* .box-only {
|
||||
* pointer-events: all;
|
||||
* }
|
||||
* .box-only * {
|
||||
* pointer-events: none;
|
||||
* }
|
||||
* ```
|
||||
* > Since `pointerEvents` does not affect layout/appearance, and we are
|
||||
* > already deviating from the spec by adding additional modes, we opt to not
|
||||
* > include `pointerEvents` on `style`. On some platforms, we would need to
|
||||
* > implement it as a `className` anyways. Using `style` or not is an
|
||||
* > implementation detail of the platform.
|
||||
*/
|
||||
pointerEvents: PropTypes.oneOf([
|
||||
'box-none',
|
||||
@@ -368,10 +388,6 @@ module.exports = {
|
||||
'box-only',
|
||||
'auto',
|
||||
]),
|
||||
|
||||
/**
|
||||
* See http://facebook.github.io/react-native/docs/style.html
|
||||
*/
|
||||
style: stylePropType,
|
||||
|
||||
/**
|
||||
@@ -381,8 +397,6 @@ module.exports = {
|
||||
* view that contains many subviews that extend outside its bound. The
|
||||
* subviews must also have `overflow: hidden`, as should the containing view
|
||||
* (or one of its superviews).
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#removeclippedsubviews
|
||||
*/
|
||||
removeClippedSubviews: PropTypes.bool,
|
||||
|
||||
@@ -390,18 +404,31 @@ module.exports = {
|
||||
* Whether this `View` should render itself (and all of its children) into a
|
||||
* single hardware texture on the GPU.
|
||||
*
|
||||
* On Android, this is useful for animations and interactions that only
|
||||
* modify opacity, rotation, translation, and/or scale: in those cases, the
|
||||
* view doesn't have to be redrawn and display lists don't need to be
|
||||
* re-executed. The texture can just be re-used and re-composited with
|
||||
* different parameters. The downside is that this can use up limited video
|
||||
* memory, so this prop should be set back to false at the end of the
|
||||
* interaction/animation.
|
||||
*
|
||||
* @platform android
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#rendertohardwaretextureandroid
|
||||
*/
|
||||
renderToHardwareTextureAndroid: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether this `View` should be rendered as a bitmap before compositing.
|
||||
*
|
||||
* On iOS, this is useful for animations and interactions that do not
|
||||
* modify this component's dimensions nor its children; for example, when
|
||||
* translating the position of a static view, rasterization allows the
|
||||
* renderer to reuse a cached bitmap of a static view and quickly composite
|
||||
* it during each frame.
|
||||
*
|
||||
* Rasterization incurs an off-screen drawing pass and the bitmap consumes
|
||||
* memory. Test and measure when using this property.
|
||||
*
|
||||
* @platform ios
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#shouldrasterizeios
|
||||
*/
|
||||
shouldRasterizeIOS: PropTypes.bool,
|
||||
|
||||
@@ -412,18 +439,29 @@ module.exports = {
|
||||
* ensure that this `View` exists in the native view hierarchy.
|
||||
*
|
||||
* @platform android
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#collapsable
|
||||
*/
|
||||
collapsable: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether this `View` needs to rendered offscreen and composited with an
|
||||
* alpha in order to preserve 100% correct colors and blending behavior.
|
||||
* Whether this `View` needs to rendered offscreen and composited with an alpha
|
||||
* in order to preserve 100% correct colors and blending behavior. The default
|
||||
* (`false`) falls back to drawing the component and its children with an alpha
|
||||
* applied to the paint used to draw each element instead of rendering the full
|
||||
* component offscreen and compositing it back with an alpha value. This default
|
||||
* may be noticeable and undesired in the case where the `View` you are setting
|
||||
* an opacity on has multiple overlapping elements (e.g. multiple overlapping
|
||||
* `View`s, or text and a background).
|
||||
*
|
||||
* Rendering offscreen to preserve correct alpha behavior is extremely
|
||||
* expensive and hard to debug for non-native developers, which is why it is
|
||||
* not turned on by default. If you do need to enable this property for an
|
||||
* animation, consider combining it with renderToHardwareTextureAndroid if the
|
||||
* view **contents** are static (i.e. it doesn't need to be redrawn each frame).
|
||||
* If that property is enabled, this View will be rendered off-screen once,
|
||||
* saved in a hardware texture, and then composited onto the screen with an alpha
|
||||
* each frame without having to switch rendering targets on the GPU.
|
||||
*
|
||||
* @platform android
|
||||
*
|
||||
* See http://facebook.github.io/react-native/docs/view.html#needsoffscreenalphacompositing
|
||||
*/
|
||||
needsOffscreenAlphaCompositing: PropTypes.bool,
|
||||
};
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ let register = function () {
|
||||
if (__DEV__) {
|
||||
const AppState = require('AppState');
|
||||
const WebSocket = require('WebSocket');
|
||||
const {PlatformConstants} = require('NativeModules');
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an
|
||||
* error found when Flow v0.54 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
const reactDevTools = require('react-devtools-core');
|
||||
const getDevServer = require('getDevServer');
|
||||
|
||||
// Initialize dev tools only if the native module for WebSocket is available
|
||||
if (WebSocket.isAvailable) {
|
||||
@@ -42,11 +42,10 @@ if (__DEV__) {
|
||||
// or the code will throw for bundles that don't have it.
|
||||
const isAppActive = () => AppState.currentState !== 'background';
|
||||
|
||||
// Get hostname from development server (packager)
|
||||
const devServer = getDevServer();
|
||||
const host = devServer.bundleLoadedFromServer
|
||||
? devServer.url.replace(/https?:\/\//, '').split(':')[0]
|
||||
: 'localhost';
|
||||
// Special case: Genymotion is running on a different host.
|
||||
const host = PlatformConstants && PlatformConstants.ServerHost ?
|
||||
PlatformConstants.ServerHost.split(':')[0] :
|
||||
'localhost';
|
||||
|
||||
reactDevTools.connectToDevTools({
|
||||
isAppActive,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
/* eslint-disable strict */
|
||||
/* globals window: true */
|
||||
|
||||
/**
|
||||
@@ -100,7 +101,6 @@ if (!global.process.env.NODE_ENV) {
|
||||
// Setup the Systrace profiling hooks if necessary
|
||||
if (global.__RCTProfileIsProfiling) {
|
||||
const Systrace = require('Systrace');
|
||||
Systrace.installReactHook(true);
|
||||
Systrace.setEnabled(true);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,9 @@ if (!global.__fbDisableExceptionsManager) {
|
||||
try {
|
||||
ExceptionsManager.handleException(e, isFatal);
|
||||
} catch (ee) {
|
||||
/* eslint-disable no-console */
|
||||
console.log('Failed to print error: ', ee.message);
|
||||
/* eslint-enable no-console */
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
@@ -124,8 +126,10 @@ if (!global.__fbDisableExceptionsManager) {
|
||||
}
|
||||
|
||||
// Check for compatibility between the JS and native code
|
||||
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
|
||||
ReactNativeVersionCheck.checkVersions();
|
||||
if (__DEV__) {
|
||||
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
|
||||
ReactNativeVersionCheck.checkVersions();
|
||||
}
|
||||
|
||||
// Set up Promise
|
||||
// The native Promise implementation throws the following error:
|
||||
@@ -204,17 +208,16 @@ BatchedBridge.registerLazyCallableModule('RCTDeviceEventEmitter', () => require(
|
||||
BatchedBridge.registerLazyCallableModule('RCTNativeAppEventEmitter', () => require('RCTNativeAppEventEmitter'));
|
||||
BatchedBridge.registerLazyCallableModule('PerformanceLogger', () => require('PerformanceLogger'));
|
||||
|
||||
global.fetchSegment = function(
|
||||
segmentId: number,
|
||||
global.fetchBundle = function(
|
||||
bundleId: number,
|
||||
callback: (?Error) => void,
|
||||
) {
|
||||
const {SegmentFetcher} = require('NativeModules');
|
||||
if (!SegmentFetcher) {
|
||||
throw new Error('SegmentFetcher is missing. Please ensure that it is ' +
|
||||
'included as a NativeModule.');
|
||||
const {BundleFetcher} = require('NativeModules');
|
||||
if (!BundleFetcher) {
|
||||
throw new Error('BundleFetcher is missing');
|
||||
}
|
||||
|
||||
SegmentFetcher.fetchSegment(segmentId, (errorObject: ?{message: string, code: string}) => {
|
||||
BundleFetcher.fetchBundle(bundleId, (errorObject: ?{message: string, code: string}) => {
|
||||
if (errorObject) {
|
||||
const error = new Error(errorObject.message);
|
||||
(error: any).code = errorObject.code;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
exports.version = {
|
||||
major: 0,
|
||||
minor: 53,
|
||||
patch: 2,
|
||||
minor: 51,
|
||||
patch: 1,
|
||||
prerelease: null,
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ exports.checkVersions = function checkVersions(): void {
|
||||
ReactNativeVersion.version.major !== nativeVersion.major ||
|
||||
ReactNativeVersion.version.minor !== nativeVersion.minor
|
||||
) {
|
||||
console.error(
|
||||
throw new Error(
|
||||
`React Native version mismatch.\n\nJavaScript version: ${_formatVersion(
|
||||
ReactNativeVersion.version,
|
||||
)}\n` +
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@@ -37,24 +36,8 @@ function _setDevelopmentModeForTests(dev) {
|
||||
}
|
||||
|
||||
function _defineCheckVersionTests() {
|
||||
const consoleError = console.error;
|
||||
const globalConsole = global.console;
|
||||
|
||||
let spyOnConsoleError;
|
||||
let consoleOutput;
|
||||
|
||||
beforeEach(() => {
|
||||
consoleOutput = '';
|
||||
console.error = jest.fn();
|
||||
global.console = {error: jest.fn(error => (consoleOutput += error))};
|
||||
spyOnConsoleError = jest.spyOn(global.console, 'error');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetModules();
|
||||
console.error = consoleError;
|
||||
global.console = globalConsole;
|
||||
spyOnConsoleError.mockReset();
|
||||
});
|
||||
|
||||
it('passes when all the versions are zero', () => {
|
||||
@@ -77,43 +60,40 @@ function _defineCheckVersionTests() {
|
||||
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
|
||||
});
|
||||
|
||||
it("logs error when the minor doesn't match when the major is zero", () => {
|
||||
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');
|
||||
|
||||
ReactNativeVersionCheck.checkVersions();
|
||||
expect(spyOnConsoleError).toHaveBeenCalledTimes(1);
|
||||
expect(consoleOutput).toMatch(/React Native version mismatch/);
|
||||
expect(() => ReactNativeVersionCheck.checkVersions()).toThrowError(
|
||||
/React Native version mismatch/,
|
||||
);
|
||||
});
|
||||
|
||||
it("logs error when the major doesn't match", () => {
|
||||
it("throws when the major doesn't match", () => {
|
||||
_mockJsVersion(1, 0, 0);
|
||||
_mockNativeVersion(2, 0, 0);
|
||||
|
||||
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
|
||||
ReactNativeVersionCheck.checkVersions();
|
||||
expect(spyOnConsoleError).toHaveBeenCalledTimes(1);
|
||||
expect(consoleOutput).toMatch(/React Native version mismatch/);
|
||||
expect(() => ReactNativeVersionCheck.checkVersions()).toThrowError(
|
||||
/React Native version mismatch/,
|
||||
);
|
||||
});
|
||||
|
||||
it("doesn't log error if the patch doesn't match", () => {
|
||||
it("doesn't throw if the patch doesn't match", () => {
|
||||
_mockJsVersion(0, 1, 0);
|
||||
_mockNativeVersion(0, 1, 2);
|
||||
|
||||
const ReactNativeVersionCheck = require('ReactNativeVersionCheck');
|
||||
ReactNativeVersionCheck.checkVersions();
|
||||
expect(spyOnConsoleError).toHaveBeenCalledTimes(0);
|
||||
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
|
||||
});
|
||||
|
||||
it("doesn't log error if the prerelease doesn't match", () => {
|
||||
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');
|
||||
ReactNativeVersionCheck.checkVersions();
|
||||
expect(spyOnConsoleError).toHaveBeenCalledTimes(0);
|
||||
expect(() => ReactNativeVersionCheck.checkVersions()).not.toThrow();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -115,9 +115,15 @@ class SwipeableListView extends React.Component<Props, State> {
|
||||
|
||||
render(): React.Node {
|
||||
return (
|
||||
/* $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. */
|
||||
<ListView
|
||||
{...this.props}
|
||||
ref={(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._listViewRef = ref;
|
||||
}}
|
||||
dataSource={this.state.dataSource.getDataSource()}
|
||||
|
||||
@@ -88,16 +88,6 @@ class SwipeableListViewDataSource {
|
||||
return Object.keys(this._dataBlob)[0];
|
||||
}
|
||||
|
||||
getLastRowID(): ?string {
|
||||
if (this.rowIdentities && this.rowIdentities.length) {
|
||||
const lastSection = this.rowIdentities[this.rowIdentities.length - 1];
|
||||
if (lastSection && lastSection.length) {
|
||||
return lastSection[lastSection.length - 1];
|
||||
}
|
||||
}
|
||||
return Object.keys(this._dataBlob)[this._dataBlob.length - 1];
|
||||
}
|
||||
|
||||
setOpenRowID(rowID: string): SwipeableListViewDataSource {
|
||||
this._previousOpenRowID = this._openRowID;
|
||||
this._openRowID = rowID;
|
||||
|
||||
@@ -176,11 +176,6 @@ class WindowedListView extends React.Component<Props, State> {
|
||||
maxNumToRender: 30,
|
||||
numToRenderAhead: 10,
|
||||
viewablePercentThreshold: 50,
|
||||
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an
|
||||
* error caught by Flow 0.59 which was not caught before. Most likely, this
|
||||
* error is because an exported function parameter is missing an
|
||||
* annotation. Without an annotation, these parameters are uncovered by
|
||||
* Flow. */
|
||||
renderScrollComponent: (props) => <ScrollView {...props} />,
|
||||
disableIncrementalRendering: false,
|
||||
recomputeRowsBatchingPeriod: 10, // This should capture most events that happen within a frame
|
||||
@@ -687,9 +682,6 @@ class CellRenderer extends React.Component<CellProps> {
|
||||
}
|
||||
};
|
||||
componentWillUnmount() {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
clearTimeout(this._timeout);
|
||||
this.props.onProgressChange({rowKey: this.props.rowKey, inProgress: false});
|
||||
this.props.onWillUnmount(this.props.rowKey);
|
||||
|
||||
@@ -50,17 +50,14 @@ function getAssetPathInDrawableFolder(asset): string {
|
||||
class AssetSourceResolver {
|
||||
|
||||
serverUrl: ?string;
|
||||
// where the jsbundle is being run from
|
||||
jsbundleUrl: ?string;
|
||||
// where the bundle is being run from
|
||||
bundleUrl: ?string;
|
||||
// the asset to resolve
|
||||
asset: PackagerAsset;
|
||||
|
||||
constructor(serverUrl: ?string,
|
||||
jsbundleUrl: ?string,
|
||||
asset: PackagerAsset
|
||||
) {
|
||||
constructor(serverUrl: ?string, bundleUrl: ?string, asset: PackagerAsset) {
|
||||
this.serverUrl = serverUrl;
|
||||
this.jsbundleUrl = jsbundleUrl;
|
||||
this.bundleUrl = bundleUrl;
|
||||
this.asset = asset;
|
||||
}
|
||||
|
||||
@@ -69,7 +66,7 @@ class AssetSourceResolver {
|
||||
}
|
||||
|
||||
isLoadedFromFileSystem(): boolean {
|
||||
return !!(this.jsbundleUrl && this.jsbundleUrl.startsWith('file://'));
|
||||
return !!this.bundleUrl;
|
||||
}
|
||||
|
||||
defaultAsset(): ResolvedAssetSource {
|
||||
@@ -82,7 +79,7 @@ class AssetSourceResolver {
|
||||
this.drawableFolderInBundle() :
|
||||
this.resourceIdentifierWithoutScale();
|
||||
} else {
|
||||
return this.scaledAssetURLNearBundle();
|
||||
return this.scaledAssetURLInBundle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +107,8 @@ class AssetSourceResolver {
|
||||
* Resolves to where the bundle is running from, with a scaled asset filename
|
||||
* E.g. 'file:///sdcard/bundle/assets/AwesomeModule/icon@2x.png'
|
||||
*/
|
||||
scaledAssetURLNearBundle(): ResolvedAssetSource {
|
||||
const path = this.jsbundleUrl || 'file://';
|
||||
scaledAssetURLInBundle(): ResolvedAssetSource {
|
||||
const path = this.bundleUrl || 'file://';
|
||||
return this.fromSource(path + getScaledAssetPath(this.asset));
|
||||
}
|
||||
|
||||
@@ -132,7 +129,7 @@ class AssetSourceResolver {
|
||||
* E.g. 'file:///sdcard/AwesomeModule/drawable-mdpi/icon.png'
|
||||
*/
|
||||
drawableFolderInBundle(): ResolvedAssetSource {
|
||||
const path = this.jsbundleUrl || 'file://';
|
||||
const path = this.bundleUrl || 'file://';
|
||||
return this.fromSource(
|
||||
path + getAssetPathInDrawableFolder(this.asset)
|
||||
);
|
||||
|
||||
@@ -55,7 +55,7 @@ function generateRequestId() {
|
||||
* />
|
||||
* <Image
|
||||
* style={styles.logo}
|
||||
* source={{uri: 'https://facebook.github.io/react-native/img/opengraph.png'}}
|
||||
* source={{uri: 'https://facebook.github.io/react/logo-og.png'}}
|
||||
* />
|
||||
* </View>
|
||||
* );
|
||||
@@ -274,7 +274,7 @@ var Image = createReactClass({
|
||||
}
|
||||
|
||||
if (this.props.children) {
|
||||
throw new Error('The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.');
|
||||
throw new Error('The <Image> component cannot contain children. If you want to render content on top of the image, consider using aboslute positioning.');
|
||||
}
|
||||
|
||||
if (source && (source.uri || Array.isArray(source))) {
|
||||
|
||||
@@ -374,7 +374,7 @@ const Image = createReactClass({
|
||||
}
|
||||
|
||||
if (this.props.children) {
|
||||
throw new Error('The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.');
|
||||
throw new Error('The <Image> component cannot contain children. If you want to render content on top of the image, consider using aboslute positioning.');
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -33,7 +33,7 @@ import type {NativeMethodsMixinType} from 'ReactNativeTypes';
|
||||
* return (
|
||||
* <ImageBackground
|
||||
* style={{width: 50, height: 50}}
|
||||
* source={{uri: 'https://facebook.github.io/react-native/img/opengraph.png'}}
|
||||
* source={{uri: 'https://facebook.github.io/react/logo-og.png'}}
|
||||
* >
|
||||
* <Text>React</Text>
|
||||
* </ImageBackground>
|
||||
|
||||
@@ -38,18 +38,6 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* If available, RCTImageRedirectProtocol is invoked before loading an asset.
|
||||
* Implementation should return either a new URL or nil when redirection is
|
||||
* not needed.
|
||||
*/
|
||||
|
||||
@protocol RCTImageRedirectProtocol
|
||||
|
||||
- (NSURL *)redirectAssetsURL:(NSURL *)URL;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIImage (React)
|
||||
|
||||
@property (nonatomic, copy) CAKeyframeAnimation *reactKeyframeAnimation;
|
||||
@@ -83,9 +71,6 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
|
||||
*/
|
||||
@property (nonatomic, assign) NSUInteger maxConcurrentDecodingBytes;
|
||||
|
||||
- (instancetype)init;
|
||||
- (instancetype)initWithRedirectDelegate:(id<RCTImageRedirectProtocol>)redirectDelegate NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Loads the specified image at the highest available resolution.
|
||||
* Can be called from any thread, will call back on an unspecified thread.
|
||||
|
||||
@@ -48,26 +48,12 @@
|
||||
NSMutableArray *_pendingDecodes;
|
||||
NSInteger _scheduledDecodes;
|
||||
NSUInteger _activeBytes;
|
||||
__weak id<RCTImageRedirectProtocol> _redirectDelegate;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithRedirectDelegate:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithRedirectDelegate:(id<RCTImageRedirectProtocol>)redirectDelegate
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_redirectDelegate = redirectDelegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
// Set defaults
|
||||
@@ -330,9 +316,6 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
if (request.URL.fileURL && request.URL.pathExtension.length == 0) {
|
||||
mutableRequest.URL = [request.URL URLByAppendingPathExtension:@"png"];
|
||||
}
|
||||
if (_redirectDelegate != nil) {
|
||||
mutableRequest.URL = [_redirectDelegate redirectAssetsURL:mutableRequest.URL];
|
||||
}
|
||||
request = mutableRequest;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
'use strict';
|
||||
|
||||
// This is a stub for flow to make it understand require('./icon.png')
|
||||
// See metro/src/Bundler/index.js
|
||||
// See metro-bundler/src/Bundler/index.js
|
||||
|
||||
var AssetRegistry = require('AssetRegistry');
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@@ -146,7 +144,7 @@ describe('resolveAssetSource', () => {
|
||||
height: 200,
|
||||
scales: [1],
|
||||
hash: '5b6f00f',
|
||||
name: '!@Logo#1_\u20ac', // Invalid chars shouldn't get passed to native
|
||||
name: '!@Logo#1_€', // Invalid chars shouldn't get passed to native
|
||||
type: 'png',
|
||||
}, {
|
||||
__packager_asset: true,
|
||||
@@ -174,7 +172,7 @@ describe('resolveAssetSource', () => {
|
||||
height: 200,
|
||||
scales: [1],
|
||||
hash: '5b6f00f',
|
||||
name: '!@Logo#1_\u20ac',
|
||||
name: '!@Logo#1_€',
|
||||
type: 'png',
|
||||
}, {
|
||||
__packager_asset: true,
|
||||
@@ -202,7 +200,7 @@ describe('resolveAssetSource', () => {
|
||||
height: 200,
|
||||
scales: [1],
|
||||
hash: '5b6f00f',
|
||||
name: '!@Logo#1_\u20ac',
|
||||
name: '!@Logo#1_€',
|
||||
type: 'png',
|
||||
}, {
|
||||
__packager_asset: true,
|
||||
@@ -233,7 +231,7 @@ describe('resolveAssetSource', () => {
|
||||
height: 200,
|
||||
scales: [1],
|
||||
hash: '5b6f00f',
|
||||
name: '!@Logo#1_\u20ac',
|
||||
name: '!@Logo#1_€',
|
||||
type: 'png',
|
||||
}, {
|
||||
__packager_asset: true,
|
||||
@@ -256,7 +254,7 @@ describe('resolveAssetSource', () => {
|
||||
height: 200,
|
||||
scales: [1],
|
||||
hash: '5b6f00f',
|
||||
name: '!@Logo#1_\u20ac',
|
||||
name: '!@Logo#1_€',
|
||||
type: 'png',
|
||||
}, {
|
||||
__packager_asset: true,
|
||||
|
||||
@@ -19,45 +19,45 @@ const NativeModules = require('NativeModules');
|
||||
|
||||
import type { ResolvedAssetSource } from 'AssetSourceResolver';
|
||||
|
||||
let _customSourceTransformer, _serverURL, _scriptURL;
|
||||
let _customSourceTransformer, _serverURL, _bundleSourceURL;
|
||||
|
||||
function getDevServerURL(): ?string {
|
||||
if (_serverURL === undefined) {
|
||||
var scriptURL = NativeModules.SourceCode.scriptURL;
|
||||
var match = scriptURL && scriptURL.match(/^https?:\/\/.*?\//);
|
||||
if (match) {
|
||||
// jsBundle was loaded from network
|
||||
// Bundle was loaded from network
|
||||
_serverURL = match[0];
|
||||
} else {
|
||||
// jsBundle was loaded from file
|
||||
// Bundle was loaded from file
|
||||
_serverURL = null;
|
||||
}
|
||||
}
|
||||
return _serverURL;
|
||||
}
|
||||
|
||||
function _coerceLocalScriptURL(scriptURL: ?string): ?string {
|
||||
if (scriptURL) {
|
||||
function getBundleSourceURL(): ?string {
|
||||
if (_bundleSourceURL === undefined) {
|
||||
const scriptURL = NativeModules.SourceCode.scriptURL;
|
||||
if (!scriptURL) {
|
||||
// scriptURL is falsy, we have nothing to go on here
|
||||
_bundleSourceURL = null;
|
||||
return _bundleSourceURL;
|
||||
}
|
||||
if (scriptURL.startsWith('assets://')) {
|
||||
// android: running from within assets, no offline path to use
|
||||
return null;
|
||||
_bundleSourceURL = null;
|
||||
return _bundleSourceURL;
|
||||
}
|
||||
scriptURL = scriptURL.substring(0, scriptURL.lastIndexOf('/') + 1);
|
||||
if (!scriptURL.includes('://')) {
|
||||
_bundleSourceURL = scriptURL.substring(0, scriptURL.lastIndexOf('/') + 1);
|
||||
if (!scriptURL.startsWith('file://')) {
|
||||
// Add file protocol in case we have an absolute file path and not a URL.
|
||||
// This shouldn't really be necessary. scriptURL should be a URL.
|
||||
scriptURL = 'file://' + scriptURL;
|
||||
_bundleSourceURL = 'file://' + _bundleSourceURL;
|
||||
}
|
||||
}
|
||||
return scriptURL;
|
||||
}
|
||||
|
||||
function getScriptURL(): ?string {
|
||||
if (_scriptURL === undefined) {
|
||||
const scriptURL = NativeModules.SourceCode.scriptURL;
|
||||
_scriptURL = _coerceLocalScriptURL(scriptURL);
|
||||
}
|
||||
return _scriptURL;
|
||||
return _bundleSourceURL;
|
||||
}
|
||||
|
||||
function setCustomSourceTransformer(
|
||||
@@ -80,11 +80,7 @@ function resolveAssetSource(source: any): ?ResolvedAssetSource {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resolver = new AssetSourceResolver(
|
||||
getDevServerURL(),
|
||||
getScriptURL(),
|
||||
asset,
|
||||
);
|
||||
const resolver = new AssetSourceResolver(getDevServerURL(), getBundleSourceURL(), asset);
|
||||
if (_customSourceTransformer) {
|
||||
return _customSourceTransformer(resolver);
|
||||
}
|
||||
|
||||
@@ -25,11 +25,9 @@ const flattenStyle = require('flattenStyle');
|
||||
const mapWithSeparator = require('mapWithSeparator');
|
||||
const openFileInEditor = require('openFileInEditor');
|
||||
|
||||
import type {StyleObj} from 'StyleSheetTypes';
|
||||
|
||||
class ElementProperties extends React.Component<{
|
||||
hierarchy: Array<$FlowFixMe>,
|
||||
style?: StyleObj,
|
||||
style?: Object | Array<$FlowFixMe> | number,
|
||||
source?: {
|
||||
fileName?: string,
|
||||
lineNumber?: number,
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
/* eslint-disable dot-notation, no-dimensions-get-window */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Dimensions = require('Dimensions');
|
||||
@@ -99,9 +101,6 @@ class Inspector extends React.Component<{
|
||||
attachToDevtools = (agent: Object) => {
|
||||
let _hideWait = null;
|
||||
const hlSub = agent.sub('highlight', ({node, name, props}) => {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
clearTimeout(_hideWait);
|
||||
|
||||
if (typeof node !== 'number') {
|
||||
|
||||
@@ -168,9 +168,6 @@ declare function setImmediate(callback: any, ...args: Array<any>): number;
|
||||
function _scheduleUpdate() {
|
||||
if (!_nextUpdateHandle) {
|
||||
if (_deadline > 0) {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
_nextUpdateHandle = setTimeout(_processUpdate, 0 + DEBUG_DELAY);
|
||||
} else {
|
||||
_nextUpdateHandle = setImmediate(_processUpdate);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||||
|
||||
+15
-33
@@ -16,7 +16,6 @@ const MetroListView = require('MetroListView'); // Used as a fallback legacy opt
|
||||
const React = require('React');
|
||||
const View = require('View');
|
||||
const VirtualizedList = require('VirtualizedList');
|
||||
const ListView = require('ListView');
|
||||
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
|
||||
@@ -28,12 +27,6 @@ import type {
|
||||
} from 'ViewabilityHelper';
|
||||
import type {Props as VirtualizedListProps} from 'VirtualizedList';
|
||||
|
||||
export type SeparatorsObj = {
|
||||
highlight: () => void,
|
||||
unhighlight: () => void,
|
||||
updateProps: (select: 'leading' | 'trailing', newProps: Object) => void,
|
||||
};
|
||||
|
||||
type RequiredProps<ItemT> = {
|
||||
/**
|
||||
* Takes an item from `data` and renders it into the list. Example usage:
|
||||
@@ -64,7 +57,11 @@ type RequiredProps<ItemT> = {
|
||||
renderItem: (info: {
|
||||
item: ItemT,
|
||||
index: number,
|
||||
separators: SeparatorsObj,
|
||||
separators: {
|
||||
highlight: () => void,
|
||||
unhighlight: () => void,
|
||||
updateProps: (select: 'leading' | 'trailing', newProps: Object) => void,
|
||||
},
|
||||
}) => ?React.Element<any>,
|
||||
/**
|
||||
* For simplicity, data is just a plain array. If you want to use something else, like an
|
||||
@@ -329,9 +326,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
* Scrolls to the end of the content. May be janky without `getItemLayout` prop.
|
||||
*/
|
||||
scrollToEnd(params?: ?{animated?: ?boolean}) {
|
||||
if (this._listRef) {
|
||||
this._listRef.scrollToEnd(params);
|
||||
}
|
||||
this._listRef.scrollToEnd(params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,9 +343,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
viewOffset?: number,
|
||||
viewPosition?: number,
|
||||
}) {
|
||||
if (this._listRef) {
|
||||
this._listRef.scrollToIndex(params);
|
||||
}
|
||||
this._listRef.scrollToIndex(params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,9 +357,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
item: ItemT,
|
||||
viewPosition?: number,
|
||||
}) {
|
||||
if (this._listRef) {
|
||||
this._listRef.scrollToItem(params);
|
||||
}
|
||||
this._listRef.scrollToItem(params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,9 +366,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
* Check out [scrollToOffset](docs/virtualizedlist.html#scrolltooffset) of VirtualizedList
|
||||
*/
|
||||
scrollToOffset(params: {animated?: ?boolean, offset: number}) {
|
||||
if (this._listRef) {
|
||||
this._listRef.scrollToOffset(params);
|
||||
}
|
||||
this._listRef.scrollToOffset(params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,9 +375,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
* taps on items or by navigation actions.
|
||||
*/
|
||||
recordInteraction() {
|
||||
if (this._listRef) {
|
||||
this._listRef.recordInteraction();
|
||||
}
|
||||
this._listRef.recordInteraction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,9 +384,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
* @platform ios
|
||||
*/
|
||||
flashScrollIndicators() {
|
||||
if (this._listRef) {
|
||||
this._listRef.flashScrollIndicators();
|
||||
}
|
||||
this._listRef.flashScrollIndicators();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,9 +447,6 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
}),
|
||||
);
|
||||
} else if (this.props.onViewableItemsChanged) {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
this._virtualizedListPairs.push({
|
||||
viewabilityConfig: this.props.viewabilityConfig,
|
||||
onViewableItemsChanged: this._createOnViewableItemsChanged(
|
||||
@@ -475,10 +457,13 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
}
|
||||
|
||||
_hasWarnedLegacy = false;
|
||||
_listRef: null | VirtualizedList | ListView;
|
||||
_listRef: VirtualizedList;
|
||||
_virtualizedListPairs: Array<ViewabilityConfigCallbackPair> = [];
|
||||
|
||||
_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._listRef = ref;
|
||||
};
|
||||
|
||||
@@ -558,9 +543,6 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
.map((it, kk) => keyExtractor(it, index * numColumns + kk))
|
||||
.join(':');
|
||||
} else {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
return keyExtractor(items, index);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -98,6 +98,9 @@ var DEFAULT_SCROLL_CALLBACK_THROTTLE = 50;
|
||||
* rendering rows.
|
||||
*/
|
||||
|
||||
/* $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. */
|
||||
var ListView = createReactClass({
|
||||
displayName: 'ListView',
|
||||
_childFrames: ([]: Array<Object>),
|
||||
@@ -519,8 +522,14 @@ var ListView = createReactClass({
|
||||
if (props.removeClippedSubviews === undefined) {
|
||||
props.removeClippedSubviews = true;
|
||||
}
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This comment
|
||||
* suppresses an error found when Flow v0.54 was deployed. To see the error
|
||||
* delete this comment and run Flow. */
|
||||
Object.assign(props, {
|
||||
onScroll: this._onScroll,
|
||||
/* $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. */
|
||||
stickyHeaderIndices: this.props.stickyHeaderIndices.concat(
|
||||
stickySectionHeaderIndices,
|
||||
),
|
||||
|
||||
@@ -19,11 +19,6 @@ const StaticRenderer = require('StaticRenderer');
|
||||
class ListViewMock extends React.Component<$FlowFixMeProps> {
|
||||
static latestRef: ?ListViewMock;
|
||||
static defaultProps = {
|
||||
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an
|
||||
* error caught by Flow 0.59 which was not caught before. Most likely, this
|
||||
* error is because an exported function parameter is missing an
|
||||
* annotation. Without an annotation, these parameters are uncovered by
|
||||
* Flow. */
|
||||
renderScrollComponent: props => <ScrollView {...props} />,
|
||||
};
|
||||
componentDidMount() {
|
||||
|
||||
@@ -107,6 +107,9 @@ class MetroListView extends React.Component<Props, $FlowFixMeState> {
|
||||
renderScrollComponent: (props: Props) => {
|
||||
if (props.onRefresh) {
|
||||
return (
|
||||
/* $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. */
|
||||
<ScrollView
|
||||
{...props}
|
||||
refreshControl={
|
||||
@@ -122,6 +125,9 @@ class MetroListView extends React.Component<Props, $FlowFixMeState> {
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
/* $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 <ScrollView {...props} />;
|
||||
}
|
||||
},
|
||||
@@ -140,6 +146,9 @@ class MetroListView extends React.Component<Props, $FlowFixMeState> {
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
/* $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. */
|
||||
<ListView
|
||||
{...this.props}
|
||||
dataSource={this.state.ds}
|
||||
@@ -153,6 +162,9 @@ class MetroListView extends React.Component<Props, $FlowFixMeState> {
|
||||
}
|
||||
_listRef: ListView;
|
||||
_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._listRef = ref;
|
||||
};
|
||||
_computeState(props: Props, state) {
|
||||
|
||||
@@ -71,7 +71,7 @@ type OptionalProps<SectionT: SectionBase<any>> = {
|
||||
/**
|
||||
* Default renderer for every item in every section. Can be over-ridden on a per-section basis.
|
||||
*/
|
||||
renderItem?: (info: {
|
||||
renderItem: (info: {
|
||||
item: Item,
|
||||
index: number,
|
||||
section: SectionT,
|
||||
|
||||
@@ -60,23 +60,21 @@ export type ViewabilityConfig = {|
|
||||
|};
|
||||
|
||||
/**
|
||||
* A Utility class for calculating viewable items based on current metrics like scroll position and
|
||||
* layout.
|
||||
*
|
||||
* An item is said to be in a "viewable" state when any of the following
|
||||
* is true for longer than `minimumViewTime` milliseconds (after an interaction if `waitForInteraction`
|
||||
* is true):
|
||||
*
|
||||
* - Occupying >= `viewAreaCoveragePercentThreshold` of the view area XOR fraction of the item
|
||||
* visible in the view area >= `itemVisiblePercentThreshold`.
|
||||
* - Entirely visible on screen
|
||||
*/
|
||||
* A Utility class for calculating viewable items based on current metrics like scroll position and
|
||||
* layout.
|
||||
*
|
||||
* An item is said to be in a "viewable" state when any of the following
|
||||
* is true for longer than `minimumViewTime` milliseconds (after an interaction if `waitForInteraction`
|
||||
* is true):
|
||||
*
|
||||
* - Occupying >= `viewAreaCoveragePercentThreshold` of the view area XOR fraction of the item
|
||||
* visible in the view area >= `itemVisiblePercentThreshold`.
|
||||
* - Entirely visible on screen
|
||||
*/
|
||||
class ViewabilityHelper {
|
||||
_config: ViewabilityConfig;
|
||||
_hasInteracted: boolean = false;
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an error
|
||||
* found when Flow v0.63 was deployed. To see the error delete this comment
|
||||
* and run Flow. */
|
||||
_lastUpdateTime: number = 0;
|
||||
_timers: Set<number> = new Set();
|
||||
_viewableIndices: Array<number> = [];
|
||||
_viewableItems: Map<string, ViewToken> = new Map();
|
||||
@@ -172,11 +170,15 @@ class ViewabilityHelper {
|
||||
}) => void,
|
||||
renderRange?: {first: number, last: number}, // Optional optimization to reduce the scan size
|
||||
): void {
|
||||
if (
|
||||
(this._config.waitForInteraction && !this._hasInteracted) ||
|
||||
itemCount === 0 ||
|
||||
!getFrameMetrics(0)
|
||||
) {
|
||||
const updateTime = Date.now();
|
||||
if (this._lastUpdateTime === 0 && itemCount > 0 && getFrameMetrics(0)) {
|
||||
// Only count updates after the first item is rendered and has a frame.
|
||||
this._lastUpdateTime = updateTime;
|
||||
}
|
||||
const updateElapsed = this._lastUpdateTime
|
||||
? updateTime - this._lastUpdateTime
|
||||
: 0;
|
||||
if (this._config.waitForInteraction && !this._hasInteracted) {
|
||||
return;
|
||||
}
|
||||
let viewableIndices = [];
|
||||
@@ -198,7 +200,11 @@ class ViewabilityHelper {
|
||||
return;
|
||||
}
|
||||
this._viewableIndices = viewableIndices;
|
||||
if (this._config.minimumViewTime) {
|
||||
this._lastUpdateTime = updateTime;
|
||||
if (
|
||||
this._config.minimumViewTime &&
|
||||
updateElapsed < this._config.minimumViewTime
|
||||
) {
|
||||
const handle = setTimeout(() => {
|
||||
this._timers.delete(handle);
|
||||
this._onUpdateSync(
|
||||
|
||||
@@ -25,19 +25,17 @@ function elementsThatOverlapOffsets(
|
||||
getFrameMetrics: (index: number) => {length: number, offset: number},
|
||||
): Array<number> {
|
||||
const out = [];
|
||||
let outLength = 0;
|
||||
for (let ii = 0; ii < itemCount; ii++) {
|
||||
const frame = getFrameMetrics(ii);
|
||||
const trailingOffset = frame.offset + frame.length;
|
||||
for (let kk = 0; kk < offsets.length; kk++) {
|
||||
if (out[kk] == null && trailingOffset >= offsets[kk]) {
|
||||
out[kk] = ii;
|
||||
outLength++;
|
||||
if (kk === offsets.length - 1) {
|
||||
invariant(
|
||||
outLength === offsets.length,
|
||||
'bad offsets input, should be in increasing order: %s',
|
||||
JSON.stringify(offsets),
|
||||
out.length === offsets.length,
|
||||
'bad offsets input, should be in increasing order ' +
|
||||
JSON.stringify(offsets),
|
||||
);
|
||||
return out;
|
||||
}
|
||||
@@ -116,15 +114,6 @@ function computeWindowedRenderLimits(
|
||||
);
|
||||
const overscanEnd = Math.max(0, visibleEnd + leadFactor * overscanLength);
|
||||
|
||||
const lastItemOffset = getFrameMetricsApprox(itemCount - 1).offset;
|
||||
if (lastItemOffset < overscanBegin) {
|
||||
// Entire list is before our overscan window
|
||||
return {
|
||||
first: Math.max(0, itemCount - 1 - maxToRenderPerBatch),
|
||||
last: itemCount - 1,
|
||||
};
|
||||
}
|
||||
|
||||
// Find the indices that correspond to the items at the render boundaries we're targetting.
|
||||
let [overscanFirst, first, last, overscanLast] = elementsThatOverlapOffsets(
|
||||
[overscanBegin, visibleBegin, visibleEnd, overscanEnd],
|
||||
|
||||
@@ -20,7 +20,6 @@ const ReactNative = require('ReactNative');
|
||||
const RefreshControl = require('RefreshControl');
|
||||
const ScrollView = require('ScrollView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const UIManager = require('UIManager');
|
||||
const View = require('View');
|
||||
const ViewabilityHelper = require('ViewabilityHelper');
|
||||
|
||||
@@ -130,12 +129,6 @@ type OptionalProps = {
|
||||
* a rendered element.
|
||||
*/
|
||||
ListHeaderComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
||||
/**
|
||||
* A unique identifier for this list. If there are multiple VirtualizedLists at the same level of
|
||||
* nesting within another VirtualizedList, this key is necessary for virtualization to
|
||||
* work properly.
|
||||
*/
|
||||
listKey?: string,
|
||||
/**
|
||||
* The maximum number of items to render in each incremental render batch. The more rendered at
|
||||
* once, the better the fill rate, but responsiveness my suffer because rendering content may
|
||||
@@ -213,19 +206,6 @@ export type Props = RequiredProps & OptionalProps;
|
||||
|
||||
let _usedIndexForKey = false;
|
||||
|
||||
type Frame = {
|
||||
offset: number,
|
||||
length: number,
|
||||
index: number,
|
||||
inLayout: boolean,
|
||||
};
|
||||
|
||||
type ChildListState = {
|
||||
first: number,
|
||||
last: number,
|
||||
frames: {[key: number]: Frame},
|
||||
};
|
||||
|
||||
type State = {first: number, last: number};
|
||||
|
||||
/**
|
||||
@@ -432,99 +412,26 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
virtualizedCell: PropTypes.shape({
|
||||
cellKey: PropTypes.string,
|
||||
}),
|
||||
virtualizedList: PropTypes.shape({
|
||||
getScrollMetrics: PropTypes.func,
|
||||
horizontal: PropTypes.bool,
|
||||
getOutermostParentListRef: PropTypes.func,
|
||||
registerAsNestedChild: PropTypes.func,
|
||||
unregisterAsNestedChild: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
virtualizedList: PropTypes.shape({
|
||||
getScrollMetrics: PropTypes.func,
|
||||
horizontal: PropTypes.bool,
|
||||
getOutermostParentListRef: PropTypes.func,
|
||||
registerAsNestedChild: PropTypes.func,
|
||||
unregisterAsNestedChild: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
virtualizedList: {
|
||||
getScrollMetrics: this._getScrollMetrics,
|
||||
horizontal: this.props.horizontal,
|
||||
getOutermostParentListRef: this._getOutermostParentListRef,
|
||||
registerAsNestedChild: this._registerAsNestedChild,
|
||||
unregisterAsNestedChild: this._unregisterAsNestedChild,
|
||||
// TODO: support nested virtualization and onViewableItemsChanged
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_getCellKey(): string {
|
||||
return (
|
||||
(this.context.virtualizedCell && this.context.virtualizedCell.cellKey) ||
|
||||
'rootList'
|
||||
);
|
||||
}
|
||||
|
||||
_getScrollMetrics = () => {
|
||||
return this._scrollMetrics;
|
||||
};
|
||||
|
||||
hasMore(): boolean {
|
||||
return this._hasMore;
|
||||
}
|
||||
|
||||
_getOutermostParentListRef = () => {
|
||||
if (this._isNestedWithSameOrientation()) {
|
||||
return this.context.virtualizedList.getOutermostParentListRef();
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
_registerAsNestedChild = (childList: {
|
||||
cellKey: string,
|
||||
key: string,
|
||||
ref: VirtualizedList,
|
||||
}): ?ChildListState => {
|
||||
// Register the mapping between this child key and the cellKey for its cell
|
||||
const childListsInCell =
|
||||
this._cellKeysToChildListKeys.get(childList.cellKey) || new Set();
|
||||
childListsInCell.add(childList.key);
|
||||
this._cellKeysToChildListKeys.set(childList.cellKey, childListsInCell);
|
||||
|
||||
const existingChildData = this._nestedChildLists.get(childList.key);
|
||||
invariant(
|
||||
!(existingChildData && existingChildData.ref !== null),
|
||||
'A VirtualizedList contains a cell which itself contains ' +
|
||||
'more than one VirtualizedList of the same orientation as the parent ' +
|
||||
'list. You must pass a unique listKey prop to each sibling list.',
|
||||
);
|
||||
this._nestedChildLists.set(childList.key, {
|
||||
ref: childList.ref,
|
||||
state: null,
|
||||
});
|
||||
|
||||
return existingChildData && existingChildData.state;
|
||||
};
|
||||
|
||||
_unregisterAsNestedChild = (childList: {
|
||||
key: string,
|
||||
state: ChildListState,
|
||||
}): void => {
|
||||
this._nestedChildLists.set(childList.key, {
|
||||
ref: null,
|
||||
state: childList.state,
|
||||
});
|
||||
};
|
||||
|
||||
state: State;
|
||||
|
||||
constructor(props: Props, context: Object) {
|
||||
@@ -534,10 +441,10 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
'Components based on VirtualizedList must be wrapped with Animated.createAnimatedComponent ' +
|
||||
'to support native onScroll events with useNativeDriver',
|
||||
);
|
||||
|
||||
invariant(
|
||||
props.windowSize > 0,
|
||||
'VirtualizedList: The windowSize prop must be present and set to a value greater than 0.',
|
||||
!(this._isNestedWithSameOrientation() && props.onViewableItemsChanged),
|
||||
'Nesting lists that scroll in the same direction does not support onViewableItemsChanged' +
|
||||
'on the inner list.',
|
||||
);
|
||||
|
||||
this._fillRateHelper = new FillRateHelper(this._getFrameMetrics);
|
||||
@@ -560,7 +467,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
let initialState = {
|
||||
this.state = {
|
||||
first: this.props.initialScrollIndex || 0,
|
||||
last:
|
||||
Math.min(
|
||||
@@ -568,30 +475,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
(this.props.initialScrollIndex || 0) + this.props.initialNumToRender,
|
||||
) - 1,
|
||||
};
|
||||
|
||||
if (this._isNestedWithSameOrientation()) {
|
||||
const storedState = this.context.virtualizedList.registerAsNestedChild({
|
||||
cellKey: this._getCellKey(),
|
||||
key: this.props.listKey || this._getCellKey(),
|
||||
ref: this,
|
||||
});
|
||||
if (storedState) {
|
||||
initialState = storedState;
|
||||
this.state = storedState;
|
||||
this._frames = storedState.frames;
|
||||
}
|
||||
}
|
||||
|
||||
this.state = initialState;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.initialScrollIndex) {
|
||||
this._initialScrollIndexTimeout = setTimeout(
|
||||
() =>
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses
|
||||
* an error found when Flow v0.63 was deployed. To see the error
|
||||
* delete this comment and run Flow. */
|
||||
this.scrollToIndex({
|
||||
animated: false,
|
||||
index: this.props.initialScrollIndex,
|
||||
@@ -602,25 +491,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._isNestedWithSameOrientation()) {
|
||||
this.context.virtualizedList.unregisterAsNestedChild({
|
||||
key: this.props.listKey || this._getCellKey(),
|
||||
state: {
|
||||
first: this.state.first,
|
||||
last: this.state.last,
|
||||
frames: this._frames,
|
||||
},
|
||||
});
|
||||
}
|
||||
this._updateViewableItems(null);
|
||||
this._updateCellsToRenderBatcher.dispose({abort: true});
|
||||
this._updateCellsToRenderBatcher.dispose();
|
||||
this._viewabilityTuples.forEach(tuple => {
|
||||
tuple.viewabilityHelper.dispose();
|
||||
});
|
||||
this._fillRateHelper.deactivateAndFlush();
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
clearTimeout(this._initialScrollIndexTimeout);
|
||||
}
|
||||
|
||||
@@ -673,7 +549,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
for (let ii = first; ii <= last; ii++) {
|
||||
const item = getItem(data, ii);
|
||||
const key = keyExtractor(item, ii);
|
||||
this._indicesToKeys.set(ii, key);
|
||||
if (stickyIndicesFromProps.has(ii + stickyOffset)) {
|
||||
stickyHeaderIndices.push(cells.length);
|
||||
}
|
||||
@@ -710,7 +585,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
_isVirtualizationDisabled(): boolean {
|
||||
return this.props.disableVirtualization;
|
||||
return (
|
||||
this.props.disableVirtualization || this._isNestedWithSameOrientation()
|
||||
);
|
||||
}
|
||||
|
||||
_isNestedWithSameOrientation(): boolean {
|
||||
@@ -729,6 +606,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
'Consider using `numColumns` with `FlatList` instead.',
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
ListEmptyComponent,
|
||||
ListFooterComponent,
|
||||
@@ -755,13 +633,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
<ListHeaderComponent />
|
||||
);
|
||||
cells.push(
|
||||
<VirtualizedCellWrapper
|
||||
cellKey={this._getCellKey() + '-header'}
|
||||
key="$header">
|
||||
<View onLayout={this._onLayoutHeader} style={inversionStyle}>
|
||||
{element}
|
||||
</View>
|
||||
</VirtualizedCellWrapper>,
|
||||
<View
|
||||
key="$header"
|
||||
onLayout={this._onLayoutHeader}
|
||||
style={inversionStyle}>
|
||||
{element}
|
||||
</View>,
|
||||
);
|
||||
}
|
||||
const itemCount = this.props.getItemCount(data);
|
||||
@@ -880,13 +757,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
<ListFooterComponent />
|
||||
);
|
||||
cells.push(
|
||||
<VirtualizedCellWrapper
|
||||
cellKey={this._getCellKey() + '-footer'}
|
||||
key="$footer">
|
||||
<View onLayout={this._onLayoutFooter} style={inversionStyle}>
|
||||
{element}
|
||||
</View>
|
||||
</VirtualizedCellWrapper>,
|
||||
<View
|
||||
key="$footer"
|
||||
onLayout={this._onLayoutFooter}
|
||||
style={inversionStyle}>
|
||||
{element}
|
||||
</View>,
|
||||
);
|
||||
}
|
||||
const scrollProps = {
|
||||
@@ -903,10 +779,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
if (inversionStyle) {
|
||||
scrollProps.style = [inversionStyle, this.props.style];
|
||||
}
|
||||
|
||||
this._hasMore =
|
||||
this.state.last < this.props.getItemCount(this.props.data) - 1;
|
||||
|
||||
const ret = React.cloneElement(
|
||||
(this.props.renderScrollComponent || this._defaultRenderScrollComponent)(
|
||||
scrollProps,
|
||||
@@ -933,25 +805,15 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
_averageCellLength = 0;
|
||||
// Maps a cell key to the set of keys for all outermost child lists within that cell
|
||||
_cellKeysToChildListKeys: Map<string, Set<string>> = new Map();
|
||||
_cellRefs = {};
|
||||
_fillRateHelper: FillRateHelper;
|
||||
_frames = {};
|
||||
_footerLength = 0;
|
||||
_hasDataChangedSinceEndReached = true;
|
||||
_hasMore = false;
|
||||
_hasWarned = {};
|
||||
_highestMeasuredFrameIndex = 0;
|
||||
_headerLength = 0;
|
||||
_indicesToKeys: Map<number, string> = new Map();
|
||||
_initialScrollIndexTimeout = 0;
|
||||
_nestedChildLists: Map<
|
||||
string,
|
||||
{ref: ?VirtualizedList, state: ?ChildListState},
|
||||
> = new Map();
|
||||
_offsetFromParentVirtualizedList: number = 0;
|
||||
_prevParentOffset: number = 0;
|
||||
_fillRateHelper: FillRateHelper;
|
||||
_frames = {};
|
||||
_footerLength = 0;
|
||||
_scrollMetrics = {
|
||||
contentLength: 0,
|
||||
dOffset: 0,
|
||||
@@ -991,6 +853,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
'`',
|
||||
);
|
||||
return (
|
||||
/* $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. */
|
||||
<ScrollView
|
||||
{...props}
|
||||
refreshControl={
|
||||
@@ -1006,6 +871,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
/* $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 <ScrollView {...props} />;
|
||||
}
|
||||
};
|
||||
@@ -1048,41 +916,10 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
_measureLayoutRelativeToContainingList(): void {
|
||||
UIManager.measureLayout(
|
||||
ReactNative.findNodeHandle(this),
|
||||
ReactNative.findNodeHandle(
|
||||
this.context.virtualizedList.getOutermostParentListRef(),
|
||||
),
|
||||
error => {
|
||||
console.warn(
|
||||
"VirtualizedList: Encountered an error while measuring a list's" +
|
||||
' offset from its containing VirtualizedList.',
|
||||
);
|
||||
},
|
||||
(x, y, width, height) => {
|
||||
this._offsetFromParentVirtualizedList = this._selectOffset({x, y});
|
||||
this._scrollMetrics.contentLength = this._selectLength({width, height});
|
||||
|
||||
const scrollMetrics = this._convertParentScrollMetrics(
|
||||
this.context.virtualizedList.getScrollMetrics(),
|
||||
);
|
||||
this._scrollMetrics.visibleLength = scrollMetrics.visibleLength;
|
||||
this._scrollMetrics.offset = scrollMetrics.offset;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_onLayout = (e: Object) => {
|
||||
if (this._isNestedWithSameOrientation()) {
|
||||
// Need to adjust our scroll metrics to be relative to our containing
|
||||
// VirtualizedList before we can make claims about list item viewability
|
||||
this._measureLayoutRelativeToContainingList();
|
||||
} else {
|
||||
this._scrollMetrics.visibleLength = this._selectLength(
|
||||
e.nativeEvent.layout,
|
||||
);
|
||||
}
|
||||
this._scrollMetrics.visibleLength = this._selectLength(
|
||||
e.nativeEvent.layout,
|
||||
);
|
||||
this.props.onLayout && this.props.onLayout(e);
|
||||
this._scheduleCellsToRenderUpdate();
|
||||
this._maybeCallOnEndReached();
|
||||
@@ -1182,9 +1019,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
if (
|
||||
onEndReached &&
|
||||
this.state.last === getItemCount(data) - 1 &&
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
distanceFromEnd < onEndReachedThreshold * visibleLength &&
|
||||
(this._hasDataChangedSinceEndReached ||
|
||||
this._scrollMetrics.contentLength !== this._sentEndForContentLength)
|
||||
@@ -1205,63 +1039,17 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
this._maybeCallOnEndReached();
|
||||
};
|
||||
|
||||
/* Translates metrics from a scroll event in a parent VirtualizedList into
|
||||
* coordinates relative to the child list.
|
||||
*/
|
||||
_convertParentScrollMetrics = (metrics: {
|
||||
visibleLength: number,
|
||||
offset: number,
|
||||
}) => {
|
||||
// Offset of the top of the nested list relative to the top of its parent's viewport
|
||||
const offset = metrics.offset - this._offsetFromParentVirtualizedList;
|
||||
// Child's visible length is the same as its parent's
|
||||
const visibleLength = metrics.visibleLength;
|
||||
const dOffset = offset - this._scrollMetrics.offset;
|
||||
const contentLength = this._scrollMetrics.contentLength;
|
||||
|
||||
return {
|
||||
visibleLength,
|
||||
contentLength,
|
||||
offset,
|
||||
dOffset,
|
||||
};
|
||||
};
|
||||
|
||||
_onScroll = (e: Object) => {
|
||||
this._nestedChildLists.forEach(childList => {
|
||||
childList.ref && childList.ref._onScroll(e);
|
||||
});
|
||||
if (this.props.onScroll) {
|
||||
this.props.onScroll(e);
|
||||
}
|
||||
const timestamp = e.timeStamp;
|
||||
let visibleLength = this._selectLength(e.nativeEvent.layoutMeasurement);
|
||||
let contentLength = this._selectLength(e.nativeEvent.contentSize);
|
||||
let offset = this._selectOffset(e.nativeEvent.contentOffset);
|
||||
let dOffset = offset - this._scrollMetrics.offset;
|
||||
|
||||
if (this._isNestedWithSameOrientation()) {
|
||||
if (this._scrollMetrics.contentLength === 0) {
|
||||
// Ignore scroll events until onLayout has been called and we
|
||||
// know our offset from our offset from our parent
|
||||
return;
|
||||
}
|
||||
({
|
||||
visibleLength,
|
||||
contentLength,
|
||||
offset,
|
||||
dOffset,
|
||||
} = this._convertParentScrollMetrics({
|
||||
visibleLength,
|
||||
offset,
|
||||
}));
|
||||
}
|
||||
|
||||
const visibleLength = this._selectLength(e.nativeEvent.layoutMeasurement);
|
||||
const contentLength = this._selectLength(e.nativeEvent.contentSize);
|
||||
const offset = this._selectOffset(e.nativeEvent.contentOffset);
|
||||
const dt = this._scrollMetrics.timestamp
|
||||
? Math.max(1, timestamp - this._scrollMetrics.timestamp)
|
||||
: 1;
|
||||
const velocity = dOffset / dt;
|
||||
|
||||
if (
|
||||
dt > 500 &&
|
||||
this._scrollMetrics.dt > 500 &&
|
||||
@@ -1276,6 +1064,8 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
);
|
||||
this._hasWarned.perf = true;
|
||||
}
|
||||
const dOffset = offset - this._scrollMetrics.offset;
|
||||
const velocity = dOffset / dt;
|
||||
this._scrollMetrics = {
|
||||
contentLength,
|
||||
dt,
|
||||
@@ -1307,9 +1097,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
const distBottom =
|
||||
this._getFrameMetricsApprox(last).offset - (offset + visibleLength);
|
||||
const scrollingThreshold =
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete
|
||||
* this comment and run Flow. */
|
||||
this.props.onEndReachedThreshold * visibleLength / 2;
|
||||
hiPri =
|
||||
Math.min(distTop, distBottom) < 0 ||
|
||||
@@ -1386,9 +1173,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
const {contentLength, offset, visibleLength} = this._scrollMetrics;
|
||||
const distanceFromEnd = contentLength - visibleLength - offset;
|
||||
const renderAhead =
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses
|
||||
* an error found when Flow v0.63 was deployed. To see the error
|
||||
* delete this comment and run Flow. */
|
||||
distanceFromEnd < onEndReachedThreshold * visibleLength
|
||||
? this.props.maxToRenderPerBatch
|
||||
: 0;
|
||||
@@ -1397,36 +1181,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
last: Math.min(state.last + renderAhead, getItemCount(data) - 1),
|
||||
};
|
||||
}
|
||||
if (newState && this._nestedChildLists.size > 0) {
|
||||
const newFirst = newState.first;
|
||||
const newLast = newState.last;
|
||||
// If some cell in the new state has a child list in it, we should only render
|
||||
// up through that item, so that we give that list a chance to render.
|
||||
// Otherwise there's churn from multiple child lists mounting and un-mounting
|
||||
// their items.
|
||||
for (let ii = newFirst; ii <= newLast; ii++) {
|
||||
const cellKeyForIndex = this._indicesToKeys.get(ii);
|
||||
const childListKeys =
|
||||
cellKeyForIndex &&
|
||||
this._cellKeysToChildListKeys.get(cellKeyForIndex);
|
||||
if (!childListKeys) {
|
||||
continue;
|
||||
}
|
||||
let someChildHasMore = false;
|
||||
// For each cell, need to check whether any child list in it has more elements to render
|
||||
for (let childKey of childListKeys) {
|
||||
const childList = this._nestedChildLists.get(childKey);
|
||||
if (childList && childList.ref && childList.ref.hasMore()) {
|
||||
someChildHasMore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (someChildHasMore) {
|
||||
newState.last = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
@@ -1496,9 +1250,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
}
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
return frame;
|
||||
};
|
||||
|
||||
@@ -1547,20 +1298,6 @@ class CellRenderer extends React.Component<
|
||||
},
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
virtualizedCell: PropTypes.shape({
|
||||
cellKey: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
virtualizedCell: {
|
||||
cellKey: this.props.cellKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: consider factoring separator stuff out of VirtualizedList into FlatList since it's not
|
||||
// reused by SectionList and we can keep VirtualizedList simpler.
|
||||
_separators = {
|
||||
@@ -1647,29 +1384,6 @@ class CellRenderer extends React.Component<
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualizedCellWrapper extends React.Component<{
|
||||
cellKey: string,
|
||||
children: React.Node,
|
||||
}> {
|
||||
static childContextTypes = {
|
||||
virtualizedCell: PropTypes.shape({
|
||||
cellKey: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
virtualizedCell: {
|
||||
cellKey: this.props.cellKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
verticallyInverted: {
|
||||
transform: [{scaleY: -1}],
|
||||
|
||||
@@ -233,9 +233,6 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
|
||||
return {
|
||||
...viewable,
|
||||
index: info.index,
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
key: keyExtractor(viewable.item, info.index),
|
||||
section: info.section,
|
||||
};
|
||||
|
||||
@@ -89,9 +89,6 @@ module.exports = {
|
||||
|
||||
testBadSectionsShape(): React.Element<*> {
|
||||
const sections = [
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
{
|
||||
key: 'a',
|
||||
items: [
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user