mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c662a5d3a | ||
|
|
d18a233fad | ||
|
|
0dace35d89 | ||
|
|
096f1d9c4c | ||
|
|
7047d6e191 | ||
|
|
a79154daee | ||
|
|
2c6abbd638 | ||
|
|
d16e0a2579 | ||
|
|
2c803b04e9 | ||
|
|
0686b0147c | ||
|
|
83fbef5dd1 | ||
|
|
b774062bae | ||
|
|
d2c00d58c5 | ||
|
|
0a5967dbfe | ||
|
|
1c7f39e461 | ||
|
|
e0a0bb1857 | ||
|
|
fcd33c8825 | ||
|
|
436682fbad | ||
|
|
2740e08ef7 | ||
|
|
d91d28a2a6 | ||
|
|
13b14d6771 | ||
|
|
8fef46a5ff | ||
|
|
d8124a3fb7 | ||
|
|
3a20dcf965 | ||
|
|
4a787bfff8 | ||
|
|
3f6943c9ab | ||
|
|
b1d3e442c8 | ||
|
|
308b88f417 | ||
|
|
1dba7e0a5a | ||
|
|
d5ba36b974 | ||
|
|
4e64c57d8d | ||
|
|
331809fd0c | ||
|
|
79fcbbfe09 | ||
|
|
d7c0acdcbd | ||
|
|
2d7c5c4746 | ||
|
|
6a97054696 | ||
|
|
6bc78b1d66 | ||
|
|
b360168e8b | ||
|
|
85e1ad6b61 | ||
|
|
12698ed4af | ||
|
|
a602d19093 | ||
|
|
7ce522c0a2 | ||
|
|
851fae5a03 | ||
|
|
6ee1f8cd50 | ||
|
|
9b178b8f1a | ||
|
|
db252ebd8f | ||
|
|
d42ffaa149 | ||
|
|
3f0e3b7d5e | ||
|
|
4a73a31725 | ||
|
|
6992c3284a | ||
|
|
0dff168820 | ||
|
|
2e44754c9d | ||
|
|
e4e3cb29e5 | ||
|
|
2c13456f99 | ||
|
|
12bfcac731 | ||
|
|
c760ca90a5 | ||
|
|
c32d9bc423 | ||
|
|
d82dbdff2d | ||
|
|
e3be311c3f | ||
|
|
a014a16be9 | ||
|
|
52b64907a8 | ||
|
|
f3c1f4220f | ||
|
|
72b9fccae2 | ||
|
|
379e15fe81 | ||
|
|
08b48cafb3 | ||
|
|
f26b493b46 | ||
|
|
2baf1de3db | ||
|
|
0a21fb6160 | ||
|
|
0a615ea9ea | ||
|
|
c7b1e278ef | ||
|
|
475a954951 | ||
|
|
ab997ebdde | ||
|
|
48a993b5c5 | ||
|
|
0bfcc9a6c2 | ||
|
|
fe3bd860ac | ||
|
|
db3a724bb2 | ||
|
|
fae38f59d4 | ||
|
|
c6203f6516 | ||
|
|
d81cb6e162 | ||
|
|
443d44d8f4 | ||
|
|
da9bd7b55a | ||
|
|
adbdfe65e1 | ||
|
|
efe446e962 | ||
|
|
bd8671f4da | ||
|
|
041770be62 | ||
|
|
de8cfae5e4 | ||
|
|
14475b7879 | ||
|
|
0024a1c0a0 | ||
|
|
a597809cfa | ||
|
|
750c633940 | ||
|
|
f895ce7fbc | ||
|
|
9c26742743 | ||
|
|
399ace3733 | ||
|
|
80baec3bb6 | ||
|
|
ab8d5cec83 | ||
|
|
fb9bacdf2d |
@@ -37,7 +37,7 @@
|
||||
"rules": {
|
||||
"no-cond-assign": 1, // disallow assignment in conditional expressions
|
||||
"no-console": 0, // disallow use of console (off by default in the node environment)
|
||||
"no-constant-condition": 1, // disallow use of constant expressions in conditions
|
||||
"no-constant-condition": 0, // disallow use of constant expressions in conditions
|
||||
"no-comma-dangle": 0, // disallow trailing commas in object literals
|
||||
"no-control-regex": 1, // disallow control characters in regular expressions
|
||||
"no-debugger": 1, // disallow use of debugger
|
||||
|
||||
+46
-16
@@ -1,32 +1,62 @@
|
||||
language: objective-c
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
before_install:
|
||||
- brew update
|
||||
- brew reinstall xctool
|
||||
install:
|
||||
- brew install watchman
|
||||
- brew reinstall flow watchman xctool
|
||||
- npm install
|
||||
before_script:
|
||||
- npm test
|
||||
- (npm start > packager.log 2>&1 &) && echo $! > packager.pid
|
||||
|
||||
after_script:
|
||||
- pkill -9 -F packager.pid
|
||||
- cat packager.log
|
||||
- rm packager.log
|
||||
- rm packager.pid
|
||||
|
||||
# Automatically publish the website
|
||||
after_success:
|
||||
script:
|
||||
- |
|
||||
if [ "$TRAVIS_PULL_REQUEST" = false ] && [ "$TRAVIS_BRANCH" = master ]; then
|
||||
echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" >~/.netrc
|
||||
if [ "$TEST_TYPE" = objc ]
|
||||
then
|
||||
|
||||
(npm start > packager.log 2>&1 &)
|
||||
echo $! > packager.pid
|
||||
|
||||
xctool \
|
||||
-project Examples/UIExplorer/UIExplorer.xcodeproj \
|
||||
-scheme UIExplorer -sdk iphonesimulator8.1
|
||||
test
|
||||
|
||||
pkill -9 -F packager.pid
|
||||
cat packager.log
|
||||
rm packager.log packager.pid
|
||||
|
||||
elif [ "$TEST_TYPE" = js ]
|
||||
then
|
||||
|
||||
flow check && npm test
|
||||
|
||||
elif [ "$TEST_TYPE" = build_website ]
|
||||
then
|
||||
|
||||
cd website
|
||||
npm install
|
||||
./setup.sh
|
||||
./publish.sh
|
||||
if [ "$TRAVIS_PULL_REQUEST" = false ] && [ "$TRAVIS_BRANCH" = master ]; then
|
||||
# Automatically publish the website
|
||||
echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" >~/.netrc
|
||||
./publish.sh
|
||||
else
|
||||
# Make sure the website builds without error
|
||||
node server/generate.js
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Unknown test type: $TEST_TYPE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- TEST_TYPE=objc
|
||||
- TEST_TYPE=js
|
||||
- TEST_TYPE=build_website
|
||||
global:
|
||||
- secure: "HlmG8M2DmBUSBh6KH1yVIe/8gR4iibg4WfcHq1x/xYQxGbvleq7NOo04V6eFHnl9cvZCu+PKH0841WLnGR7c4BBf47GVu/o16nXzggPumHKy++lDzxFPlJ1faMDfjg/5vjbAxRUe7D3y98hQSeGHH4tedc8LvTaFLVu7iiGqvjU="
|
||||
|
||||
|
||||
@@ -7,23 +7,16 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13ACB6741AC2117000FF4204 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13ACB6711AC2113600FF4204 /* libRCTAnimation.a */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
1461632D1AC3E23900C2F5AD /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1461632C1AC3E22900C2F5AD /* libReact.a */; };
|
||||
58C1E40E1ACF54E9006D1A47 /* libRCTAnimationExperimental.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C1E40B1ACF54B4006D1A47 /* libRCTAnimationExperimental.a */; };
|
||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
13ACB6701AC2113600FF4204 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTAnimation;
|
||||
};
|
||||
1461632B1AC3E22900C2F5AD /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 146163271AC3E22900C2F5AD /* React.xcodeproj */;
|
||||
@@ -31,6 +24,13 @@
|
||||
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
|
||||
remoteInfo = React;
|
||||
};
|
||||
58C1E40A1ACF54B4006D1A47 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 13ACB66C1AC2113500FF4204 /* RCTAnimationExperimental.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTAnimationExperimental;
|
||||
};
|
||||
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
|
||||
@@ -41,7 +41,7 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = ../../Libraries/Animation/RCTAnimation.xcodeproj; sourceTree = "<group>"; };
|
||||
13ACB66C1AC2113500FF4204 /* RCTAnimationExperimental.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimationExperimental.xcodeproj; path = ../../Libraries/Animation/RCTAnimationExperimental.xcodeproj; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* 2048.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = 2048.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = 2048/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = 2048/AppDelegate.m; sourceTree = "<group>"; };
|
||||
@@ -59,7 +59,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1461632D1AC3E23900C2F5AD /* libReact.a in Frameworks */,
|
||||
13ACB6741AC2117000FF4204 /* libRCTAnimation.a in Frameworks */,
|
||||
58C1E40E1ACF54E9006D1A47 /* libRCTAnimationExperimental.a in Frameworks */,
|
||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -67,14 +67,6 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
13ACB66D1AC2113500FF4204 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13ACB6711AC2113600FF4204 /* libRCTAnimation.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
13B07FAE1A68108700A75B9A /* 2048 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -96,12 +88,20 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
58C1E4071ACF54B4006D1A47 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
58C1E40B1ACF54B4006D1A47 /* libRCTAnimationExperimental.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
146163271AC3E22900C2F5AD /* React.xcodeproj */,
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
|
||||
13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */,
|
||||
13ACB66C1AC2113500FF4204 /* RCTAnimationExperimental.xcodeproj */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -121,7 +121,9 @@
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
@@ -173,8 +175,8 @@
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
{
|
||||
ProductGroup = 13ACB66D1AC2113500FF4204 /* Products */;
|
||||
ProjectRef = 13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */;
|
||||
ProductGroup = 58C1E4071ACF54B4006D1A47 /* Products */;
|
||||
ProjectRef = 13ACB66C1AC2113500FF4204 /* RCTAnimationExperimental.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
|
||||
@@ -193,13 +195,6 @@
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
13ACB6711AC2113600FF4204 /* libRCTAnimation.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTAnimation.a;
|
||||
remoteRef = 13ACB6701AC2113600FF4204 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
1461632C1AC3E22900C2F5AD /* libReact.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
@@ -207,6 +202,13 @@
|
||||
remoteRef = 1461632B1AC3E22900C2F5AD /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
58C1E40B1ACF54B4006D1A47 /* libRCTAnimationExperimental.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTAnimationExperimental.a;
|
||||
remoteRef = 58C1E40A1ACF54B4006D1A47 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
@@ -264,6 +266,10 @@
|
||||
);
|
||||
INFOPLIST_FILE = "$(SRCROOT)/2048/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"/Users/sahrens/src/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/Animation/build/Debug-iphoneos",
|
||||
);
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = 2048;
|
||||
};
|
||||
@@ -280,6 +286,10 @@
|
||||
);
|
||||
INFOPLIST_FILE = "$(SRCROOT)/2048/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"/Users/sahrens/src/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/Animation/build/Debug-iphoneos",
|
||||
);
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = 2048;
|
||||
};
|
||||
|
||||
+19
-11
@@ -18,13 +18,13 @@
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
Animation,
|
||||
AppRegistry,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var AnimationExperimental = require('AnimationExperimental');
|
||||
var GameBoard = require('GameBoard');
|
||||
var TouchableBounce = require('TouchableBounce');
|
||||
|
||||
@@ -73,21 +73,29 @@ class Tile extends React.Component {
|
||||
if (tile.isNew()) {
|
||||
offset.opacity = 0;
|
||||
} else {
|
||||
var point = [
|
||||
animationPosition(tile.toColumn()),
|
||||
animationPosition(tile.toRow()),
|
||||
];
|
||||
Animation.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {position: point});
|
||||
var point = {
|
||||
x: animationPosition(tile.toColumn()),
|
||||
y: animationPosition(tile.toRow()),
|
||||
};
|
||||
AnimationExperimental.startAnimation({
|
||||
node: this.refs['this'],
|
||||
duration: 100,
|
||||
easing: 'easeInOutQuad',
|
||||
property: 'position',
|
||||
toValue: point,
|
||||
});
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
Animation.startAnimation(this.refs['this'], 300, 0, 'easeInOutQuad', {scaleXY: [1, 1]});
|
||||
Animation.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {opacity: 1});
|
||||
}, 0);
|
||||
AnimationExperimental.startAnimation({
|
||||
node: this.refs['this'],
|
||||
duration: 100,
|
||||
easing: 'easeInOutQuad',
|
||||
property: 'opacity',
|
||||
toValue: 1,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -159,7 +159,9 @@
|
||||
58C571FC1AA6124500CDF9C8 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */; };
|
||||
00481BEA1AC0C89D00671115 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE91AC0C89D00671115 /* libicucore.dylib */; };
|
||||
008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */ = {isa = PBXBuildFile; fileRef = 008F07F21AC5B25A0029DE68 /* main.jsbundle */; };
|
||||
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
|
||||
00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */; };
|
||||
@@ -16,6 +15,7 @@
|
||||
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
|
||||
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
|
||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
|
||||
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
@@ -81,6 +81,13 @@
|
||||
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
|
||||
remoteInfo = React;
|
||||
};
|
||||
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTLinking;
|
||||
};
|
||||
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
|
||||
@@ -92,7 +99,6 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = ../../Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj; sourceTree = "<group>"; };
|
||||
00481BE91AC0C89D00671115 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = iOS/main.jsbundle; sourceTree = "<group>"; };
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
|
||||
@@ -108,6 +114,7 @@
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iOS/main.m; sourceTree = "<group>"; };
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = "<group>"; };
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -116,13 +123,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
00481BEA1AC0C89D00671115 /* libicucore.dylib in Frameworks */,
|
||||
146834051AC3E58100842450 /* libReact.a in Frameworks */,
|
||||
00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */,
|
||||
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
|
||||
00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */,
|
||||
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
|
||||
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
|
||||
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
|
||||
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
|
||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
|
||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
|
||||
@@ -210,9 +217,18 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
78C398B11ACF4ADC00677621 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */,
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
|
||||
@@ -222,7 +238,6 @@
|
||||
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
|
||||
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
|
||||
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */,
|
||||
00481BE91AC0C89D00671115 /* libicucore.dylib */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -242,7 +257,9 @@
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
@@ -309,6 +326,10 @@
|
||||
ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */;
|
||||
ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 78C398B11ACF4ADC00677621 /* Products */;
|
||||
ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
|
||||
ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
|
||||
@@ -394,6 +415,13 @@
|
||||
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTLinking.a;
|
||||
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
||||
@@ -36,5 +36,7 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -24,7 +24,7 @@ var SampleApp = React.createClass({
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Press Cmd+R to reload,{'\n'}
|
||||
Cmd+Shift+Z for dev menu
|
||||
Cmd+Control+Z for dev menu
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -121,7 +121,9 @@
|
||||
58C572071AA6126D00CDF9C8 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -29,16 +29,17 @@ var COLORS = ['red', 'orange', 'yellow', 'green', 'blue'];
|
||||
|
||||
var BasicStorageExample = React.createClass({
|
||||
componentDidMount() {
|
||||
AsyncStorage.getItem(STORAGE_KEY, (error, value) => {
|
||||
if (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
} else if (value !== null) {
|
||||
this.setState({selectedValue: value});
|
||||
this._appendMessage('Recovered selection from disk: ' + value);
|
||||
} else {
|
||||
this._appendMessage('Initialized with no selection on disk.');
|
||||
}
|
||||
});
|
||||
AsyncStorage.getItem(STORAGE_KEY)
|
||||
.then((value) => {
|
||||
if (value !== null){
|
||||
this.setState({selectedValue: value});
|
||||
this._appendMessage('Recovered selection from disk: ' + value);
|
||||
} else {
|
||||
this._appendMessage('Initialized with no selection on disk.');
|
||||
}
|
||||
})
|
||||
.catch((error) => this._appendMessage('AsyncStorage error: ' + error.message))
|
||||
.done();
|
||||
},
|
||||
getInitialState() {
|
||||
return {
|
||||
@@ -81,23 +82,17 @@ var BasicStorageExample = React.createClass({
|
||||
|
||||
_onValueChange(selectedValue) {
|
||||
this.setState({selectedValue});
|
||||
AsyncStorage.setItem(STORAGE_KEY, selectedValue, (error) => {
|
||||
if (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
} else {
|
||||
this._appendMessage('Saved selection to disk: ' + selectedValue);
|
||||
}
|
||||
});
|
||||
AsyncStorage.setItem(STORAGE_KEY, selectedValue)
|
||||
.then(() => this._appendMessage('Saved selection to disk: ' + selectedValue))
|
||||
.catch((error) => this._appendMessage('AsyncStorage error: ' + error.message))
|
||||
.done();
|
||||
},
|
||||
|
||||
_removeStorage() {
|
||||
AsyncStorage.removeItem(STORAGE_KEY, (error) => {
|
||||
if (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
} else {
|
||||
this._appendMessage('Selection removed from disk.');
|
||||
}
|
||||
});
|
||||
AsyncStorage.removeItem(STORAGE_KEY)
|
||||
.then(() => this._appendMessage('Selection removed from disk.'))
|
||||
.catch((error) => { this._appendMessage('AsyncStorage error: ' + error.message) })
|
||||
.done();
|
||||
},
|
||||
|
||||
_appendMessage(message) {
|
||||
|
||||
@@ -115,7 +115,7 @@ var BreadcrumbNavSample = React.createClass({
|
||||
renderScene={this._renderScene}
|
||||
navigationBar={
|
||||
<Navigator.BreadcrumbNavigationBar
|
||||
navigationBarRouteMapper={this._navBarRouteMapper}
|
||||
routeMapper={this._navBarRouteMapper}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -52,32 +52,47 @@ var ROUTE_STACK = [
|
||||
var INIT_ROUTE_INDEX = 1;
|
||||
|
||||
class JumpingNavBar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
tabIndex: props.initTabIndex,
|
||||
};
|
||||
}
|
||||
handleWillFocus(route) {
|
||||
var tabIndex = ROUTE_STACK.indexOf(route);
|
||||
this.setState({ tabIndex, });
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.tabs}>
|
||||
<TabBarIOS
|
||||
selectedTab={'tab_' + this.props.tabIndex}>
|
||||
<TabBarIOS>
|
||||
<TabBarIOS.Item
|
||||
name="tab_0"
|
||||
icon={require('image!tabnav_notification')}
|
||||
selected={this.props.tabIndex === 0}
|
||||
onPress={() => { this.props.onTabIndex(0); }}
|
||||
children={<View />}
|
||||
/>
|
||||
selected={this.state.tabIndex === 0}
|
||||
onPress={() => {
|
||||
this.props.onTabIndex(0);
|
||||
this.setState({ tabIndex: 0, });
|
||||
}}>
|
||||
<View />
|
||||
</TabBarIOS.Item>
|
||||
<TabBarIOS.Item
|
||||
name="tab_1"
|
||||
icon={require('image!tabnav_list')}
|
||||
selected={this.props.tabIndex === 1}
|
||||
onPress={() => { this.props.onTabIndex(1); }}
|
||||
children={<View />}
|
||||
/>
|
||||
selected={this.state.tabIndex === 1}
|
||||
onPress={() => {
|
||||
this.props.onTabIndex(1);
|
||||
this.setState({ tabIndex: 1, });
|
||||
}}>
|
||||
<View />
|
||||
</TabBarIOS.Item>
|
||||
<TabBarIOS.Item
|
||||
name="tab_2"
|
||||
icon={require('image!tabnav_settings')}
|
||||
selected={this.props.tabIndex === 2}
|
||||
onPress={() => { this.props.onTabIndex(2); }}
|
||||
children={<View />}
|
||||
/>
|
||||
selected={this.state.tabIndex === 2}
|
||||
onPress={() => {
|
||||
this.props.onTabIndex(2);
|
||||
this.setState({ tabIndex: 2, });
|
||||
}}>
|
||||
<View />
|
||||
</TabBarIOS.Item>
|
||||
</TabBarIOS>
|
||||
</View>
|
||||
);
|
||||
@@ -85,12 +100,6 @@ class JumpingNavBar extends React.Component {
|
||||
}
|
||||
|
||||
var JumpingNavSample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
tabIndex: INIT_ROUTE_INDEX,
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<Navigator
|
||||
@@ -102,23 +111,19 @@ var JumpingNavSample = React.createClass({
|
||||
initialRoute={ROUTE_STACK[INIT_ROUTE_INDEX]}
|
||||
initialRouteStack={ROUTE_STACK}
|
||||
renderScene={this.renderScene}
|
||||
configureScene={() => ({
|
||||
...Navigator.SceneConfigs.HorizontalSwipeJump,
|
||||
})}
|
||||
navigationBar={
|
||||
<JumpingNavBar
|
||||
ref={(navBar) => { this.navBar = navBar; }}
|
||||
initTabIndex={INIT_ROUTE_INDEX}
|
||||
routeStack={ROUTE_STACK}
|
||||
tabIndex={this.state.tabIndex}
|
||||
onTabIndex={(index) => {
|
||||
this.setState({ tabIndex: index }, () => {
|
||||
this._navigator.jumpTo(ROUTE_STACK[index]);
|
||||
});
|
||||
this._navigator.jumpTo(ROUTE_STACK[index]);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onWillFocus={(route) => {
|
||||
this.setState({
|
||||
tabIndex: ROUTE_STACK.indexOf(route),
|
||||
});
|
||||
}}
|
||||
shouldJumpOnBackstackPop={true}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -121,8 +121,8 @@ var NavigationBarSample = React.createClass({
|
||||
)}
|
||||
navigationBar={
|
||||
<Navigator.NavigationBar
|
||||
navigationBarRouteMapper={NavigationBarRouteMapper}
|
||||
navigationBarStyles={styles.navBar}
|
||||
routeMapper={NavigationBarRouteMapper}
|
||||
style={styles.navBar}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
var React = require('react-native');
|
||||
var ViewExample = require('./ViewExample');
|
||||
var createExamplePage = require('./createExamplePage');
|
||||
var {
|
||||
PixelRatio,
|
||||
ScrollView,
|
||||
@@ -77,7 +78,7 @@ var NavigatorIOSExample = React.createClass({
|
||||
{this._renderRow('Push View Example', () => {
|
||||
this.props.navigator.push({
|
||||
title: 'Very Long Custom View Example Title',
|
||||
component: ViewExample,
|
||||
component: createExamplePage(null, ViewExample),
|
||||
});
|
||||
})}
|
||||
{this._renderRow('Custom Right Button', () => {
|
||||
|
||||
@@ -74,7 +74,7 @@ class NotificationExample extends React.Component {
|
||||
_onNotification(notification) {
|
||||
AlertIOS.alert(
|
||||
'Notification Received',
|
||||
`Alert message: ${notification.getMessage()}`,
|
||||
'Alert message: ' + notification.getMessage(),
|
||||
[{
|
||||
text: 'Dismiss',
|
||||
onPress: null,
|
||||
|
||||
@@ -38,7 +38,7 @@ var NavigatorIOSExample = React.createClass({
|
||||
_previousLeft: 0,
|
||||
_previousTop: 0,
|
||||
_circleStyles: {},
|
||||
circle: (null : ?React.Element),
|
||||
circle: (null : ?{ setNativeProps(props: Object): void }),
|
||||
|
||||
componentWillMount: function() {
|
||||
this._panResponder = PanResponder.create({
|
||||
|
||||
@@ -22,9 +22,8 @@ var {
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
var TabBarItemIOS = TabBarIOS.Item;
|
||||
var TabBarExample = React.createClass({
|
||||
|
||||
var TabBarExample = React.createClass({
|
||||
statics: {
|
||||
title: '<TabBarIOS>',
|
||||
description: 'Tab-based navigation.'
|
||||
@@ -42,19 +41,16 @@ var TabBarExample = React.createClass({
|
||||
return (
|
||||
<View style={[styles.tabContent, {backgroundColor: color}]}>
|
||||
<Text style={styles.tabText}>{pageText}</Text>
|
||||
<Text style={styles.tabText}>{this.state.presses} re-renders of this tab</Text>
|
||||
<Text style={styles.tabText}>{this.state.presses} re-renders of the More tab</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<TabBarIOS
|
||||
selectedTab={this.state.selectedTab}>
|
||||
<TabBarItemIOS
|
||||
name="blueTab"
|
||||
icon={_ix_DEPRECATED('favorites')}
|
||||
accessibilityLabel="Blue Tab"
|
||||
<TabBarIOS>
|
||||
<TabBarIOS.Item
|
||||
title="Blue Tab"
|
||||
selected={this.state.selectedTab === 'blueTab'}
|
||||
onPress={() => {
|
||||
this.setState({
|
||||
@@ -62,12 +58,10 @@ var TabBarExample = React.createClass({
|
||||
});
|
||||
}}>
|
||||
{this._renderContent('#414A8C', 'Blue Tab')}
|
||||
</TabBarItemIOS>
|
||||
<TabBarItemIOS
|
||||
accessibilityLabel="Red Tab"
|
||||
name="redTab"
|
||||
icon={_ix_DEPRECATED('history')}
|
||||
badgeValue={this.state.notifCount ? String(this.state.notifCount) : null}
|
||||
</TabBarIOS.Item>
|
||||
<TabBarIOS.Item
|
||||
systemIcon="history"
|
||||
badge={this.state.notifCount > 0 ? this.state.notifCount : undefined}
|
||||
selected={this.state.selectedTab === 'redTab'}
|
||||
onPress={() => {
|
||||
this.setState({
|
||||
@@ -76,11 +70,9 @@ var TabBarExample = React.createClass({
|
||||
});
|
||||
}}>
|
||||
{this._renderContent('#783E33', 'Red Tab')}
|
||||
</TabBarItemIOS>
|
||||
<TabBarItemIOS
|
||||
name="greenTab"
|
||||
icon={_ix_DEPRECATED('more')}
|
||||
accessibilityLabel="Green Tab"
|
||||
</TabBarIOS.Item>
|
||||
<TabBarIOS.Item
|
||||
systemIcon="more"
|
||||
selected={this.state.selectedTab === 'greenTab'}
|
||||
onPress={() => {
|
||||
this.setState({
|
||||
@@ -89,7 +81,7 @@ var TabBarExample = React.createClass({
|
||||
});
|
||||
}}>
|
||||
{this._renderContent('#21551C', 'Green Tab')}
|
||||
</TabBarItemIOS>
|
||||
</TabBarIOS.Item>
|
||||
</TabBarIOS>
|
||||
);
|
||||
},
|
||||
@@ -107,14 +99,4 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
// This is needed because the actual image may not exist as a file and
|
||||
// is used by the native code to load a system image.
|
||||
// TODO(nicklockwood): How can this fit our require system?
|
||||
function _ix_DEPRECATED(imageUri) {
|
||||
return {
|
||||
uri: imageUri,
|
||||
isStatic: true,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = TabBarExample;
|
||||
|
||||
@@ -251,7 +251,7 @@ exports.examples = [
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="true">
|
||||
<TextInput secureTextEntry={true} style={styles.default} value="abc" />
|
||||
<TextInput password={true} style={styles.default} value="abc" />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
004D28A31AAF61C70097A701 /* UIExplorerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 004D28A21AAF61C70097A701 /* UIExplorerTests.m */; };
|
||||
00D2771A1AB8C3E100DC1E48 /* libRCTWebSocketDebugger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00D277131AB8C2C700DC1E48 /* libRCTWebSocketDebugger.a */; };
|
||||
00D2771C1AB8C55500DC1E48 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 00D2771B1AB8C55500DC1E48 /* libicucore.dylib */; };
|
||||
13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FE81AA91428003F314A /* libRCTImage.a */; };
|
||||
134180011AA9153C003F314A /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FEF1AA914B8003F314A /* libRCTText.a */; };
|
||||
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; };
|
||||
@@ -118,7 +117,6 @@
|
||||
004D28A11AAF61C70097A701 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
004D28A21AAF61C70097A701 /* UIExplorerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIExplorerTests.m; sourceTree = "<group>"; };
|
||||
00D2770E1AB8C2C700DC1E48 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = ../../Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj; sourceTree = "<group>"; };
|
||||
00D2771B1AB8C55500DC1E48 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
|
||||
13417FE31AA91428003F314A /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
|
||||
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
|
||||
134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
|
||||
@@ -150,7 +148,6 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
00D2771C1AB8C55500DC1E48 /* libicucore.dylib in Frameworks */,
|
||||
14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */,
|
||||
00D2771A1AB8C3E100DC1E48 /* libRCTWebSocketDebugger.a in Frameworks */,
|
||||
58005BF21ABA80A60062E044 /* libRCTTest.a in Frameworks */,
|
||||
@@ -207,7 +204,6 @@
|
||||
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */,
|
||||
00D2770E1AB8C2C700DC1E48 /* RCTWebSocketDebugger.xcodeproj */,
|
||||
D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */,
|
||||
00D2771B1AB8C55500DC1E48 /* libicucore.dylib */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -305,7 +301,9 @@
|
||||
004D289F1AAF61C70097A701 /* UIExplorerTests */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:UIExplorer.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -72,7 +72,8 @@
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
@@ -90,7 +91,8 @@
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
|
||||
+4
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
|
||||
@@ -233,7 +233,9 @@
|
||||
004D289F1AAF61C70097A701 /* IntegrationTestsTests */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:IntegrationTests.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -54,7 +54,9 @@
|
||||
14C644C21AB0DFC900DE3C65 /* RCTActionSheetManager.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
|
||||
@@ -11,14 +11,17 @@
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface RCTActionSheetManager() <UIActionSheetDelegate>
|
||||
@interface RCTActionSheetManager () <UIActionSheetDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTActionSheetManager {
|
||||
@implementation RCTActionSheetManager
|
||||
{
|
||||
NSMutableDictionary *_callbacks;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
@@ -27,12 +30,10 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)showActionSheetWithOptions:(NSDictionary *)options
|
||||
failureCallback:(RCTResponseSenderBlock)failureCallback
|
||||
successCallback:(RCTResponseSenderBlock)successCallback
|
||||
RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options
|
||||
failureCallback:(RCTResponseSenderBlock)failureCallback
|
||||
successCallback:(RCTResponseSenderBlock)successCallback)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIActionSheet *actionSheet = [[UIActionSheet alloc] init];
|
||||
|
||||
@@ -62,12 +63,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
- (void)showShareActionSheetWithOptions:(NSDictionary *)options
|
||||
failureCallback:(RCTResponseSenderBlock)failureCallback
|
||||
successCallback:(RCTResponseSenderBlock)successCallback
|
||||
RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
|
||||
failureCallback:(RCTResponseSenderBlock)failureCallback
|
||||
successCallback:(RCTResponseSenderBlock)successCallback)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSMutableArray *items = [NSMutableArray array];
|
||||
id message = options[@"message"];
|
||||
@@ -134,7 +133,7 @@
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
static NSString *keyForInstance(id instance)
|
||||
NS_INLINE NSString *keyForInstance(id instance)
|
||||
{
|
||||
return [NSString stringWithFormat:@"%p", instance];
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
|
||||
@implementation RCTAdSupport
|
||||
|
||||
- (void)getAdvertisingId:(RCTResponseSenderBlock)callback withErrorCallback:(RCTResponseSenderBlock)errorCallback
|
||||
{
|
||||
RCT_EXPORT();
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
RCT_EXPORT_METHOD(getAdvertisingId:(RCTResponseSenderBlock)callback
|
||||
withErrorCallback:(RCTResponseSenderBlock)errorCallback)
|
||||
{
|
||||
if ([ASIdentifierManager class]) {
|
||||
callback(@[[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]]);
|
||||
} else {
|
||||
@@ -22,12 +23,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getAdvertisingTrackingEnabled:(RCTResponseSenderBlock)callback withErrorCallback:(RCTResponseSenderBlock)errorCallback
|
||||
RCT_EXPORT_METHOD(getAdvertisingTrackingEnabled:(RCTResponseSenderBlock)callback
|
||||
withErrorCallback:(RCTResponseSenderBlock)errorCallback)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
if ([ASIdentifierManager class]) {
|
||||
bool hasTracking = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled];
|
||||
BOOL hasTracking = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled];
|
||||
callback(@[@(hasTracking)]);
|
||||
} else {
|
||||
return errorCallback(@[@"as_identifier_unavailable"]);
|
||||
|
||||
@@ -46,7 +46,9 @@
|
||||
832C819B1AAF6E1A007FA2F7 /* RCTAdSupport.m */,
|
||||
832C81811AAF6DEF007FA2F7 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
832C81811AAF6DEF007FA2F7 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule Animation
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var RCTAnimationManager = require('NativeModules').AnimationManager;
|
||||
var AnimationUtils = require('AnimationUtils');
|
||||
|
||||
type EasingFunction = (t: number) => number;
|
||||
|
||||
var Animation = {
|
||||
Mixin: require('AnimationMixin'),
|
||||
|
||||
startAnimation: function(
|
||||
node: any,
|
||||
duration: number,
|
||||
delay: number,
|
||||
easing: (string | EasingFunction),
|
||||
properties: {[key: string]: any}
|
||||
): number {
|
||||
var nodeHandle = +node.getNodeHandle();
|
||||
var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing);
|
||||
var tag: number = RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties);
|
||||
return tag;
|
||||
},
|
||||
|
||||
stopAnimation: function(tag: number) {
|
||||
RCTAnimationManager.stopAnimation(tag);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = Animation;
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule AnimationExperimental
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var RCTAnimationManager = require('NativeModules').AnimationExperimentalManager;
|
||||
var AnimationUtils = require('AnimationUtils');
|
||||
|
||||
type EasingFunction = (t: number) => number;
|
||||
|
||||
var Properties = {
|
||||
opacity: true,
|
||||
position: true,
|
||||
positionX: true,
|
||||
positionY: true,
|
||||
rotation: true,
|
||||
scaleXY: true,
|
||||
};
|
||||
|
||||
type ValueType = number | Array<number> | {[key: string]: number};
|
||||
|
||||
/**
|
||||
* This is an experimental module that is under development, incomplete,
|
||||
* potentially buggy, not used in any production apps, and will probably change
|
||||
* in non-backward compatible ways.
|
||||
*
|
||||
* Use at your own risk.
|
||||
*/
|
||||
var AnimationExperimental = {
|
||||
startAnimation: function(
|
||||
anim: {
|
||||
node: any;
|
||||
duration: number;
|
||||
easing: ($Enum<typeof AnimationUtils.Defaults> | EasingFunction);
|
||||
property: $Enum<typeof Properties>;
|
||||
toValue: ValueType;
|
||||
fromValue?: ValueType;
|
||||
delay?: number;
|
||||
},
|
||||
callback?: ?(finished: bool) => void
|
||||
): number {
|
||||
var nodeHandle = anim.node.getNodeHandle();
|
||||
var easingSample = AnimationUtils.evaluateEasingFunction(
|
||||
anim.duration,
|
||||
anim.easing
|
||||
);
|
||||
var tag: number = AnimationUtils.allocateTag();
|
||||
var props = {};
|
||||
props[anim.property] = {to: anim.toValue};
|
||||
RCTAnimationManager.startAnimation(
|
||||
nodeHandle,
|
||||
tag,
|
||||
anim.duration,
|
||||
anim.delay,
|
||||
easingSample,
|
||||
props,
|
||||
callback
|
||||
);
|
||||
return tag;
|
||||
},
|
||||
|
||||
stopAnimation: function(tag: number) {
|
||||
RCTAnimationManager.stopAnimation(tag);
|
||||
},
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
if (RCTAnimationManager && RCTAnimationManager.Properties) {
|
||||
var a = Object.keys(Properties);
|
||||
var b = RCTAnimationManager.Properties;
|
||||
var diff = a.filter((i) => b.indexOf(i) < 0).concat(
|
||||
b.filter((i) => a.indexOf(i) < 0)
|
||||
);
|
||||
if (diff.length > 0) {
|
||||
throw new Error('JS animation properties don\'t match native properties.' +
|
||||
JSON.stringify(diff, null, ' '));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AnimationExperimental;
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule AnimationMixin
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var AnimationUtils = require('AnimationUtils');
|
||||
var RCTAnimationManager = require('NativeModules').AnimationManager;
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
type EasingFunction = (t: number) => number;
|
||||
|
||||
var AnimationMixin = {
|
||||
getInitialState: function(): Object {
|
||||
return {};
|
||||
},
|
||||
|
||||
startAnimation: function(
|
||||
refKey: string,
|
||||
duration: number,
|
||||
delay: number,
|
||||
easing: (string | EasingFunction),
|
||||
properties: {[key: string]: any}
|
||||
): number {
|
||||
var ref = this.refs[refKey];
|
||||
invariant(
|
||||
ref,
|
||||
'Invalid refKey ' + refKey + '; ' +
|
||||
'valid refs: ' + JSON.stringify(Object.keys(this.refs))
|
||||
);
|
||||
|
||||
var nodeHandle = +ref.getNodeHandle();
|
||||
var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing);
|
||||
var tag: number = RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties);
|
||||
return tag;
|
||||
},
|
||||
|
||||
stopAnimation: function(tag: number) {
|
||||
RCTAnimationManager.stopAnimation(tag);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = AnimationMixin;
|
||||
@@ -20,27 +20,27 @@
|
||||
type EasingFunction = (t: number) => number;
|
||||
|
||||
var defaults = {
|
||||
easeInQuad: function(t) {
|
||||
easeInQuad: function(t: number): number {
|
||||
return t * t;
|
||||
},
|
||||
easeOutQuad: function(t) {
|
||||
easeOutQuad: function(t: number): number {
|
||||
return -t * (t - 2);
|
||||
},
|
||||
easeInOutQuad: function(t) {
|
||||
easeInOutQuad: function(t: number): number {
|
||||
t = t * 2;
|
||||
if (t < 1) {
|
||||
return 0.5 * t * t;
|
||||
}
|
||||
return -((t - 1) * (t - 3) - 1) / 2;
|
||||
},
|
||||
easeInCubic: function(t) {
|
||||
easeInCubic: function(t: number): number {
|
||||
return t * t * t;
|
||||
},
|
||||
easeOutCubic: function(t) {
|
||||
easeOutCubic: function(t: number): number {
|
||||
t -= 1;
|
||||
return t * t * t + 1;
|
||||
},
|
||||
easeInOutCubic: function(t) {
|
||||
easeInOutCubic: function(t: number): number {
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return 0.5 * t * t * t;
|
||||
@@ -48,14 +48,14 @@ var defaults = {
|
||||
t -= 2;
|
||||
return (t * t * t + 2) / 2;
|
||||
},
|
||||
easeInQuart: function(t) {
|
||||
easeInQuart: function(t: number): number {
|
||||
return t * t * t * t;
|
||||
},
|
||||
easeOutQuart: function(t) {
|
||||
easeOutQuart: function(t: number): number {
|
||||
t -= 1;
|
||||
return -(t * t * t * t - 1);
|
||||
},
|
||||
easeInOutQuart: function(t) {
|
||||
easeInOutQuart: function(t: number): number {
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return 0.5 * t * t * t * t;
|
||||
@@ -63,14 +63,14 @@ var defaults = {
|
||||
t -= 2;
|
||||
return -(t * t * t * t - 2) / 2;
|
||||
},
|
||||
easeInQuint: function(t) {
|
||||
easeInQuint: function(t: number): number {
|
||||
return t * t * t * t * t;
|
||||
},
|
||||
easeOutQuint: function(t) {
|
||||
easeOutQuint: function(t: number): number {
|
||||
t -= 1;
|
||||
return t * t * t * t * t + 1;
|
||||
},
|
||||
easeInOutQuint: function(t) {
|
||||
easeInOutQuint: function(t: number): number {
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return (t * t * t * t * t) / 2;
|
||||
@@ -78,22 +78,22 @@ var defaults = {
|
||||
t -= 2;
|
||||
return (t * t * t * t * t + 2) / 2;
|
||||
},
|
||||
easeInSine: function(t) {
|
||||
easeInSine: function(t: number): number {
|
||||
return -Math.cos(t * (Math.PI / 2)) + 1;
|
||||
},
|
||||
easeOutSine: function(t) {
|
||||
easeOutSine: function(t: number): number {
|
||||
return Math.sin(t * (Math.PI / 2));
|
||||
},
|
||||
easeInOutSine: function(t) {
|
||||
easeInOutSine: function(t: number): number {
|
||||
return -(Math.cos(Math.PI * t) - 1) / 2;
|
||||
},
|
||||
easeInExpo: function(t) {
|
||||
easeInExpo: function(t: number): number {
|
||||
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
|
||||
},
|
||||
easeOutExpo: function(t) {
|
||||
easeOutExpo: function(t: number): number {
|
||||
return (t === 1) ? 1 : (-Math.pow(2, -10 * t) + 1);
|
||||
},
|
||||
easeInOutExpo: function(t) {
|
||||
easeInOutExpo: function(t: number): number {
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -106,14 +106,14 @@ var defaults = {
|
||||
}
|
||||
return (-Math.pow(2, -10 * (t - 1)) + 2) / 2;
|
||||
},
|
||||
easeInCirc: function(t) {
|
||||
easeInCirc: function(t: number): number {
|
||||
return -(Math.sqrt(1 - t * t) - 1);
|
||||
},
|
||||
easeOutCirc: function(t) {
|
||||
easeOutCirc: function(t: number): number {
|
||||
t -= 1;
|
||||
return Math.sqrt(1 - t * t);
|
||||
},
|
||||
easeInOutCirc: function(t) {
|
||||
easeInOutCirc: function(t: number): number {
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return -(Math.sqrt(1 - t * t) - 1) / 2;
|
||||
@@ -121,7 +121,7 @@ var defaults = {
|
||||
t -= 2;
|
||||
return (Math.sqrt(1 - t * t) + 1) / 2;
|
||||
},
|
||||
easeInElastic: function(t) {
|
||||
easeInElastic: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
var p = 0.3;
|
||||
if (t === 0) {
|
||||
@@ -134,7 +134,7 @@ var defaults = {
|
||||
t -= 1;
|
||||
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
|
||||
},
|
||||
easeOutElastic: function(t) {
|
||||
easeOutElastic: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
var p = 0.3;
|
||||
if (t === 0) {
|
||||
@@ -146,7 +146,7 @@ var defaults = {
|
||||
var s = p / (2 * Math.PI) * Math.asin(1);
|
||||
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
|
||||
},
|
||||
easeInOutElastic: function(t) {
|
||||
easeInOutElastic: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
var p = 0.3 * 1.5;
|
||||
if (t === 0) {
|
||||
@@ -164,16 +164,16 @@ var defaults = {
|
||||
t -= 1;
|
||||
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) / 2 + 1;
|
||||
},
|
||||
easeInBack: function(t) {
|
||||
easeInBack: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
return t * t * ((s + 1) * t - s);
|
||||
},
|
||||
easeOutBack: function(t) {
|
||||
easeOutBack: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
t -= 1;
|
||||
return (t * t * ((s + 1) * t + s) + 1);
|
||||
},
|
||||
easeInOutBack: function(t) {
|
||||
easeInOutBack: function(t: number): number {
|
||||
var s = 1.70158 * 1.525;
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
@@ -182,10 +182,10 @@ var defaults = {
|
||||
t -= 2;
|
||||
return (t * t * ((s + 1) * t + s) + 2) / 2;
|
||||
},
|
||||
easeInBounce: function(t) {
|
||||
easeInBounce: function(t: number): number {
|
||||
return 1 - this.easeOutBounce(1 - t);
|
||||
},
|
||||
easeOutBounce: function(t) {
|
||||
easeOutBounce: function(t: number): number {
|
||||
if (t < (1 / 2.75)) {
|
||||
return 7.5625 * t * t;
|
||||
} else if (t < (2 / 2.75)) {
|
||||
@@ -199,7 +199,7 @@ var defaults = {
|
||||
return 7.5625 * t * t + 0.984375;
|
||||
}
|
||||
},
|
||||
easeInOutBounce: function(t) {
|
||||
easeInOutBounce: function(t: number): number {
|
||||
if (t < 0.5) {
|
||||
return this.easeInBounce(t * 2) / 2;
|
||||
}
|
||||
@@ -234,4 +234,6 @@ module.exports = {
|
||||
|
||||
return samples;
|
||||
},
|
||||
|
||||
Defaults: defaults,
|
||||
};
|
||||
|
||||
@@ -17,18 +17,20 @@ var RCTUIManager = require('NativeModules').UIManager;
|
||||
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
|
||||
var keyMirror = require('keyMirror');
|
||||
|
||||
var Types = keyMirror({
|
||||
var TypesEnum = {
|
||||
spring: true,
|
||||
linear: true,
|
||||
easeInEaseOut: true,
|
||||
easeIn: true,
|
||||
easeOut: true,
|
||||
});
|
||||
};
|
||||
var Types = keyMirror(TypesEnum);
|
||||
|
||||
var Properties = keyMirror({
|
||||
var PropertiesEnum = {
|
||||
opacity: true,
|
||||
scaleXY: true,
|
||||
});
|
||||
};
|
||||
var Properties = keyMirror(PropertiesEnum);
|
||||
|
||||
var animChecker = createStrictShapeTypeChecker({
|
||||
duration: PropTypes.number,
|
||||
@@ -48,8 +50,8 @@ type Anim = {
|
||||
delay?: number;
|
||||
springDamping?: number;
|
||||
initialVelocity?: number;
|
||||
type?: $Enum<typeof Types>;
|
||||
property?: $Enum<typeof Properties>;
|
||||
type?: $Enum<typeof TypesEnum>;
|
||||
property?: $Enum<typeof PropertiesEnum>;
|
||||
}
|
||||
|
||||
var configChecker = createStrictShapeTypeChecker({
|
||||
|
||||
+18
-18
@@ -7,7 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13BE3DEE1AC21097009241FE /* RCTAnimationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAnimationManager.m */; };
|
||||
13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -23,9 +23,9 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
134814201AA4EA6300B7C361 /* libRCTAnimation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimation.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13BE3DEC1AC21097009241FE /* RCTAnimationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationManager.h; sourceTree = "<group>"; };
|
||||
13BE3DED1AC21097009241FE /* RCTAnimationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationManager.m; sourceTree = "<group>"; };
|
||||
134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimationExperimental.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationExperimentalManager.h; sourceTree = "<group>"; };
|
||||
13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationExperimentalManager.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -42,7 +42,7 @@
|
||||
134814211AA4EA7D00B7C361 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
134814201AA4EA6300B7C361 /* libRCTAnimation.a */,
|
||||
134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -50,8 +50,8 @@
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13BE3DEC1AC21097009241FE /* RCTAnimationManager.h */,
|
||||
13BE3DED1AC21097009241FE /* RCTAnimationManager.m */,
|
||||
13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */,
|
||||
13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
@@ -59,9 +59,9 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
58B511DA1A9E6C8500147676 /* RCTAnimation */ = {
|
||||
58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimation" */;
|
||||
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */;
|
||||
buildPhases = (
|
||||
58B511D71A9E6C8500147676 /* Sources */,
|
||||
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||
@@ -71,9 +71,9 @@
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = RCTAnimation;
|
||||
name = RCTAnimationExperimental;
|
||||
productName = RCTDataManager;
|
||||
productReference = 134814201AA4EA6300B7C361 /* libRCTAnimation.a */;
|
||||
productReference = 134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
@@ -90,7 +90,7 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimation" */;
|
||||
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
@@ -102,7 +102,7 @@
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
58B511DA1A9E6C8500147676 /* RCTAnimation */,
|
||||
58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -112,7 +112,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13BE3DEE1AC21097009241FE /* RCTAnimationManager.m in Sources */,
|
||||
13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -203,7 +203,7 @@
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RCTAnimation;
|
||||
PRODUCT_NAME = RCTAnimationExperimental;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -218,7 +218,7 @@
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RCTAnimation;
|
||||
PRODUCT_NAME = RCTAnimationExperimental;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
@@ -226,7 +226,7 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimation" */ = {
|
||||
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511ED1A9E6C8500147676 /* Debug */,
|
||||
@@ -235,7 +235,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimation" */ = {
|
||||
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511F01A9E6C8500147676 /* Debug */,
|
||||
+1
-1
@@ -12,6 +12,6 @@
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
@interface RCTAnimationManager : NSObject <RCTBridgeModule>
|
||||
@interface RCTAnimationExperimentalManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
+118
-40
@@ -7,12 +7,13 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTAnimationManager.h"
|
||||
#import "RCTAnimationExperimentalManager.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#if CGFLOAT_IS_DOUBLE
|
||||
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_D
|
||||
@@ -20,17 +21,48 @@
|
||||
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_F
|
||||
#endif
|
||||
|
||||
@implementation RCTAnimationManager
|
||||
@implementation RCTAnimationExperimentalManager
|
||||
{
|
||||
RCTSparseArray *_animationRegistry; // Main thread only; animation tag -> view tag
|
||||
RCTSparseArray *_callbackRegistry; // Main thread only; animation tag -> callback
|
||||
NSDictionary *_keypathMapping;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_animationRegistry = [[RCTSparseArray alloc] init];
|
||||
_callbackRegistry = [[RCTSparseArray alloc] init];
|
||||
_keypathMapping = @{
|
||||
@"opacity": @{
|
||||
@"keypath": @"opacity",
|
||||
@"type": @"NSNumber",
|
||||
},
|
||||
@"position": @{
|
||||
@"keypath": @"position",
|
||||
@"type": @"CGPoint",
|
||||
},
|
||||
@"positionX": @{
|
||||
@"keypath": @"position.x",
|
||||
@"type": @"NSNumber",
|
||||
},
|
||||
@"positionY": @{
|
||||
@"keypath": @"position.y",
|
||||
@"type": @"NSNumber",
|
||||
},
|
||||
@"rotation": @{
|
||||
@"keypath": @"transform.rotation.z",
|
||||
@"type": @"NSNumber",
|
||||
},
|
||||
@"scaleXY": @{
|
||||
@"keypath": @"transform.scale",
|
||||
@"type": @"CGPoint",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -61,25 +93,50 @@
|
||||
};
|
||||
}
|
||||
|
||||
- (void)startAnimationForTag:(NSNumber *)reactTag animationTag:(NSNumber *)animationTag duration:(double)duration delay:(double)delay easingSample:(NSArray *)easingSample properties:(NSDictionary *)properties
|
||||
static void RCTInvalidAnimationProp(RCTSparseArray *callbacks, NSNumber *tag, NSString *key, id value)
|
||||
{
|
||||
RCT_EXPORT(startAnimation);
|
||||
RCTResponseSenderBlock callback = callbacks[tag];
|
||||
RCTLogError(@"Invalid animation property `%@ = %@`", key, value);
|
||||
if (callback) {
|
||||
callback(@[@NO]);
|
||||
callbacks[tag] = nil;
|
||||
}
|
||||
[CATransaction commit];
|
||||
return;
|
||||
}
|
||||
|
||||
__weak RCTAnimationManager *weakSelf = self;
|
||||
RCT_EXPORT_METHOD(startAnimation:(NSNumber *)reactTag
|
||||
animationTag:(NSNumber *)animationTag
|
||||
duration:(NSTimeInterval)duration
|
||||
delay:(NSTimeInterval)delay
|
||||
easingSample:(NSArray *)easingSample
|
||||
properties:(NSDictionary *)properties
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
__weak RCTAnimationExperimentalManager *weakSelf = self;
|
||||
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
RCTAnimationManager *strongSelf = weakSelf;
|
||||
RCTAnimationExperimentalManager *strongSelf = weakSelf;
|
||||
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
if (!view) {
|
||||
RCTLogWarn(@"React tag %@ is not registered with the view registry", reactTag);
|
||||
RCTLogWarn(@"React tag #%@ is not registered with the view registry", reactTag);
|
||||
return;
|
||||
}
|
||||
|
||||
[properties enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
__block BOOL completionBlockSet = NO;
|
||||
[CATransaction begin];
|
||||
for (NSString *prop in properties) {
|
||||
NSString *keypath = _keypathMapping[prop][@"keypath"];
|
||||
id obj = properties[prop][@"to"];
|
||||
if (!keypath) {
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
|
||||
}
|
||||
NSValue *toValue = nil;
|
||||
if ([key isEqualToString:@"scaleXY"]) {
|
||||
key = @"transform.scale";
|
||||
toValue = obj[0];
|
||||
if ([keypath isEqualToString:@"transform.scale"]) {
|
||||
CGPoint point = [RCTConvert CGPoint:obj];
|
||||
if (point.x != point.y) {
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
|
||||
}
|
||||
toValue = @(point.x);
|
||||
} else if ([obj respondsToSelector:@selector(count)]) {
|
||||
switch ([obj count]) {
|
||||
case 2:
|
||||
@@ -95,11 +152,15 @@
|
||||
case 16:
|
||||
toValue = [NSValue valueWithCGAffineTransform:[RCTConvert CGAffineTransform:obj]];
|
||||
break;
|
||||
default:
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
|
||||
}
|
||||
} else if (![obj respondsToSelector:@selector(objCType)]) {
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
|
||||
}
|
||||
if (!toValue) {
|
||||
toValue = obj;
|
||||
}
|
||||
|
||||
if (!toValue) toValue = obj;
|
||||
|
||||
const char *typeName = toValue.objCType;
|
||||
|
||||
size_t count;
|
||||
@@ -150,7 +211,7 @@
|
||||
break;
|
||||
}
|
||||
|
||||
NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:key];
|
||||
NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:keypath];
|
||||
CGFloat fromFields[count];
|
||||
[fromValue getValue:fromFields];
|
||||
|
||||
@@ -161,49 +222,66 @@
|
||||
CGFloat t = sample.CG_APPEND(, floatValue, doubleValue);
|
||||
[sampledValues addObject:interpolationBlock(t)];
|
||||
}
|
||||
|
||||
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:key];
|
||||
animation.beginTime = CACurrentMediaTime() + delay / 1000.0;
|
||||
animation.duration = duration / 1000.0;
|
||||
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
|
||||
animation.beginTime = CACurrentMediaTime() + delay;
|
||||
animation.duration = duration;
|
||||
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
||||
animation.values = sampledValues;
|
||||
|
||||
[view.layer setValue:toValue forKey:key];
|
||||
|
||||
NSString *animationKey = [NSString stringWithFormat:@"RCT.%@.%@", animationTag, key];
|
||||
[view.layer addAnimation:animation forKey:animationKey];
|
||||
}];
|
||||
|
||||
@try {
|
||||
[view.layer setValue:toValue forKey:keypath];
|
||||
NSString *animationKey = [@"RCT" stringByAppendingString:RCTJSONStringify(@{@"tag": animationTag, @"key": keypath}, nil)];
|
||||
[view.layer addAnimation:animation forKey:animationKey];
|
||||
if (!completionBlockSet) {
|
||||
strongSelf->_callbackRegistry[animationTag] = callback;
|
||||
[CATransaction setCompletionBlock:^{
|
||||
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
|
||||
if (cb) {
|
||||
cb(@[@YES]);
|
||||
strongSelf->_callbackRegistry[animationTag] = nil;
|
||||
}
|
||||
}];
|
||||
completionBlockSet = YES;
|
||||
}
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, toValue);
|
||||
}
|
||||
}
|
||||
[CATransaction commit];
|
||||
strongSelf->_animationRegistry[animationTag] = reactTag;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)stopAnimation:(NSNumber *)animationTag
|
||||
RCT_EXPORT_METHOD(stopAnimation:(NSNumber *)animationTag)
|
||||
{
|
||||
RCT_EXPORT(stopAnimation);
|
||||
|
||||
__weak RCTAnimationManager *weakSelf = self;
|
||||
__weak RCTAnimationExperimentalManager *weakSelf = self;
|
||||
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
RCTAnimationManager *strongSelf = weakSelf;
|
||||
RCTAnimationExperimentalManager *strongSelf = weakSelf;
|
||||
|
||||
NSNumber *reactTag = strongSelf->_animationRegistry[animationTag];
|
||||
if (!reactTag) return;
|
||||
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
for (NSString *animationKey in view.layer.animationKeys) {
|
||||
if ([animationKey hasPrefix:@"RCT"]) {
|
||||
NSRange periodLocation = [animationKey rangeOfString:@"." options:0 range:(NSRange){3, animationKey.length - 3}];
|
||||
if (periodLocation.location != NSNotFound) {
|
||||
NSInteger integerTag = [[animationKey substringWithRange:(NSRange){3, periodLocation.location}] integerValue];
|
||||
if (animationTag.integerValue == integerTag) {
|
||||
[view.layer removeAnimationForKey:animationKey];
|
||||
}
|
||||
if ([animationKey hasPrefix:@"RCT{"]) {
|
||||
NSDictionary *data = RCTJSONParse([animationKey substringFromIndex:3], nil);
|
||||
if (animationTag.integerValue == [data[@"tag"] integerValue]) {
|
||||
[view.layer removeAnimationForKey:animationKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
|
||||
if (cb) {
|
||||
cb(@[@NO]);
|
||||
strongSelf->_callbackRegistry[animationTag] = nil;
|
||||
}
|
||||
strongSelf->_animationRegistry[animationTag] = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return @{@"Properties": [_keypathMapping allKeys] };
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -50,6 +50,7 @@ var RCTNavigatorItem = createReactIOSNativeComponentClass({
|
||||
rightButtonTitle: true,
|
||||
onNavRightButtonTap: true,
|
||||
tintColor: true,
|
||||
navigationBarHidden: true,
|
||||
backButtonTitle: true,
|
||||
titleTextColor: true,
|
||||
style: true,
|
||||
@@ -235,6 +236,11 @@ var NavigatorIOS = React.createClass({
|
||||
|
||||
}).isRequired,
|
||||
|
||||
/**
|
||||
* A Boolean value that indicates whether the navigation bar is hidden
|
||||
*/
|
||||
navigationBarHidden: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The default wrapper style for components in the navigator.
|
||||
* A common use case is to set the backgroundColor for every page
|
||||
@@ -547,6 +553,7 @@ var NavigatorIOS = React.createClass({
|
||||
backButtonTitle={route.backButtonTitle}
|
||||
rightButtonTitle={route.rightButtonTitle}
|
||||
onNavRightButtonTap={route.onRightButtonPress}
|
||||
navigationBarHidden={this.props.navigationBarHidden}
|
||||
tintColor={this.props.tintColor}>
|
||||
<Component
|
||||
navigator={this.navigator}
|
||||
|
||||
@@ -357,17 +357,23 @@ var ScrollResponderMixin = {
|
||||
* A helper function to zoom to a specific rect in the scrollview.
|
||||
* @param {object} rect Should have shape {x, y, w, h}
|
||||
*/
|
||||
scrollResponderZoomTo: function(rect: { x: number; y: number; w: number; h: number; }) {
|
||||
scrollResponderZoomTo: function(rect: { x: number; y: number; width: number; height: number; }) {
|
||||
RCTUIManagerDeprecated.zoomToRect(this.getNodeHandle(), rect);
|
||||
},
|
||||
|
||||
/**
|
||||
* This method should be used as the callback to onFocus in a TextInputs'
|
||||
* parent view. Note that any module using this mixin needs to return
|
||||
* the parent view's ref in getScrollViewRef() in order to use this method
|
||||
* the parent view's ref in getScrollViewRef() in order to use this method.
|
||||
* @param {any} nodeHandle The TextInput node handle
|
||||
* @param {number} additionalOffset The scroll view's top "contentInset".
|
||||
* Default is 0.
|
||||
* @param {bool} preventNegativeScrolling Whether to allow pulling the content
|
||||
* down to make it meet the keyboard's top. Default is false.
|
||||
*/
|
||||
scrollResponderScrollNativeHandleToKeyboard: function(nodeHandle: any, additionalOffset?: number) {
|
||||
scrollResponderScrollNativeHandleToKeyboard: function(nodeHandle: any, additionalOffset?: number, preventNegativeScrollOffset?: bool) {
|
||||
this.additionalScrollOffset = additionalOffset || 0;
|
||||
this.preventNegativeScrollOffset = !!preventNegativeScrollOffset;
|
||||
RCTUIManager.measureLayout(
|
||||
nodeHandle,
|
||||
this.getNodeHandle(),
|
||||
@@ -386,14 +392,23 @@ var ScrollResponderMixin = {
|
||||
* @param {number} width Width of the text input.
|
||||
* @param {number} height Height of the text input.
|
||||
*/
|
||||
scrollResponderInputMeasureAndScrollToKeyboard: function(left: number, top: number, width: number, height: number) {
|
||||
scrollResponderInputMeasureAndScrollToKeyboard: function(left: number, top: number, width: number, height: number) {
|
||||
if (this.keyboardWillOpenTo) {
|
||||
var scrollOffsetY =
|
||||
top - this.keyboardWillOpenTo.endCoordinates.screenY + 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.
|
||||
// If requested otherwise, cap the offset at 0 minimum to avoid content
|
||||
// shifting down.
|
||||
if (this.preventNegativeScrollOffset) {
|
||||
scrollOffsetY = Math.max(0, scrollOffsetY);
|
||||
}
|
||||
this.scrollResponderScrollTo(0, scrollOffsetY);
|
||||
}
|
||||
this.additionalOffset = 0;
|
||||
this.preventNegativeScrollOffset = false;
|
||||
},
|
||||
|
||||
scrollResponderTextInputFocusError: function(e: Event) {
|
||||
|
||||
@@ -40,7 +40,7 @@ var SliderIOS = React.createClass({
|
||||
* Default value is 0.
|
||||
*
|
||||
* *This is not a controlled component*, e.g. if you don't update
|
||||
* the value, the component won't be reset to it's inital value.
|
||||
* the value, the component won't be reset to its inital value.
|
||||
*/
|
||||
value: PropTypes.number,
|
||||
|
||||
@@ -82,6 +82,8 @@ var SliderIOS = React.createClass({
|
||||
<RCTSlider
|
||||
style={[styles.slider, this.props.style]}
|
||||
value={this.props.value}
|
||||
maximumValue={this.props.maximumValue}
|
||||
minimumValue={this.props.minimumValue}
|
||||
onChange={this._onValueChange}
|
||||
/>
|
||||
);
|
||||
@@ -94,8 +96,15 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
var validAttributes = {
|
||||
...ReactIOSViewAttributes.UIView,
|
||||
value: true,
|
||||
minimumValue: true,
|
||||
maximumValue: true,
|
||||
};
|
||||
|
||||
var RCTSlider = createReactIOSNativeComponentClass({
|
||||
validAttributes: merge(ReactIOSViewAttributes.UIView, {value: true}),
|
||||
validAttributes: validAttributes,
|
||||
uiViewClassName: 'RCTSlider',
|
||||
});
|
||||
|
||||
|
||||
@@ -11,308 +11,25 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Subscribable wraps EventEmitter in a clean interface, and provides a mixin
|
||||
* so components can easily subscribe to events and not worry about cleanup on
|
||||
* unmount.
|
||||
*
|
||||
* Also acts as a basic store because it records the last data that it emitted,
|
||||
* and provides a way to populate the initial data. The most recent data can be
|
||||
* fetched from the Subscribable by calling `get()`
|
||||
*
|
||||
* Advantages over EventEmitter + Subscibable.Mixin.addListenerOn:
|
||||
* - Cleaner usage: no strings to identify the event
|
||||
* - Lifespan pattern enforces cleanup
|
||||
* - More logical: Subscribable.Mixin now uses a Subscribable class
|
||||
* - Subscribable saves the last data and makes it available with `.get()`
|
||||
*
|
||||
* Legacy Subscribable.Mixin.addListenerOn allowed automatic subscription to
|
||||
* EventEmitters. Now we should avoid EventEmitters and wrap with Subscribable
|
||||
* instead:
|
||||
*
|
||||
* ```
|
||||
* AppState.networkReachability = new Subscribable(
|
||||
* RCTDeviceEventEmitter,
|
||||
* 'reachabilityDidChange',
|
||||
* (resp) => resp.network_reachability,
|
||||
* RCTReachability.getCurrentReachability
|
||||
* );
|
||||
*
|
||||
* var myComponent = React.createClass({
|
||||
* mixins: [Subscribable.Mixin],
|
||||
* getInitialState: function() {
|
||||
* return {
|
||||
* isConnected: AppState.networkReachability.get() !== 'none'
|
||||
* };
|
||||
* },
|
||||
* componentDidMount: function() {
|
||||
* this._reachSubscription = this.subscribeTo(
|
||||
* AppState.networkReachability,
|
||||
* (reachability) => {
|
||||
* this.setState({ isConnected: reachability !== 'none' })
|
||||
* }
|
||||
* );
|
||||
* },
|
||||
* render: function() {
|
||||
* return (
|
||||
* <Text>
|
||||
* {this.state.isConnected ? 'Network Connected' : 'No network'}
|
||||
* </Text>
|
||||
* <Text onPress={() => this._reachSubscription.remove()}>
|
||||
* End reachability subscription
|
||||
* </Text>
|
||||
* );
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
var EventEmitter = require('EventEmitter');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var logError = require('logError');
|
||||
|
||||
var SUBSCRIBABLE_INTERNAL_EVENT = 'subscriptionEvent';
|
||||
|
||||
type Data = Object;
|
||||
type EventMapping = (_: Data) => Data;
|
||||
|
||||
class Subscribable {
|
||||
_eventMapping: EventMapping;
|
||||
_lastData: Data;
|
||||
|
||||
/**
|
||||
* Creates a new Subscribable object
|
||||
*
|
||||
* @param {EventEmitter} eventEmitter Emitter to trigger subscription events.
|
||||
* @param {string} eventName Name of emitted event that triggers subscription
|
||||
* events.
|
||||
* @param {function} eventMapping (optional) Function to convert the output
|
||||
* of the eventEmitter to the subscription output.
|
||||
* @param {function} getInitData (optional) Async function to grab the initial
|
||||
* data to publish. Signature `function(successCallback, errorCallback)`.
|
||||
* The resolved data will be transformed with the eventMapping before it
|
||||
* gets emitted.
|
||||
*/
|
||||
constructor(eventEmitter: EventEmitter, eventName: string, eventMapping?: EventMapping, getInitData?: Function) {
|
||||
|
||||
this._internalEmitter = new EventEmitter();
|
||||
this._eventMapping = eventMapping || (data => data);
|
||||
|
||||
this._upstreamSubscription = eventEmitter.addListener(
|
||||
eventName,
|
||||
this._handleEmit,
|
||||
this
|
||||
);
|
||||
|
||||
// Asyncronously get the initial data, if provided
|
||||
getInitData && getInitData(this._handleInitData.bind(this), logError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last data emitted from the Subscribable, or undefined
|
||||
*/
|
||||
get(): Data {
|
||||
return this._lastData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from the upstream EventEmitter
|
||||
*/
|
||||
cleanup() {
|
||||
this._upstreamSubscription && this._upstreamSubscription.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new listener to the subscribable. This should almost never be used
|
||||
* directly, and instead through Subscribable.Mixin.subscribeTo
|
||||
*
|
||||
* @param {object} lifespan Object with `addUnmountCallback` that accepts
|
||||
* a handler to be called when the component unmounts. This is required and
|
||||
* desirable because it enforces cleanup. There is no easy way to leave the
|
||||
* subsciption hanging
|
||||
* {
|
||||
* addUnmountCallback: function(newUnmountHanlder) {...},
|
||||
* }
|
||||
* @param {function} callback Handler to call when Subscribable has data
|
||||
* updates
|
||||
* @param {object} context Object to bind the handler on, as "this"
|
||||
*
|
||||
* @return {object} the subscription object:
|
||||
* {
|
||||
* remove: function() {...},
|
||||
* }
|
||||
* Call `remove` to terminate the subscription before unmounting
|
||||
*/
|
||||
subscribe(lifespan: { addUnmountCallback: Function }, callback: Function, context: Object) {
|
||||
invariant(
|
||||
typeof lifespan.addUnmountCallback === 'function',
|
||||
'Must provide a valid lifespan, which provides a way to add a ' +
|
||||
'callback for when subscription can be cleaned up. This is used ' +
|
||||
'automatically by Subscribable.Mixin'
|
||||
);
|
||||
invariant(
|
||||
typeof callback === 'function',
|
||||
'Must provide a valid subscription handler.'
|
||||
);
|
||||
|
||||
// Add a listener to the internal EventEmitter
|
||||
var subscription = this._internalEmitter.addListener(
|
||||
SUBSCRIBABLE_INTERNAL_EVENT,
|
||||
callback,
|
||||
context
|
||||
);
|
||||
|
||||
// Clean up subscription upon the lifespan unmount callback
|
||||
lifespan.addUnmountCallback(() => {
|
||||
subscription.remove();
|
||||
});
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the initial data resolution. Currently behaves the same as
|
||||
* `_handleEmit`, but we may eventually want to keep track of the difference
|
||||
*/
|
||||
_handleInitData(dataInput: Data) {
|
||||
var emitData = this._eventMapping(dataInput);
|
||||
this._lastData = emitData;
|
||||
this._internalEmitter.emit(SUBSCRIBABLE_INTERNAL_EVENT, emitData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle new data emissions. Pass the data through our eventMapping
|
||||
* transformation, store it for later `get()`ing, and emit it for subscribers
|
||||
*/
|
||||
_handleEmit(dataInput: Data) {
|
||||
var emitData = this._eventMapping(dataInput);
|
||||
this._lastData = emitData;
|
||||
this._internalEmitter.emit(SUBSCRIBABLE_INTERNAL_EVENT, emitData);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Subscribable provides a mixin for safely subscribing a component to an
|
||||
* eventEmitter
|
||||
*
|
||||
* This will be replaced with the observe interface that will be coming soon to
|
||||
* React Core
|
||||
*/
|
||||
|
||||
var Subscribable = {};
|
||||
|
||||
Subscribable.Mixin = {
|
||||
|
||||
/**
|
||||
* @return {object} lifespan Object with `addUnmountCallback` that accepts
|
||||
* a handler to be called when the component unmounts
|
||||
* {
|
||||
* addUnmountCallback: function(newUnmountHanlder) {...},
|
||||
* }
|
||||
*/
|
||||
_getSubscribableLifespan: function() {
|
||||
if (!this._subscribableLifespan) {
|
||||
this._subscribableLifespan = {
|
||||
addUnmountCallback: (cb) => {
|
||||
this._endSubscribableLifespanCallbacks.push(cb);
|
||||
},
|
||||
};
|
||||
}
|
||||
return this._subscribableLifespan;
|
||||
},
|
||||
|
||||
_endSubscribableLifespan: function() {
|
||||
this._endSubscribableLifespanCallbacks.forEach(cb => cb());
|
||||
},
|
||||
|
||||
/**
|
||||
* Components use `subscribeTo` for listening to Subscribable stores. Cleanup
|
||||
* is automatic on component unmount.
|
||||
*
|
||||
* To stop listening to the subscribable and end the subscription early,
|
||||
* components should store the returned subscription object and invoke the
|
||||
* `remove()` function on it
|
||||
*
|
||||
* @param {Subscribable} subscription to subscribe to.
|
||||
* @param {function} listener Function to invoke when event occurs.
|
||||
* @param {object} context Object to bind the handler on, as "this"
|
||||
*
|
||||
* @return {object} the subscription object:
|
||||
* {
|
||||
* remove: function() {...},
|
||||
* }
|
||||
* Call `remove` to terminate the subscription before unmounting
|
||||
*/
|
||||
subscribeTo: function(subscribable, handler, context) {
|
||||
invariant(
|
||||
subscribable instanceof Subscribable,
|
||||
'Must provide a Subscribable'
|
||||
);
|
||||
return subscribable.subscribe(
|
||||
this._getSubscribableLifespan(),
|
||||
handler,
|
||||
context
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a Subscribable store, scoped to the component, that can be passed to
|
||||
* children. The component will automatically clean up the subscribable's
|
||||
* subscription to the eventEmitter when unmounting.
|
||||
*
|
||||
* `provideSubscribable` will always return the same Subscribable for any
|
||||
* particular emitter/eventName combo, so it can be called directly from
|
||||
* render, and it will never create duplicate Subscribables.
|
||||
*
|
||||
* @param {EventEmitter} eventEmitter Emitter to trigger subscription events.
|
||||
* @param {string} eventName Name of emitted event that triggers subscription
|
||||
* events.
|
||||
* @param {function} eventMapping (optional) Function to convert the output
|
||||
* of the eventEmitter to the subscription output.
|
||||
* @param {function} getInitData (optional) Async function to grab the initial
|
||||
* data to publish. Signature `function(successCallback, errorCallback)`.
|
||||
* The resolved data will be transformed with the eventMapping before it
|
||||
* gets emitted.
|
||||
*/
|
||||
provideSubscribable: function(eventEmitter, eventName, eventMapping, getInitData) {
|
||||
this._localSubscribables = this._localSubscribables || {};
|
||||
this._localSubscribables[eventEmitter] =
|
||||
this._localSubscribables[eventEmitter] || {};
|
||||
if (!this._localSubscribables[eventEmitter][eventName]) {
|
||||
this._localSubscribables[eventEmitter][eventName] =
|
||||
new Subscribable(eventEmitter, eventName, eventMapping, getInitData);
|
||||
}
|
||||
return this._localSubscribables[eventEmitter][eventName];
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes any local Subscribables created with `provideSubscribable`, so the
|
||||
* component can unmount without leaving any dangling listeners on
|
||||
* eventEmitters
|
||||
*/
|
||||
_cleanupLocalSubscribables: function() {
|
||||
if (!this._localSubscribables) {
|
||||
return;
|
||||
}
|
||||
Object.keys(this._localSubscribables).forEach((eventEmitter) => {
|
||||
var emitterSubscribables = this._localSubscribables[eventEmitter];
|
||||
if (emitterSubscribables) {
|
||||
Object.keys(emitterSubscribables).forEach((eventName) => {
|
||||
emitterSubscribables[eventName].cleanup();
|
||||
});
|
||||
}
|
||||
});
|
||||
this._localSubscribables = null;
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._endSubscribableLifespanCallbacks = [];
|
||||
|
||||
// DEPRECATED addListenerOn* usage:
|
||||
this._subscribableSubscriptions = [];
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
// Resolve the lifespan, which will cause Subscribable to clean any
|
||||
// remaining subscriptions
|
||||
this._endSubscribableLifespan && this._endSubscribableLifespan();
|
||||
|
||||
this._cleanupLocalSubscribables();
|
||||
|
||||
// DEPRECATED addListenerOn* usage uses _subscribableSubscriptions array
|
||||
// instead of lifespan
|
||||
this._subscribableSubscriptions.forEach(
|
||||
(subscription) => subscription.remove()
|
||||
);
|
||||
@@ -320,9 +37,6 @@ Subscribable.Mixin = {
|
||||
},
|
||||
|
||||
/**
|
||||
* DEPRECATED - Use `Subscribable` and `Mixin.subscribeTo` instead.
|
||||
* `addListenerOn` subscribes the component to an `EventEmitter`.
|
||||
*
|
||||
* Special form of calling `addListener` that *guarantees* that a
|
||||
* subscription *must* be tied to a component instance, and therefore will
|
||||
* be cleaned up when the component is unmounted. It is impossible to create
|
||||
@@ -335,7 +49,12 @@ Subscribable.Mixin = {
|
||||
* @param {function} listener Function to invoke when event occurs.
|
||||
* @param {object} context Object to use as listener context.
|
||||
*/
|
||||
addListenerOn: function(eventEmitter, eventType, listener, context) {
|
||||
addListenerOn: function(
|
||||
eventEmitter: EventEmitter,
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
context: Object
|
||||
) {
|
||||
this._subscribableSubscriptions.push(
|
||||
eventEmitter.addListener(eventType, listener, context)
|
||||
);
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
|
||||
var React = require('React');
|
||||
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
|
||||
var TabBarItemIOS = require('TabBarItemIOS');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var TabBarItemIOS = require('TabBarItemIOS');
|
||||
var View = require('View');
|
||||
|
||||
var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
|
||||
|
||||
@@ -22,6 +23,11 @@ var TabBarIOS = React.createClass({
|
||||
statics: {
|
||||
Item: TabBarItemIOS,
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
style: View.propTypes.style,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<RCTTabBar style={[styles.tabGroup, this.props.style]}>
|
||||
|
||||
@@ -24,12 +24,57 @@ var merge = require('merge');
|
||||
|
||||
var TabBarItemIOS = React.createClass({
|
||||
propTypes: {
|
||||
icon: Image.propTypes.source.isRequired,
|
||||
onPress: React.PropTypes.func.isRequired,
|
||||
selected: React.PropTypes.bool.isRequired,
|
||||
badgeValue: React.PropTypes.string,
|
||||
title: React.PropTypes.string,
|
||||
/**
|
||||
* Little red bubble that sits at the top right of the icon.
|
||||
*/
|
||||
badge: React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.number,
|
||||
]),
|
||||
/**
|
||||
* Items comes with a few predefined system icons. Note that if you are
|
||||
* using them, the title and selectedIcon will be overriden with the
|
||||
* system ones.
|
||||
*/
|
||||
systemIcon: React.PropTypes.oneOf([
|
||||
'bookmarks',
|
||||
'contacts',
|
||||
'downloads',
|
||||
'favorites',
|
||||
'featured',
|
||||
'history',
|
||||
'more',
|
||||
'most-recent',
|
||||
'most-viewed',
|
||||
'recents',
|
||||
'search',
|
||||
'top-rated',
|
||||
]),
|
||||
/**
|
||||
* A custom icon for the tab. It is ignored when a system icon is defined.
|
||||
*/
|
||||
icon: Image.propTypes.source,
|
||||
/**
|
||||
* A custom icon when the tab is selected. It is ignored when a system
|
||||
* icon is defined. If left empty, the icon will be tinted in blue.
|
||||
*/
|
||||
selectedIcon: Image.propTypes.source,
|
||||
/**
|
||||
* Callback when this tab is being selected, you should change the state of your
|
||||
* component to set selected={true}.
|
||||
*/
|
||||
onPress: React.PropTypes.func,
|
||||
/**
|
||||
* It specifies whether the children are visible or not. If you see a
|
||||
* blank content, you probably forgot to add a selected one.
|
||||
*/
|
||||
selected: React.PropTypes.bool,
|
||||
style: View.propTypes.style,
|
||||
/**
|
||||
* Text that appears under the icon. It is ignored when a system icon
|
||||
* is defined.
|
||||
*/
|
||||
title: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@@ -63,13 +108,21 @@ var TabBarItemIOS = React.createClass({
|
||||
tabContents = <View />;
|
||||
}
|
||||
|
||||
var icon = this.props.systemIcon || (
|
||||
this.props.icon && this.props.icon.uri
|
||||
);
|
||||
|
||||
var badge = typeof this.props.badge === 'number' ?
|
||||
'' + this.props.badge :
|
||||
this.props.badge;
|
||||
|
||||
return (
|
||||
<RCTTabBarItem
|
||||
icon={this.props.icon.uri}
|
||||
icon={icon}
|
||||
selectedIcon={this.props.selectedIcon && this.props.selectedIcon.uri}
|
||||
onPress={this.props.onPress}
|
||||
selected={this.props.selected}
|
||||
badgeValue={this.props.badgeValue}
|
||||
badgeValue={badge}
|
||||
title={this.props.title}
|
||||
style={[styles.tab, this.props.style]}>
|
||||
{tabContents}
|
||||
|
||||
@@ -198,12 +198,6 @@ var TextInput = React.createClass({
|
||||
* automatically enables it when there is text. Default value is false.
|
||||
*/
|
||||
enablesReturnKeyAutomatically: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* If true, the text input obscures the text entered so that sensitive text
|
||||
* like passwords stay secure. Default value is false.
|
||||
*/
|
||||
secureTextEntry: PropTypes.bool,
|
||||
/**
|
||||
* If true, the text input can be multiple lines. Default value is false.
|
||||
*/
|
||||
@@ -217,17 +211,21 @@ var TextInput = React.createClass({
|
||||
*/
|
||||
onFocus: PropTypes.func,
|
||||
/**
|
||||
* (text: string) => void
|
||||
*
|
||||
* Callback that is called when the text input's text changes.
|
||||
*/
|
||||
onChange: PropTypes.func,
|
||||
onChangeText: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Callback that is called when text input ends.
|
||||
*/
|
||||
onEndEditing: PropTypes.func,
|
||||
/**
|
||||
* Callback that is called when the text input's submit button is pressed.
|
||||
*/
|
||||
onSubmitEditing: PropTypes.func,
|
||||
/**
|
||||
* If true, the TextInput will be a password field. Default value is false.
|
||||
* If true, the text input obscures the text entered so that sensitive text
|
||||
* like passwords stay secure. Default value is false.
|
||||
*/
|
||||
password: PropTypes.bool,
|
||||
/**
|
||||
@@ -421,7 +419,7 @@ var TextInput = React.createClass({
|
||||
keyboardType={keyboardType}
|
||||
returnKeyType={returnKeyType}
|
||||
enablesReturnKeyAutomatically={this.props.enablesReturnKeyAutomatically}
|
||||
secureTextEntry={this.props.secureTextEntry}
|
||||
secureTextEntry={this.props.password || this.props.secureTextEntry}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
@@ -466,7 +464,7 @@ var TextInput = React.createClass({
|
||||
keyboardType={keyboardType}
|
||||
returnKeyType={returnKeyType}
|
||||
enablesReturnKeyAutomatically={this.props.enablesReturnKeyAutomatically}
|
||||
secureTextEntry={this.props.secureTextEntry}
|
||||
secureTextEntry={this.props.password || this.props.secureTextEntry}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
@@ -494,7 +492,7 @@ var TextInput = React.createClass({
|
||||
|
||||
_renderAndroid: function() {
|
||||
var autoCapitalize = autoCapitalizeConsts[this.props.autoCapitalize];
|
||||
return (
|
||||
var textContainer =
|
||||
<AndroidTextInput
|
||||
ref="input"
|
||||
style={[this.props.style]}
|
||||
@@ -505,11 +503,19 @@ var TextInput = React.createClass({
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
password={this.props.password}
|
||||
onEndEditing={this.props.onEndEditing}
|
||||
onSubmitEditing={this.props.onSubmitEditing}
|
||||
password={this.props.password || this.props.secureTextEntry}
|
||||
placeholder={this.props.placeholder}
|
||||
value={this.props.value}
|
||||
testID={this.props.testID}
|
||||
/>
|
||||
value={this.state.bufferedValue}
|
||||
/>;
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this._onPress}
|
||||
testID={this.props.testID}>
|
||||
{textContainer}
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var React = require('React');
|
||||
var POPAnimation = require('POPAnimation');
|
||||
var Animation = require('Animation');
|
||||
var AnimationExperimental = require('AnimationExperimental');
|
||||
var Touchable = require('Touchable');
|
||||
|
||||
var merge = require('merge');
|
||||
@@ -22,7 +22,7 @@ var copyProperties = require('copyProperties');
|
||||
var onlyChild = require('onlyChild');
|
||||
|
||||
type State = {
|
||||
animationID: ?number;
|
||||
animationID: ?number;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ var TouchableBounce = React.createClass({
|
||||
value: number,
|
||||
velocity: number,
|
||||
bounciness: number,
|
||||
fromValue?: ?Function | number,
|
||||
fromValue?: ?number,
|
||||
callback?: ?Function
|
||||
) {
|
||||
if (POPAnimation) {
|
||||
@@ -71,21 +71,21 @@ var TouchableBounce = React.createClass({
|
||||
toValue: [value, value],
|
||||
velocity: [velocity, velocity],
|
||||
springBounciness: bounciness,
|
||||
fromValue: (undefined: ?any),
|
||||
fromValue: fromValue ? [fromValue, fromValue] : undefined,
|
||||
};
|
||||
if (fromValue) {
|
||||
anim.fromValue = [fromValue, fromValue];
|
||||
}
|
||||
this.state.animationID = POPAnimation.createSpringAnimation(anim);
|
||||
this.addAnimation(this.state.animationID, callback);
|
||||
} else {
|
||||
Animation.startAnimation(this, 300, 0, 'easeOutBack', {scaleXY: [value, value]});
|
||||
if (fromValue && typeof fromValue === 'function') {
|
||||
callback = fromValue;
|
||||
}
|
||||
if (callback) {
|
||||
setTimeout(callback, 300);
|
||||
}
|
||||
AnimationExperimental.startAnimation(
|
||||
{
|
||||
node: this,
|
||||
duration: 300,
|
||||
easing: 'easeOutBack',
|
||||
property: 'scaleXY',
|
||||
toValue: { x: value, y: value},
|
||||
},
|
||||
callback
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Regular → Executable
+1
-4
@@ -13,7 +13,6 @@
|
||||
|
||||
var React = require('React');
|
||||
var Touchable = require('Touchable');
|
||||
|
||||
var onlyChild = require('onlyChild');
|
||||
|
||||
/**
|
||||
@@ -78,10 +77,8 @@ var TouchableWithoutFeedback = React.createClass({
|
||||
},
|
||||
|
||||
render: function(): ReactElement {
|
||||
// Note(vjeux): use cloneWithProps once React has been upgraded
|
||||
var child = onlyChild(this.props.children);
|
||||
// Note(avik): remove dynamic typecast once Flow has been upgraded
|
||||
return (React: any).cloneElement(child, {
|
||||
return (React: any).cloneElement(onlyChild(this.props.children), {
|
||||
accessible: true,
|
||||
testID: this.props.testID,
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule View
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ var WebViewState = keyMirror({
|
||||
var WebView = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
renderError: PropTypes.func.isRequired, // view to show if there's an error
|
||||
renderLoading: PropTypes.func.isRequired, // loading indicator to show
|
||||
renderError: PropTypes.func, // view to show if there's an error
|
||||
renderLoading: PropTypes.func, // loading indicator to show
|
||||
url: PropTypes.string.isRequired,
|
||||
automaticallyAdjustContentInsets: PropTypes.bool,
|
||||
contentInset: EdgeInsetsPropType,
|
||||
@@ -66,10 +66,10 @@ var WebView = React.createClass({
|
||||
var otherView = null;
|
||||
|
||||
if (this.state.viewState === WebViewState.LOADING) {
|
||||
otherView = this.props.renderLoading();
|
||||
otherView = this.props.renderLoading && this.props.renderLoading();
|
||||
} else if (this.state.viewState === WebViewState.ERROR) {
|
||||
var errorEvent = this.state.lastErrorEvent;
|
||||
otherView = this.props.renderError(
|
||||
otherView = this.props.renderError && this.props.renderError(
|
||||
errorEvent.domain,
|
||||
errorEvent.code,
|
||||
errorEvent.description);
|
||||
|
||||
@@ -32,38 +32,6 @@ var invariant = require('invariant');
|
||||
var isEmpty = require('isEmpty');
|
||||
var warning = require('warning');
|
||||
|
||||
/**
|
||||
* ListViewDataSource - Provides efficient data processing and access to the
|
||||
* ListView component. A ListViewDataSource is created with functions for
|
||||
* extracting data from the input blob, and comparing elements (with default
|
||||
* implementations for convenience). The input blob can be as simple as an
|
||||
* array of strings, or an object with rows nested inside section objects.
|
||||
*
|
||||
* To update the data in the datasource, use `cloneWithRows` (or
|
||||
* `cloneWithRowsAndSections` if you care about sections). The data in the
|
||||
* data source is immutable, so you can't modify it directly. The clone methods
|
||||
* suck in the new data and compute a diff for each row so ListView knows
|
||||
* whether to re-render it or not.
|
||||
*
|
||||
* In this example, a component receives data in chunks, handled by
|
||||
* `_onDataArrived`, which concats the new data onto the old data and updates the
|
||||
* data source. We use `concat` to create a new array - mutating `this._data`,
|
||||
* e.g. with `this._data.push(newRowData)`, would be an error. `_rowHasChanged`
|
||||
* understands the shape of the row data and knows how to efficiently compare
|
||||
* it.
|
||||
*
|
||||
* getInitialState: function() {
|
||||
* var ds = new ListViewDataSource({rowHasChanged: this._rowHasChanged});
|
||||
* return {ds};
|
||||
* },
|
||||
* _onDataArrived(newData) {
|
||||
* this._data = this._data.concat(newData);
|
||||
* this.setState({
|
||||
* ds: this.state.ds.cloneWithRows(this._data)
|
||||
* });
|
||||
* }
|
||||
*/
|
||||
|
||||
function defaultGetRowData(
|
||||
dataBlob: any,
|
||||
sectionID: number | string,
|
||||
@@ -88,15 +56,58 @@ type ParamType = {
|
||||
getSectionHeaderData: ?typeof defaultGetSectionHeaderData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides efficient data processing and access to the
|
||||
* `ListView` component. A `ListViewDataSource` is created with functions for
|
||||
* extracting data from the input blob, and comparing elements (with default
|
||||
* implementations for convenience). The input blob can be as simple as an
|
||||
* array of strings, or an object with rows nested inside section objects.
|
||||
*
|
||||
* To update the data in the datasource, use `cloneWithRows` (or
|
||||
* `cloneWithRowsAndSections` if you care about sections). The data in the
|
||||
* data source is immutable, so you can't modify it directly. The clone methods
|
||||
* suck in the new data and compute a diff for each row so ListView knows
|
||||
* whether to re-render it or not.
|
||||
*
|
||||
* In this example, a component receives data in chunks, handled by
|
||||
* `_onDataArrived`, which concats the new data onto the old data and updates the
|
||||
* data source. We use `concat` to create a new array - mutating `this._data`,
|
||||
* e.g. with `this._data.push(newRowData)`, would be an error. `_rowHasChanged`
|
||||
* understands the shape of the row data and knows how to efficiently compare
|
||||
* it.
|
||||
*
|
||||
* ```
|
||||
* getInitialState: function() {
|
||||
* var ds = new ListViewDataSource({rowHasChanged: this._rowHasChanged});
|
||||
* return {ds};
|
||||
* },
|
||||
* _onDataArrived(newData) {
|
||||
* this._data = this._data.concat(newData);
|
||||
* this.setState({
|
||||
* ds: this.state.ds.cloneWithRows(this._data)
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
class ListViewDataSource {
|
||||
|
||||
/**
|
||||
* @param {ParamType} params
|
||||
*
|
||||
* You can provide custom extraction and 'hasChanged' functions for section
|
||||
* You can provide custom extraction and `hasChanged` functions for section
|
||||
* headers and rows. If absent, data will be extracted with the
|
||||
* `defaultGetRowData` and `defaultGetSectionHeaderData` functions.
|
||||
*
|
||||
* The default extractor expects data of one of the following forms:
|
||||
*
|
||||
* { sectionID_1: { rowID_1: <rowData1>, ... }, ... }
|
||||
*
|
||||
* or
|
||||
*
|
||||
* [ [ <rowData1>, <rowData2>, ... ], ... ]
|
||||
*
|
||||
* The constructor takes in a params argument that can contain any of the
|
||||
* following:
|
||||
*
|
||||
* - getRowData(dataBlob, sectionID, rowID);
|
||||
* - getSectionHeaderData(dataBlob, sectionID);
|
||||
* - rowHasChanged(prevRowData, nextRowData);
|
||||
@@ -125,14 +136,25 @@ class ListViewDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} dataBlob -- This is an arbitrary blob of data. An extractor
|
||||
* function was defined at construction time. The default extractor assumes
|
||||
* the data is a plain array or keyed object.
|
||||
*/
|
||||
cloneWithRows(
|
||||
dataBlob: Array<any> | {[key: string]: any},
|
||||
rowIdentities: ?Array<string>
|
||||
): ListViewDataSource {
|
||||
* Clones this `ListViewDataSource` with the specified `dataBlob` and
|
||||
* `rowIdentities`. The `dataBlob` is just an aribitrary blob of data. At
|
||||
* construction an extractor to get the interesting informatoin was defined
|
||||
* (or the default was used).
|
||||
*
|
||||
* The `rowIdentities` is is a 2D array of identifiers for rows.
|
||||
* ie. [['a1', 'a2'], ['b1', 'b2', 'b3'], ...]. If not provided, it's
|
||||
* assumed that the keys of the section data are the row identities.
|
||||
*
|
||||
* Note: This function does NOT clone the data in this data source. It simply
|
||||
* passes the functions defined at construction to a new data source with
|
||||
* the data specified. If you wish to maintain the existing data you must
|
||||
* handle merging of old and new data separately and then pass that into
|
||||
* this function as the `dataBlob`.
|
||||
*/
|
||||
cloneWithRows(
|
||||
dataBlob: Array<any> | {[key: string]: any},
|
||||
rowIdentities: ?Array<string>
|
||||
): ListViewDataSource {
|
||||
var rowIds = rowIdentities ? [rowIdentities] : null;
|
||||
if (!this._sectionHeaderHasChanged) {
|
||||
this._sectionHeaderHasChanged = () => false;
|
||||
@@ -141,29 +163,20 @@ class ListViewDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} dataBlob -- This is an arbitrary blob of data. An extractor
|
||||
* function was defined at construction time. The default extractor assumes
|
||||
* the data is a nested array or keyed object of the form:
|
||||
* This performs the same function as the `cloneWithRows` function but here
|
||||
* you also specify what your `sectionIdentities` are. If you don't care
|
||||
* about sections you should safely be able to use `cloneWithRows`.
|
||||
*
|
||||
* { sectionID_1: { rowID_1: <rowData1>, ... }, ... }
|
||||
*
|
||||
* or
|
||||
*
|
||||
* [ [ <rowData1>, <rowData2>, ... ], ... ]
|
||||
*
|
||||
* @param {array} sectionIdentities -- This is an array of identifiers for
|
||||
* sections. ie. ['s1', 's2', ...]. If not provided, it's assumed that the
|
||||
* keys of dataBlob are the section identities.
|
||||
* @param {array} rowIdentities -- This is a 2D array of identifiers for rows.
|
||||
* ie. [['a1', 'a2'], ['b1', 'b2', 'b3'], ...]. If not provided, it's
|
||||
* assumed that the keys of the section data are the row identities.
|
||||
* `sectionIdentities` is an array of identifiers for sections.
|
||||
* ie. ['s1', 's2', ...]. If not provided, it's assumed that the
|
||||
* keys of dataBlob are the section identities.
|
||||
*
|
||||
* Note: this returns a new object!
|
||||
*/
|
||||
cloneWithRowsAndSections(
|
||||
dataBlob: any,
|
||||
sectionIdentities: ?Array<string>,
|
||||
rowIdentities: ?Array<Array<string>>
|
||||
dataBlob: any,
|
||||
sectionIdentities: ?Array<string>,
|
||||
rowIdentities: ?Array<Array<string>>
|
||||
): ListViewDataSource {
|
||||
invariant(
|
||||
typeof this._sectionHeaderHasChanged === 'function',
|
||||
@@ -205,9 +218,6 @@ class ListViewDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} sectionIndex
|
||||
* @param {number} rowIndex
|
||||
*
|
||||
* Returns if the row is dirtied and needs to be rerendered
|
||||
*/
|
||||
rowShouldUpdate(sectionIndex: number, rowIndex: number): bool {
|
||||
@@ -218,9 +228,6 @@ class ListViewDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} sectionIndex
|
||||
* @param {number} rowIndex
|
||||
*
|
||||
* Gets the data required to render the row.
|
||||
*/
|
||||
getRowData(sectionIndex: number, rowIndex: number): any {
|
||||
@@ -234,8 +241,6 @@ class ListViewDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} index
|
||||
*
|
||||
* Gets the rowID at index provided if the dataSource arrays were flattened,
|
||||
* or null of out of range indexes.
|
||||
*/
|
||||
@@ -252,8 +257,6 @@ class ListViewDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} index
|
||||
*
|
||||
* Gets the sectionID at index provided if the dataSource arrays were flattened,
|
||||
* or null for out of range indexes.
|
||||
*/
|
||||
@@ -281,8 +284,6 @@ class ListViewDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} sectionIndex
|
||||
*
|
||||
* Returns if the section header is dirtied and needs to be rerendered
|
||||
*/
|
||||
sectionHeaderShouldUpdate(sectionIndex: number): bool {
|
||||
@@ -293,8 +294,6 @@ class ListViewDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} sectionIndex
|
||||
*
|
||||
* Gets the data required to render the section header
|
||||
*/
|
||||
getSectionHeaderData(sectionIndex: number): any {
|
||||
|
||||
@@ -27,12 +27,13 @@
|
||||
'use strict';
|
||||
|
||||
var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule;
|
||||
var Backstack = require('Backstack');
|
||||
var Dimensions = require('Dimensions');
|
||||
var BackAndroid = require('BackAndroid');
|
||||
var InteractionMixin = require('InteractionMixin');
|
||||
var NavigatorSceneConfigs = require('NavigatorSceneConfigs');
|
||||
var NavigatorNavigationBar = require('NavigatorNavigationBar');
|
||||
var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
|
||||
var NavigatorInterceptor = require('NavigatorInterceptor');
|
||||
var NavigatorNavigationBar = require('NavigatorNavigationBar');
|
||||
var NavigatorSceneConfigs = require('NavigatorSceneConfigs');
|
||||
var NavigatorStaticContextContainer = require('NavigatorStaticContextContainer');
|
||||
var PanResponder = require('PanResponder');
|
||||
var React = require('React');
|
||||
var StaticContainer = require('StaticContainer.react');
|
||||
@@ -41,6 +42,7 @@ var Subscribable = require('Subscribable');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var View = require('View');
|
||||
|
||||
var getNavigatorContext = require('getNavigatorContext');
|
||||
var clamp = require('clamp');
|
||||
var invariant = require('invariant');
|
||||
var keyMirror = require('keyMirror');
|
||||
@@ -49,20 +51,13 @@ var rebound = require('rebound');
|
||||
|
||||
var PropTypes = React.PropTypes;
|
||||
|
||||
var SCREEN_WIDTH = Dimensions.get('window').width;
|
||||
var SCREEN_HEIGHT = Dimensions.get('window').height;
|
||||
|
||||
var OFF_SCREEN = {style: {opacity: 0}};
|
||||
|
||||
var NAVIGATION_BAR_REF = 'navigationBar_ref';
|
||||
|
||||
var __uid = 0;
|
||||
function getuid() {
|
||||
return __uid++;
|
||||
}
|
||||
|
||||
var nextComponentUid = 0;
|
||||
|
||||
// styles moved to the top of the file so getDefaultProps can refer to it
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
@@ -70,8 +65,11 @@ var styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
},
|
||||
defaultSceneStyle: {
|
||||
width: SCREEN_WIDTH,
|
||||
height: SCREEN_HEIGHT,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
},
|
||||
presentNavItem: {
|
||||
position: 'absolute',
|
||||
@@ -89,11 +87,17 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
transitioner: {
|
||||
flex: 1,
|
||||
backgroundColor: '#555555',
|
||||
backgroundColor: 'transparent',
|
||||
overflow: 'hidden',
|
||||
}
|
||||
});
|
||||
|
||||
var GESTURE_ACTIONS = [
|
||||
'pop',
|
||||
'jumpBack',
|
||||
'jumpForward',
|
||||
];
|
||||
|
||||
/**
|
||||
* Use `Navigator` to transition between different scenes in your app. To
|
||||
* accomplish this, provide route objects to the navigator to identify each
|
||||
@@ -181,12 +185,11 @@ var Navigator = React.createClass({
|
||||
|
||||
/**
|
||||
* Required function which renders the scene for a given route. Will be
|
||||
* invoked with the route, the navigator object, and a ref handler that
|
||||
* will allow a ref to your scene to be provided by props.onItemRef
|
||||
* invoked with the route and the navigator object
|
||||
*
|
||||
* ```
|
||||
* (route, navigator, onRef) =>
|
||||
* <MySceneComponent title={route.title} ref={onRef} />
|
||||
* (route, navigator) =>
|
||||
* <MySceneComponent title={route.title} />
|
||||
* ```
|
||||
*/
|
||||
renderScene: PropTypes.func.isRequired,
|
||||
@@ -238,19 +241,18 @@ var Navigator = React.createClass({
|
||||
* Styles to apply to the container of each scene
|
||||
*/
|
||||
sceneStyle: View.propTypes.style,
|
||||
},
|
||||
|
||||
/**
|
||||
* Should the backstack back button "jump" back instead of pop? Set to true
|
||||
* if a jump forward might happen after the android back button is pressed,
|
||||
* so the scenes will remain mounted
|
||||
*/
|
||||
shouldJumpOnBackstackPop: PropTypes.bool,
|
||||
contextTypes: {
|
||||
navigator: PropTypes.object,
|
||||
},
|
||||
|
||||
statics: {
|
||||
BreadcrumbNavigationBar: NavigatorBreadcrumbNavigationBar,
|
||||
NavigationBar: NavigatorNavigationBar,
|
||||
SceneConfigs: NavigatorSceneConfigs,
|
||||
Interceptor: NavigatorInterceptor,
|
||||
getContext: getNavigatorContext,
|
||||
},
|
||||
|
||||
mixins: [TimerMixin, InteractionMixin, Subscribable.Mixin],
|
||||
@@ -299,7 +301,20 @@ var Navigator = React.createClass({
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.navigatorActions = {
|
||||
this.parentNavigator = getNavigatorContext(this) || this.props.navigator;
|
||||
this.navigatorContext = {
|
||||
setHandlerForRoute: this.setHandlerForRoute,
|
||||
request: this.request,
|
||||
|
||||
parentNavigator: this.parentNavigator,
|
||||
getCurrentRoutes: this.getCurrentRoutes,
|
||||
// We want to bubble focused routes to the top navigation stack. If we
|
||||
// are a child navigator, this allows us to call props.navigator.on*Focus
|
||||
// of the topmost Navigator
|
||||
onWillFocus: this.props.onWillFocus,
|
||||
onDidFocus: this.props.onDidFocus,
|
||||
|
||||
// Legacy, imperitive nav actions. Use request when possible.
|
||||
jumpBack: this.jumpBack,
|
||||
jumpForward: this.jumpForward,
|
||||
jumpTo: this.jumpTo,
|
||||
@@ -313,13 +328,8 @@ var Navigator = React.createClass({
|
||||
resetTo: this.resetTo,
|
||||
popToRoute: this.popToRoute,
|
||||
popToTop: this.popToTop,
|
||||
parentNavigator: this.props.navigator,
|
||||
// We want to bubble focused routes to the top navigation stack. If we
|
||||
// are a child navigator, this allows us to call props.navigator.on*Focus
|
||||
// of the topmost Navigator
|
||||
onWillFocus: this.props.onWillFocus,
|
||||
onDidFocus: this.props.onDidFocus,
|
||||
};
|
||||
this._handlers = {};
|
||||
|
||||
this.panGesture = PanResponder.create({
|
||||
onStartShouldSetPanResponderCapture: this._handleStartShouldSetPanResponderCapture,
|
||||
@@ -331,17 +341,50 @@ var Navigator = React.createClass({
|
||||
});
|
||||
this._itemRefs = {};
|
||||
this._interactionHandle = null;
|
||||
this._backstackComponentKey = 'jsnavstack' + nextComponentUid;
|
||||
nextComponentUid++;
|
||||
|
||||
Backstack.eventEmitter && this.addListenerOn(
|
||||
Backstack.eventEmitter,
|
||||
'popNavigation',
|
||||
this._onBackstackPopState);
|
||||
|
||||
this._emitWillFocus(this.state.presentedIndex);
|
||||
},
|
||||
|
||||
request: function(action, arg1, arg2) {
|
||||
if (this.parentNavigator) {
|
||||
return this.parentNavigator.request.apply(null, arguments);
|
||||
}
|
||||
return this._handleRequest.apply(null, arguments);
|
||||
},
|
||||
|
||||
_handleRequest: function(action, arg1, arg2) {
|
||||
var childHandler = this._handlers[this.state.presentedIndex];
|
||||
if (childHandler && childHandler(action, arg1, arg2)) {
|
||||
return true;
|
||||
}
|
||||
switch (action) {
|
||||
case 'pop':
|
||||
return this._handlePop();
|
||||
case 'push':
|
||||
return this._handlePush(arg1);
|
||||
default:
|
||||
invariant(false, 'Unsupported request type ' + action);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_handlePop: function() {
|
||||
if (this.state.presentedIndex === 0) {
|
||||
return false;
|
||||
}
|
||||
this.pop();
|
||||
return true;
|
||||
},
|
||||
|
||||
_handlePush: function(route) {
|
||||
this.push(route);
|
||||
return true;
|
||||
},
|
||||
|
||||
setHandlerForRoute: function(route, handler) {
|
||||
this._handlers[this.state.routeStack.indexOf(route)] = handler;
|
||||
},
|
||||
|
||||
_configureSpring: function(animationConfig) {
|
||||
var config = this.spring.getSpringConfig();
|
||||
config.friction = animationConfig.springFriction;
|
||||
@@ -356,30 +399,28 @@ var Navigator = React.createClass({
|
||||
animationConfig && this._configureSpring(animationConfig);
|
||||
this.spring.addListener(this);
|
||||
this.onSpringUpdate();
|
||||
|
||||
// Fill up the Backstack with routes that have been provided in
|
||||
// initialRouteStack
|
||||
this._fillBackstackRange(0, this.state.routeStack.indexOf(this.props.initialRoute));
|
||||
this._emitDidFocus(this.state.presentedIndex);
|
||||
if (this.parentNavigator) {
|
||||
this.parentNavigator.setHandler(this._handleRequest);
|
||||
} else {
|
||||
// There is no navigator in our props or context, so this is the
|
||||
// top-level navigator. We will handle back button presses here
|
||||
BackAndroid.addEventListener('hardwareBackPress', this._handleBackPress);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
Backstack.removeComponentHistory(this._backstackComponentKey);
|
||||
if (this.parentNavigator) {
|
||||
this.parentNavigator.setHandler(null);
|
||||
} else {
|
||||
BackAndroid.removeEventListener('hardwareBackPress', this._handleBackPress);
|
||||
}
|
||||
},
|
||||
|
||||
_onBackstackPopState: function(componentKey, stateKey, state) {
|
||||
if (componentKey !== this._backstackComponentKey) {
|
||||
return;
|
||||
}
|
||||
if (!this._canNavigate()) {
|
||||
// A bit hacky: if we can't actually handle the pop, just push it back on the stack
|
||||
Backstack.pushNavigation(componentKey, stateKey, state);
|
||||
} else {
|
||||
if (this.props.shouldJumpOnBackstackPop) {
|
||||
this._jumpToWithoutBackstack(state.fromRoute);
|
||||
} else {
|
||||
this._popToRouteWithoutBackstack(state.fromRoute);
|
||||
}
|
||||
_handleBackPress: function() {
|
||||
var didPop = this.request('pop');
|
||||
if (!didPop) {
|
||||
BackAndroid.exitApp();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -457,15 +498,18 @@ var Navigator = React.createClass({
|
||||
_completeTransition: function() {
|
||||
if (this.spring.getCurrentValue() === 1) {
|
||||
var presentedIndex = this.state.toIndex;
|
||||
this.state.fromIndex = presentedIndex;
|
||||
this.state.presentedIndex = presentedIndex;
|
||||
this.state.fromIndex = presentedIndex;
|
||||
this._emitDidFocus(presentedIndex);
|
||||
this._removePoppedRoutes();
|
||||
if (AnimationsDebugModule) {
|
||||
AnimationsDebugModule.stopRecordingFps();
|
||||
AnimationsDebugModule.stopRecordingFps(Date.now());
|
||||
}
|
||||
this._hideOtherScenes(presentedIndex);
|
||||
} else {
|
||||
this.state.fromIndex = this.state.presentedIndex;
|
||||
this.state.toIndex = this.state.presentedIndex;
|
||||
}
|
||||
this._hideOtherScenes(this.state.presentedIndex);
|
||||
},
|
||||
|
||||
_transitionToToIndexWithVelocity: function(v) {
|
||||
@@ -499,6 +543,10 @@ var Navigator = React.createClass({
|
||||
|
||||
_emitWillFocus: function(index) {
|
||||
var route = this.state.routeStack[index];
|
||||
var navBar = this._navBar;
|
||||
if (navBar && navBar.handleWillFocus) {
|
||||
navBar.handleWillFocus(route);
|
||||
}
|
||||
if (this.props.onWillFocus) {
|
||||
this.props.onWillFocus(route);
|
||||
} else if (this.props.navigator && this.props.navigator.onWillFocus) {
|
||||
@@ -532,95 +580,170 @@ var Navigator = React.createClass({
|
||||
|
||||
_handleMoveShouldSetPanResponder: function(e, gestureState) {
|
||||
var currentRoute = this.state.routeStack[this.state.presentedIndex];
|
||||
var animationConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
if (!animationConfig.enableGestures) {
|
||||
return false;
|
||||
}
|
||||
var currentLoc = animationConfig.isVertical ? gestureState.moveY : gestureState.moveX;
|
||||
var travelDist = animationConfig.isVertical ? gestureState.dy : gestureState.dx;
|
||||
var oppositeAxisTravelDist =
|
||||
animationConfig.isVertical ? gestureState.dx : gestureState.dy;
|
||||
var moveStartedInRegion = currentLoc < animationConfig.edgeHitWidth;
|
||||
var moveTravelledFarEnough =
|
||||
travelDist >= animationConfig.gestureDetectMovement &&
|
||||
travelDist > oppositeAxisTravelDist * animationConfig.directionRatio;
|
||||
return (
|
||||
!this.state.isResponderOnlyToBlockTouches &&
|
||||
moveStartedInRegion &&
|
||||
!this.state.isAnimating &&
|
||||
this.state.presentedIndex > 0 &&
|
||||
moveTravelledFarEnough
|
||||
);
|
||||
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
this._expectingGestureGrant = this._matchGestureAction(sceneConfig.gestures, gestureState);
|
||||
return !! this._expectingGestureGrant;
|
||||
},
|
||||
|
||||
_doesGestureOverswipe: function(gestureName) {
|
||||
var wouldOverswipeBack = this.state.presentedIndex <= 0 &&
|
||||
(gestureName === 'pop' || gestureName === 'jumpBack');
|
||||
var wouldOverswipeForward = this.state.presentedIndex >= this.state.routeStack.length - 1 &&
|
||||
gestureName === 'jumpForward';
|
||||
return wouldOverswipeForward || wouldOverswipeBack;
|
||||
},
|
||||
|
||||
_handlePanResponderGrant: function(e, gestureState) {
|
||||
invariant(
|
||||
this._expectingGestureGrant || this.state.isAnimating,
|
||||
'Responder granted unexpectedly.'
|
||||
);
|
||||
this._activeGestureAction = this._expectingGestureGrant;
|
||||
this._expectingGestureGrant = null;
|
||||
this.state.isResponderOnlyToBlockTouches = this.state.isAnimating;
|
||||
if (!this.state.isAnimating) {
|
||||
this.state.fromIndex = this.state.presentedIndex;
|
||||
this.state.toIndex = this.state.presentedIndex - 1;
|
||||
var gestureSceneDelta = this._deltaForGestureAction(this._activeGestureAction);
|
||||
this.state.toIndex = this.state.presentedIndex + gestureSceneDelta;
|
||||
}
|
||||
},
|
||||
|
||||
_deltaForGestureAction: function(gestureAction) {
|
||||
switch (gestureAction) {
|
||||
case 'pop':
|
||||
case 'jumpBack':
|
||||
return -1;
|
||||
case 'jumpForward':
|
||||
return 1;
|
||||
default:
|
||||
invariant(false, 'Unsupported gesture action ' + gestureAction);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
_handlePanResponderRelease: function(e, gestureState) {
|
||||
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
var releaseGestureAction = this._activeGestureAction;
|
||||
this._activeGestureAction = null;
|
||||
if (this.state.isResponderOnlyToBlockTouches) {
|
||||
this.state.isResponderOnlyToBlockTouches = false;
|
||||
return;
|
||||
}
|
||||
var animationConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
var velocity = animationConfig.isVertical ? gestureState.vy : gestureState.vx;
|
||||
// It's not the real location. There is no *real* location - that's the
|
||||
// point of the pan gesture.
|
||||
var pseudoLocation = animationConfig.isVertical ?
|
||||
gestureState.y0 + gestureState.dy :
|
||||
gestureState.x0 + gestureState.dx;
|
||||
var still = Math.abs(velocity) < animationConfig.notMoving;
|
||||
var releaseGesture = sceneConfig.gestures[releaseGestureAction];
|
||||
if (this.spring.getCurrentValue() === 0) {
|
||||
// The spring is at zero, so the gesture is already complete
|
||||
this.spring.setCurrentValue(0).setAtRest();
|
||||
this._completeTransition();
|
||||
return;
|
||||
}
|
||||
var transitionVelocity =
|
||||
still && animationConfig.pastPointOfNoReturn(pseudoLocation) ? animationConfig.snapVelocity :
|
||||
still && !animationConfig.pastPointOfNoReturn(pseudoLocation) ? -animationConfig.snapVelocity :
|
||||
clamp(-10, velocity, 10); // What are Rebound UoM?
|
||||
|
||||
var isTravelVertical = releaseGesture.direction === 'top-to-bottom' || releaseGesture.direction === 'bottom-to-top';
|
||||
var isTravelInverted = releaseGesture.direction === 'right-to-left' || releaseGesture.direction === 'bottom-to-top';
|
||||
var velocity, gestureDistance;
|
||||
if (isTravelVertical) {
|
||||
velocity = isTravelInverted ? -gestureState.vy : gestureState.vy;
|
||||
gestureDistance = isTravelInverted ? -gestureState.dy : gestureState.dy;
|
||||
} else {
|
||||
velocity = isTravelInverted ? -gestureState.vx : gestureState.vx;
|
||||
gestureDistance = isTravelInverted ? -gestureState.dx : gestureState.dx;
|
||||
}
|
||||
var transitionVelocity = clamp(-10, velocity, 10);
|
||||
if (Math.abs(velocity) < releaseGesture.notMoving) {
|
||||
// The gesture velocity is so slow, is "not moving"
|
||||
var hasGesturedEnoughToComplete = gestureDistance > releaseGesture.fullDistance * releaseGesture.stillCompletionRatio;
|
||||
transitionVelocity = hasGesturedEnoughToComplete ? releaseGesture.snapVelocity : -releaseGesture.snapVelocity;
|
||||
}
|
||||
this.spring.setOvershootClampingEnabled(true);
|
||||
if (transitionVelocity < 0) {
|
||||
if (transitionVelocity < 0 || this._doesGestureOverswipe(releaseGestureAction)) {
|
||||
this._transitionToFromIndexWithVelocity(transitionVelocity);
|
||||
} else {
|
||||
this._manuallyPopBackstack(1);
|
||||
this._transitionToToIndexWithVelocity(transitionVelocity);
|
||||
}
|
||||
},
|
||||
|
||||
_handlePanResponderTerminate: function(e, gestureState) {
|
||||
this._activeGestureAction = null;
|
||||
this.state.isResponderOnlyToBlockTouches = false;
|
||||
this._transitionToFromIndexWithVelocity(0);
|
||||
},
|
||||
|
||||
_handlePanResponderMove: function(e, gestureState) {
|
||||
if (!this.state.isResponderOnlyToBlockTouches) {
|
||||
var animationConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
var distance = animationConfig.isVertical ? gestureState.dy : gestureState.dx;
|
||||
var gestureDetectMovement = animationConfig.gestureDetectMovement;
|
||||
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
var gesture = sceneConfig.gestures[this._activeGestureAction];
|
||||
var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top';
|
||||
var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top';
|
||||
var distance = isTravelVertical ? gestureState.dy : gestureState.dx;
|
||||
distance = isTravelInverted ? - distance : distance;
|
||||
var gestureDetectMovement = gesture.gestureDetectMovement;
|
||||
var nextProgress = (distance - gestureDetectMovement) /
|
||||
(animationConfig.screenDimension - gestureDetectMovement);
|
||||
(gesture.fullDistance - gestureDetectMovement);
|
||||
if (this._doesGestureOverswipe(this._activeGestureAction)) {
|
||||
var frictionConstant = gesture.overswipe.frictionConstant;
|
||||
var frictionByDistance = gesture.overswipe.frictionByDistance;
|
||||
var frictionRatio = 1 / ((frictionConstant) + (Math.abs(nextProgress) * frictionByDistance));
|
||||
nextProgress *= frictionRatio;
|
||||
}
|
||||
this.spring.setCurrentValue(clamp(0, nextProgress, 1));
|
||||
}
|
||||
},
|
||||
|
||||
_matchGestureAction: function(gestures, gestureState) {
|
||||
if (!gestures) {
|
||||
return null;
|
||||
}
|
||||
if (this.state.isResponderOnlyToBlockTouches || this.state.isAnimating) {
|
||||
return null;
|
||||
}
|
||||
var matchedGesture = null;
|
||||
GESTURE_ACTIONS.some((gestureName) => {
|
||||
var gesture = gestures[gestureName];
|
||||
if (!gesture) {
|
||||
return;
|
||||
}
|
||||
if (gesture.overswipe == null && this._doesGestureOverswipe(gestureName)) {
|
||||
// cannot swipe past first or last scene without overswiping
|
||||
return false;
|
||||
}
|
||||
var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top';
|
||||
var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top';
|
||||
var currentLoc = isTravelVertical ? gestureState.moveY : gestureState.moveX;
|
||||
var travelDist = isTravelVertical ? gestureState.dy : gestureState.dx;
|
||||
var oppositeAxisTravelDist =
|
||||
isTravelVertical ? gestureState.dx : gestureState.dy;
|
||||
if (isTravelInverted) {
|
||||
currentLoc = -currentLoc;
|
||||
travelDist = -travelDist;
|
||||
oppositeAxisTravelDist = -oppositeAxisTravelDist;
|
||||
}
|
||||
var moveStartedInRegion = gesture.edgeHitWidth == null ||
|
||||
currentLoc < gesture.edgeHitWidth;
|
||||
var moveTravelledFarEnough =
|
||||
travelDist >= gesture.gestureDetectMovement &&
|
||||
travelDist > oppositeAxisTravelDist * gesture.directionRatio;
|
||||
if (moveStartedInRegion && moveTravelledFarEnough) {
|
||||
matchedGesture = gestureName;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return matchedGesture;
|
||||
},
|
||||
|
||||
_transitionSceneStyle: function(fromIndex, toIndex, progress, index) {
|
||||
var viewAtIndex = this.refs['scene_' + index];
|
||||
if (viewAtIndex === null || viewAtIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
// Use toIndex animation when we move forwards. Use fromIndex when we move back
|
||||
var animationIndex = this.state.presentedIndex < toIndex ? toIndex : fromIndex;
|
||||
var animationConfig = this.state.sceneConfigStack[animationIndex];
|
||||
var sceneConfigIndex = this.state.presentedIndex < toIndex ? toIndex : fromIndex;
|
||||
var sceneConfig = this.state.sceneConfigStack[sceneConfigIndex];
|
||||
// this happens for overswiping when there is no scene at toIndex
|
||||
if (!sceneConfig) {
|
||||
sceneConfig = this.state.sceneConfigStack[sceneConfigIndex - 1];
|
||||
}
|
||||
var styleToUse = {};
|
||||
var useFn = index < fromIndex || index < toIndex ?
|
||||
animationConfig.interpolators.out :
|
||||
animationConfig.interpolators.into;
|
||||
sceneConfig.animationInterpolators.out :
|
||||
sceneConfig.animationInterpolators.into;
|
||||
var directionAdjustedProgress = fromIndex < toIndex ? progress : 1 - progress;
|
||||
var didChange = useFn(styleToUse, directionAdjustedProgress);
|
||||
if (didChange) {
|
||||
@@ -631,7 +754,7 @@ var Navigator = React.createClass({
|
||||
_transitionBetween: function(fromIndex, toIndex, progress) {
|
||||
this._transitionSceneStyle(fromIndex, toIndex, progress, fromIndex);
|
||||
this._transitionSceneStyle(fromIndex, toIndex, progress, toIndex);
|
||||
var navBar = this.refs[NAVIGATION_BAR_REF];
|
||||
var navBar = this._navBar;
|
||||
if (navBar && navBar.updateProgress) {
|
||||
navBar.updateProgress(progress, fromIndex, toIndex);
|
||||
}
|
||||
@@ -650,41 +773,6 @@ var Navigator = React.createClass({
|
||||
return !this.state.isAnimating;
|
||||
},
|
||||
|
||||
_jumpNWithoutBackstack: function(n) {
|
||||
var destIndex = this._getDestIndexWithinBounds(n);
|
||||
if (!this._canNavigate()) {
|
||||
return; // It's busy animating or transitioning.
|
||||
}
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._requestTransitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
this.setState({
|
||||
updatingRangeStart: destIndex,
|
||||
updatingRangeLength: 1,
|
||||
toIndex: destIndex,
|
||||
}, requestTransitionAndResetUpdatingRange);
|
||||
},
|
||||
|
||||
_fillBackstackRange: function(start, end) {
|
||||
invariant(
|
||||
start <= end,
|
||||
'Can only fill the backstack forward. Provide end index greater than start'
|
||||
);
|
||||
for (var i = 0; i < (end - start); i++) {
|
||||
var fromIndex = start + i;
|
||||
var toIndex = start + i + 1;
|
||||
Backstack.pushNavigation(
|
||||
this._backstackComponentKey,
|
||||
toIndex,
|
||||
{
|
||||
fromRoute: this.state.routeStack[fromIndex],
|
||||
toRoute: this.state.routeStack[toIndex],
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_getDestIndexWithinBounds: function(n) {
|
||||
var currentIndex = this.state.presentedIndex;
|
||||
var destIndex = currentIndex + n;
|
||||
@@ -701,20 +789,19 @@ var Navigator = React.createClass({
|
||||
},
|
||||
|
||||
_jumpN: function(n) {
|
||||
var currentIndex = this.state.presentedIndex;
|
||||
var destIndex = this._getDestIndexWithinBounds(n);
|
||||
if (!this._canNavigate()) {
|
||||
return; // It's busy animating or transitioning.
|
||||
}
|
||||
if (n > 0) {
|
||||
this._fillBackstackRange(currentIndex, currentIndex + n);
|
||||
} else {
|
||||
var landingBeforeIndex = currentIndex + n + 1;
|
||||
Backstack.resetToBefore(
|
||||
this._backstackComponentKey,
|
||||
landingBeforeIndex
|
||||
);
|
||||
}
|
||||
this._jumpNWithoutBackstack(n);
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._requestTransitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
this.setState({
|
||||
updatingRangeStart: destIndex,
|
||||
updatingRangeLength: 1,
|
||||
toIndex: destIndex,
|
||||
}, requestTransitionAndResetUpdatingRange);
|
||||
},
|
||||
|
||||
jumpTo: function(route) {
|
||||
@@ -726,15 +813,6 @@ var Navigator = React.createClass({
|
||||
this._jumpN(destIndex - this.state.presentedIndex);
|
||||
},
|
||||
|
||||
_jumpToWithoutBackstack: function(route) {
|
||||
var destIndex = this.state.routeStack.indexOf(route);
|
||||
invariant(
|
||||
destIndex !== -1,
|
||||
'Cannot jump to route that is not in the route stack'
|
||||
);
|
||||
this._jumpNWithoutBackstack(destIndex - this.state.presentedIndex);
|
||||
},
|
||||
|
||||
jumpForward: function() {
|
||||
this._jumpN(1);
|
||||
},
|
||||
@@ -765,11 +843,6 @@ var Navigator = React.createClass({
|
||||
toRoute: route,
|
||||
fromRoute: this.state.routeStack[this.state.routeStack.length - 1],
|
||||
};
|
||||
Backstack.pushNavigation(
|
||||
this._backstackComponentKey,
|
||||
this.state.routeStack.length,
|
||||
navigationState);
|
||||
|
||||
this.setState({
|
||||
idStack: nextIDStack,
|
||||
routeStack: nextStack,
|
||||
@@ -780,14 +853,7 @@ var Navigator = React.createClass({
|
||||
}, requestTransitionAndResetUpdatingRange);
|
||||
},
|
||||
|
||||
_manuallyPopBackstack: function(n) {
|
||||
Backstack.resetToBefore(this._backstackComponentKey, this.state.routeStack.length - n);
|
||||
},
|
||||
|
||||
/**
|
||||
* Like popN, but doesn't also update the Backstack.
|
||||
*/
|
||||
_popNWithoutBackstack: function(n) {
|
||||
popN: function(n) {
|
||||
if (n === 0 || !this._canNavigate()) {
|
||||
return;
|
||||
}
|
||||
@@ -801,17 +867,10 @@ var Navigator = React.createClass({
|
||||
);
|
||||
},
|
||||
|
||||
popN: function(n) {
|
||||
if (n === 0 || !this._canNavigate()) {
|
||||
return;
|
||||
}
|
||||
this._popNWithoutBackstack(n);
|
||||
this._manuallyPopBackstack(n);
|
||||
},
|
||||
|
||||
pop: function() {
|
||||
if (this.props.navigator && this.state.routeStack.length === 1) {
|
||||
return this.props.navigator.pop();
|
||||
// TODO (t6707686): remove this parentNavigator call after transitioning call sites to `.request('pop')`
|
||||
if (this.parentNavigator && this.state.routeStack.length === 1) {
|
||||
return this.parentNavigator.pop();
|
||||
}
|
||||
this.popN(1);
|
||||
},
|
||||
@@ -883,14 +942,6 @@ var Navigator = React.createClass({
|
||||
return this.state.routeStack.length - indexOfRoute - 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Like popToRoute, but doesn't update the Backstack, presumably because it's already up to date.
|
||||
*/
|
||||
_popToRouteWithoutBackstack: function(route) {
|
||||
var numToPop = this._getNumToPopForRoute(route);
|
||||
this._popNWithoutBackstack(numToPop);
|
||||
},
|
||||
|
||||
popToRoute: function(route) {
|
||||
var numToPop = this._getNumToPopForRoute(route);
|
||||
this.popN(numToPop);
|
||||
@@ -912,7 +963,11 @@ var Navigator = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
_onItemRef: function(itemId, ref) {
|
||||
getCurrentRoutes: function() {
|
||||
return this.state.routeStack;
|
||||
},
|
||||
|
||||
_handleItemRef: function(itemId, ref) {
|
||||
this._itemRefs[itemId] = ref;
|
||||
var itemIndex = this.state.idStack.indexOf(itemId);
|
||||
if (itemIndex === -1) {
|
||||
@@ -945,23 +1000,33 @@ var Navigator = React.createClass({
|
||||
this.state.updatingRangeLength !== 0 &&
|
||||
i >= this.state.updatingRangeStart &&
|
||||
i <= this.state.updatingRangeStart + this.state.updatingRangeLength;
|
||||
var sceneNavigatorContext = {
|
||||
...this.navigatorContext,
|
||||
route,
|
||||
setHandler: (handler) => {
|
||||
this.navigatorContext.setHandlerForRoute(route, handler);
|
||||
},
|
||||
};
|
||||
var child = this.props.renderScene(
|
||||
route,
|
||||
this.navigatorActions,
|
||||
this._onItemRef.bind(null, this.state.idStack[i])
|
||||
sceneNavigatorContext
|
||||
);
|
||||
|
||||
var initialSceneStyle =
|
||||
i === this.state.presentedIndex ? styles.presentNavItem : styles.futureNavItem;
|
||||
return (
|
||||
<StaticContainer key={'nav' + i} shouldUpdate={shouldUpdateChild}>
|
||||
<NavigatorStaticContextContainer
|
||||
navigatorContext={sceneNavigatorContext}
|
||||
key={'nav' + i}
|
||||
shouldUpdate={shouldUpdateChild}>
|
||||
<View
|
||||
key={this.state.idStack[i]}
|
||||
ref={'scene_' + i}
|
||||
style={[initialSceneStyle, this.props.sceneStyle]}>
|
||||
{child}
|
||||
{React.cloneElement(child, {
|
||||
ref: this._handleItemRef.bind(null, this.state.idStack[i]),
|
||||
})}
|
||||
</View>
|
||||
</StaticContainer>
|
||||
</NavigatorStaticContextContainer>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -989,8 +1054,8 @@ var Navigator = React.createClass({
|
||||
return null;
|
||||
}
|
||||
return React.cloneElement(this.props.navigationBar, {
|
||||
ref: NAVIGATION_BAR_REF,
|
||||
navigator: this.navigatorActions,
|
||||
ref: (navBar) => { this._navBar = navBar; },
|
||||
navigator: this.navigatorContext,
|
||||
navState: this.state,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -77,7 +77,7 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
||||
popToRoute: PropTypes.func,
|
||||
popToTop: PropTypes.func,
|
||||
}),
|
||||
navigationBarRouteMapper: PropTypes.shape({
|
||||
routeMapper: PropTypes.shape({
|
||||
rightContentForRoute: PropTypes.func,
|
||||
titleContentForRoute: PropTypes.func,
|
||||
iconForRoute: PropTypes.func,
|
||||
@@ -87,7 +87,7 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
||||
idStack: React.PropTypes.arrayOf(React.PropTypes.number),
|
||||
presentedIndex: React.PropTypes.number,
|
||||
}),
|
||||
navigationBarStyles: View.propTypes.style,
|
||||
style: View.propTypes.style,
|
||||
},
|
||||
|
||||
statics: {
|
||||
@@ -144,7 +144,7 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
||||
var titles = navState.routeStack.map(this._renderOrReturnTitle);
|
||||
var buttons = navState.routeStack.map(this._renderOrReturnRightButton);
|
||||
return (
|
||||
<View style={[styles.breadCrumbContainer, this.props.navigationBarStyles]}>
|
||||
<View style={[styles.breadCrumbContainer, this.props.style]}>
|
||||
{titles}
|
||||
{icons}
|
||||
{buttons}
|
||||
@@ -154,7 +154,7 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
||||
|
||||
_renderOrReturnBreadcrumb: function(route, index) {
|
||||
var uid = this.props.navState.idStack[index];
|
||||
var navBarRouteMapper = this.props.navigationBarRouteMapper;
|
||||
var navBarRouteMapper = this.props.routeMapper;
|
||||
var navOps = this.props.navigator;
|
||||
var alreadyRendered = this.refs['crumbContainer' + uid];
|
||||
if (alreadyRendered) {
|
||||
@@ -199,7 +199,7 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
||||
/>
|
||||
);
|
||||
}
|
||||
var navBarRouteMapper = this.props.navigationBarRouteMapper;
|
||||
var navBarRouteMapper = this.props.routeMapper;
|
||||
var titleContent = navBarRouteMapper.titleContentForRoute(
|
||||
navState.routeStack[index],
|
||||
this.props.navigator
|
||||
@@ -219,7 +219,7 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
||||
|
||||
_renderOrReturnRightButton: function(route, index) {
|
||||
var navState = this.props.navState;
|
||||
var navBarRouteMapper = this.props.navigationBarRouteMapper;
|
||||
var navBarRouteMapper = this.props.routeMapper;
|
||||
var uid = navState.idStack[index];
|
||||
var alreadyRendered = this.refs['rightContainer' + uid];
|
||||
if (alreadyRendered) {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* Facebook, Inc. (“Facebook”) owns all right, title and interest, including
|
||||
* all intellectual property and other proprietary rights, in and to the React
|
||||
* Native CustomComponents software (the “Software”). Subject to your
|
||||
* compliance with these terms, you are hereby granted a non-exclusive,
|
||||
* worldwide, royalty-free copyright license to (1) use and copy the Software;
|
||||
* and (2) reproduce and distribute the Software as part of your own software
|
||||
* (“Your Software”). Facebook reserves all rights not expressly granted to
|
||||
* you in this license agreement.
|
||||
*
|
||||
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
|
||||
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @providesModule NavigatorInterceptor
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var getNavigatorContext = require('getNavigatorContext');
|
||||
|
||||
var NavigatorInterceptor = React.createClass({
|
||||
|
||||
contextTypes: {
|
||||
navigator: React.PropTypes.object,
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.navigator = getNavigatorContext(this);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.navigator.setHandler(this._navigatorHandleRequest);
|
||||
},
|
||||
|
||||
childContextTypes: {
|
||||
navigator: React.PropTypes.object,
|
||||
},
|
||||
|
||||
getChildContext: function() {
|
||||
return {
|
||||
navigator: {
|
||||
...this.navigator,
|
||||
setHandler: (handler) => {
|
||||
this._childNavigationHandler = handler;
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.navigator.setHandler(null);
|
||||
},
|
||||
|
||||
_navigatorHandleRequest: function(action, arg1, arg2) {
|
||||
if (this._interceptorHandle(action, arg1, arg2)) {
|
||||
return true;
|
||||
}
|
||||
if (this._childNavigationHandler && this._childNavigationHandler(action, arg1, arg2)) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_interceptorHandle: function(action, arg1, arg2) {
|
||||
if (this.props.onRequest && this.props.onRequest(action, arg1, arg2)) {
|
||||
return true;
|
||||
}
|
||||
switch (action) {
|
||||
case 'pop':
|
||||
return this.props.onPopRequest && this.props.onPopRequest(action, arg1, arg2);
|
||||
case 'push':
|
||||
return this.props.onPushRequest && this.props.onPushRequest(action, arg1, arg2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return this.props.children;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
module.exports = NavigatorInterceptor;
|
||||
@@ -46,7 +46,7 @@ var NavigatorNavigationBar = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
navigator: React.PropTypes.object,
|
||||
navigationBarRouteMapper: React.PropTypes.shape({
|
||||
routeMapper: React.PropTypes.shape({
|
||||
Title: React.PropTypes.func.isRequired,
|
||||
LeftButton: React.PropTypes.func.isRequired,
|
||||
RightButton: React.PropTypes.func.isRequired,
|
||||
@@ -56,7 +56,7 @@ var NavigatorNavigationBar = React.createClass({
|
||||
idStack: React.PropTypes.arrayOf(React.PropTypes.number),
|
||||
presentedIndex: React.PropTypes.number,
|
||||
}),
|
||||
navigationBarStyles: View.propTypes.style,
|
||||
style: View.propTypes.style,
|
||||
},
|
||||
|
||||
statics: {
|
||||
@@ -133,7 +133,7 @@ var NavigatorNavigationBar = React.createClass({
|
||||
}, this);
|
||||
|
||||
return (
|
||||
<View style={[styles.navBarContainer, this.props.navigationBarStyles]}>
|
||||
<View style={[styles.navBarContainer, this.props.style]}>
|
||||
{components}
|
||||
</View>
|
||||
);
|
||||
@@ -145,7 +145,6 @@ var NavigatorNavigationBar = React.createClass({
|
||||
/*number*/index
|
||||
) /*object*/ {
|
||||
var navState = this.props.navState;
|
||||
var navBarRouteMapper = this.props.navigationBarRouteMapper;
|
||||
var uid = navState.idStack[index];
|
||||
var containerRef = componentName + 'Container' + uid;
|
||||
var alreadyRendered = this.refs[containerRef];
|
||||
@@ -160,7 +159,7 @@ var NavigatorNavigationBar = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
var content = navBarRouteMapper[componentName](
|
||||
var content = this.props.routeMapper[componentName](
|
||||
navState.routeStack[index],
|
||||
this.props.navigator,
|
||||
index,
|
||||
|
||||
@@ -30,12 +30,11 @@ var Dimensions = require('Dimensions');
|
||||
var PixelRatio = require('PixelRatio');
|
||||
|
||||
var buildStyleInterpolator = require('buildStyleInterpolator');
|
||||
var merge = require('merge');
|
||||
|
||||
var SCREEN_WIDTH = Dimensions.get('window').width;
|
||||
var SCREEN_HEIGHT = Dimensions.get('window').height;
|
||||
|
||||
var ToTheLeft = {
|
||||
var FadeToTheLeft = {
|
||||
// Rotate *requires* you to break out each individual component of
|
||||
// rotation (x, y, z, w)
|
||||
transformTranslate: {
|
||||
@@ -102,6 +101,23 @@ var ToTheLeft = {
|
||||
},
|
||||
};
|
||||
|
||||
var ToTheLeft = {
|
||||
transformTranslate: {
|
||||
from: {x: 0, y: 0, z: 0},
|
||||
to: {x: -Dimensions.get('window').width, y: 0, z: 0},
|
||||
min: 0,
|
||||
max: 1,
|
||||
type: 'linear',
|
||||
extrapolate: true,
|
||||
round: PixelRatio.get(),
|
||||
},
|
||||
opacity: {
|
||||
value: 1.0,
|
||||
type: 'constant',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
var FromTheRight = {
|
||||
opacity: {
|
||||
value: 1.0,
|
||||
@@ -220,32 +236,12 @@ var FromTheFront = {
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
var Interpolators = {
|
||||
Vertical: {
|
||||
into: buildStyleInterpolator(FromTheFront),
|
||||
out: buildStyleInterpolator(ToTheBack),
|
||||
},
|
||||
Horizontal: {
|
||||
into: buildStyleInterpolator(FromTheRight),
|
||||
out: buildStyleInterpolator(ToTheLeft),
|
||||
},
|
||||
var BaseOverswipeConfig = {
|
||||
frictionConstant: 1,
|
||||
frictionByDistance: 1.5,
|
||||
};
|
||||
|
||||
|
||||
// These are meant to mimic iOS default behavior
|
||||
var PastPointOfNoReturn = {
|
||||
horizontal: function(location) {
|
||||
return location > SCREEN_WIDTH * 3 / 5;
|
||||
},
|
||||
vertical: function(location) {
|
||||
return location > SCREEN_HEIGHT * 3 / 5;
|
||||
},
|
||||
};
|
||||
|
||||
var BaseConfig = {
|
||||
// When false, all gestures are ignored for this scene
|
||||
enableGestures: true,
|
||||
var BaseLeftToRightGesture = {
|
||||
|
||||
// How far the swipe must drag to start transitioning
|
||||
gestureDetectMovement: 2,
|
||||
@@ -253,48 +249,92 @@ var BaseConfig = {
|
||||
// Amplitude of release velocity that is considered still
|
||||
notMoving: 0.3,
|
||||
|
||||
// Velocity to start at when transitioning without gesture
|
||||
defaultTransitionVelocity: 1.5,
|
||||
|
||||
// Fraction of directional move required.
|
||||
directionRatio: 0.66,
|
||||
|
||||
// Velocity to transition with when the gesture release was "not moving"
|
||||
snapVelocity: 2,
|
||||
|
||||
// Region that can trigger swipe. iOS default is 30px from the left edge
|
||||
edgeHitWidth: 30,
|
||||
|
||||
// Ratio of gesture completion when non-velocity release will cause action
|
||||
stillCompletionRatio: 3 / 5,
|
||||
|
||||
fullDistance: SCREEN_WIDTH,
|
||||
|
||||
direction: 'left-to-right',
|
||||
|
||||
};
|
||||
|
||||
var BaseRightToLeftGesture = {
|
||||
...BaseLeftToRightGesture,
|
||||
direction: 'right-to-left',
|
||||
};
|
||||
|
||||
var BaseConfig = {
|
||||
// A list of all gestures that are enabled on this scene
|
||||
gestures: {
|
||||
pop: BaseLeftToRightGesture,
|
||||
},
|
||||
|
||||
// Rebound spring parameters when transitioning FROM this scene
|
||||
springFriction: 26,
|
||||
springTension: 200,
|
||||
|
||||
// Defaults for horizontal transitioning:
|
||||
// Velocity to start at when transitioning without gesture
|
||||
defaultTransitionVelocity: 1.5,
|
||||
|
||||
isVertical: false,
|
||||
screenDimension: SCREEN_WIDTH,
|
||||
|
||||
// Region that can trigger swipe. iOS default is 30px from the left edge
|
||||
edgeHitWidth: 30,
|
||||
|
||||
// Point at which a non-velocity release will cause nav pop
|
||||
pastPointOfNoReturn: PastPointOfNoReturn.horizontal,
|
||||
|
||||
// Animation interpolators for this transition
|
||||
interpolators: Interpolators.Horizontal,
|
||||
// Animation interpolators for horizontal transitioning:
|
||||
animationInterpolators: {
|
||||
into: buildStyleInterpolator(FromTheRight),
|
||||
out: buildStyleInterpolator(FadeToTheLeft),
|
||||
},
|
||||
};
|
||||
|
||||
var NavigatorSceneConfigs = {
|
||||
PushFromRight: merge(BaseConfig, {
|
||||
PushFromRight: {
|
||||
...BaseConfig,
|
||||
// We will want to customize this soon
|
||||
}),
|
||||
FloatFromRight: merge(BaseConfig, {
|
||||
},
|
||||
FloatFromRight: {
|
||||
...BaseConfig,
|
||||
// We will want to customize this soon
|
||||
}),
|
||||
FloatFromBottom: merge(BaseConfig, {
|
||||
edgeHitWidth: 150,
|
||||
interpolators: Interpolators.Vertical,
|
||||
isVertical: true,
|
||||
pastPointOfNoReturn: PastPointOfNoReturn.vertical,
|
||||
screenDimension: SCREEN_HEIGHT,
|
||||
}),
|
||||
},
|
||||
FloatFromBottom: {
|
||||
...BaseConfig,
|
||||
gestures: {
|
||||
pop: {
|
||||
...BaseLeftToRightGesture,
|
||||
edgeHitWidth: 150,
|
||||
direction: 'top-to-bottom',
|
||||
fullDistance: SCREEN_HEIGHT,
|
||||
}
|
||||
},
|
||||
animationInterpolators: {
|
||||
into: buildStyleInterpolator(FromTheFront),
|
||||
out: buildStyleInterpolator(ToTheBack),
|
||||
},
|
||||
},
|
||||
HorizontalSwipeJump: {
|
||||
...BaseConfig,
|
||||
gestures: {
|
||||
jumpBack: {
|
||||
...BaseLeftToRightGesture,
|
||||
overswipe: BaseOverswipeConfig,
|
||||
edgeHitWidth: null,
|
||||
},
|
||||
jumpForward: {
|
||||
...BaseRightToLeftGesture,
|
||||
overswipe: BaseOverswipeConfig,
|
||||
edgeHitWidth: null,
|
||||
},
|
||||
},
|
||||
animationInterpolators: {
|
||||
into: buildStyleInterpolator(FromTheRight),
|
||||
out: buildStyleInterpolator(ToTheLeft),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = NavigatorSceneConfigs;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* Facebook, Inc. (“Facebook”) owns all right, title and interest, including
|
||||
* all intellectual property and other proprietary rights, in and to the React
|
||||
* Native CustomComponents software (the “Software”). Subject to your
|
||||
* compliance with these terms, you are hereby granted a non-exclusive,
|
||||
* worldwide, royalty-free copyright license to (1) use and copy the Software;
|
||||
* and (2) reproduce and distribute the Software as part of your own software
|
||||
* (“Your Software”). Facebook reserves all rights not expressly granted to
|
||||
* you in this license agreement.
|
||||
*
|
||||
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
|
||||
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @providesModule NavigatorStaticContextContainer
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var StaticContainer = require('StaticContainer.react');
|
||||
|
||||
var PropTypes = React.PropTypes;
|
||||
|
||||
var NavigatorStaticContextContainer = React.createClass({
|
||||
|
||||
childContextTypes: {
|
||||
navigator: PropTypes.object,
|
||||
},
|
||||
|
||||
getChildContext: function() {
|
||||
return {
|
||||
navigator: this.props.navigatorContext,
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<StaticContainer {...this.props} />
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = NavigatorStaticContextContainer;
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* Facebook, Inc. (“Facebook”) owns all right, title and interest, including
|
||||
* all intellectual property and other proprietary rights, in and to the React
|
||||
* Native CustomComponents software (the “Software”). Subject to your
|
||||
* compliance with these terms, you are hereby granted a non-exclusive,
|
||||
* worldwide, royalty-free copyright license to (1) use and copy the Software;
|
||||
* and (2) reproduce and distribute the Software as part of your own software
|
||||
* (“Your Software”). Facebook reserves all rights not expressly granted to
|
||||
* you in this license agreement.
|
||||
*
|
||||
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
|
||||
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @providesModule getNavigatorContext
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
|
||||
function getNavigatorContext(el) {
|
||||
// TODO (t6707746): replace with `el.context.navigator` when parent context is supported
|
||||
return ReactInstanceMap.get(el)._context.navigator;
|
||||
}
|
||||
|
||||
module.exports = getNavigatorContext;
|
||||
@@ -23,11 +23,9 @@ var subscriptions = [];
|
||||
var updatesEnabled = false;
|
||||
|
||||
/**
|
||||
* /!\ ATTENTION /!\
|
||||
* You need to add NSLocationWhenInUseUsageDescription key
|
||||
* in Info.plist to enable geolocation, otherwise it's going
|
||||
* to *fail silently*!
|
||||
* \!/ \!/
|
||||
* You need to include the `NSLocationWhenInUseUsageDescription` key
|
||||
* in Info.plist to enable geolocation. Geolocation is enabled by default
|
||||
* when you create a project with `react-native init`.
|
||||
*
|
||||
* Geolocation follows the MDN specification:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
|
||||
|
||||
@@ -54,7 +54,9 @@
|
||||
134814051AA4E45400B7C361 /* RCTLocationObserver.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
|
||||
@@ -99,6 +99,8 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
|
||||
RCTLocationOptions _observerOptions;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
@@ -151,9 +153,9 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
|
||||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (void)startObserving:(NSDictionary *)optionsJSON
|
||||
RCT_EXPORT_METHOD(startObserving:(NSDictionary *)optionsJSON)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
[self checkLocationConfig];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
@@ -170,10 +172,8 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
|
||||
});
|
||||
}
|
||||
|
||||
- (void)stopObserving
|
||||
RCT_EXPORT_METHOD(stopObserving)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
// Stop observing
|
||||
@@ -187,11 +187,11 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
|
||||
});
|
||||
}
|
||||
|
||||
- (void)getCurrentPosition:(NSDictionary *)optionsJSON
|
||||
withSuccessCallback:(RCTResponseSenderBlock)successBlock
|
||||
errorCallback:(RCTResponseSenderBlock)errorBlock
|
||||
RCT_EXPORT_METHOD(getCurrentPosition:(NSDictionary *)optionsJSON
|
||||
withSuccessCallback:(RCTResponseSenderBlock)successBlock
|
||||
errorCallback:(RCTResponseSenderBlock)errorBlock)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
[self checkLocationConfig];
|
||||
|
||||
if (!successBlock) {
|
||||
RCTLogError(@"%@.getCurrentPosition called with nil success parameter.", [self class]);
|
||||
@@ -323,4 +323,11 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
|
||||
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
|
||||
}
|
||||
|
||||
- (void)checkLocationConfig
|
||||
{
|
||||
if (![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) {
|
||||
RCTLogError(@"NSLocationWhenInUseUsageDescription key must be present in Info.plist to use geolocation.");
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -56,35 +56,33 @@ var warning = require('warning');
|
||||
|
||||
var Image = React.createClass({
|
||||
propTypes: {
|
||||
/**
|
||||
* `uri` is a string representing the resource identifier for the image, which
|
||||
* could be an http address, a local file path, or the name of a static image
|
||||
* resource (which should be wrapped in the `required('image!name')` function).
|
||||
*/
|
||||
source: PropTypes.shape({
|
||||
/**
|
||||
* A string representing the resource identifier for the image, which
|
||||
* could be an http address, a local file path, or the name of a static image
|
||||
* resource (which should be wrapped in the `ix` function).
|
||||
*/
|
||||
uri: PropTypes.string,
|
||||
}),
|
||||
/**
|
||||
* accessible - Whether this element should be revealed as an accessible
|
||||
* element.
|
||||
* Whether this element should be revealed as an accessible element.
|
||||
*/
|
||||
accessible: PropTypes.bool,
|
||||
/**
|
||||
* accessibilityLabel - Custom string to display for accessibility.
|
||||
* Custom string to display for accessibility.
|
||||
*/
|
||||
accessibilityLabel: PropTypes.string,
|
||||
/**
|
||||
* capInsets - When the image is resized, the corners of the size specified
|
||||
* When the image is resized, the corners of the size specified
|
||||
* by capInsets will stay a fixed size, but the center content and borders
|
||||
* of the image will be stretched. This is useful for creating resizable
|
||||
* rounded buttons, shadows, and other resizable assets. More info:
|
||||
*
|
||||
* https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/index.html#//apple_ref/occ/instm/UIImage/resizableImageWithCapInsets:
|
||||
* rounded buttons, shadows, and other resizable assets. More info on
|
||||
* [Apple documentation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/index.html#//apple_ref/occ/instm/UIImage/resizableImageWithCapInsets)
|
||||
*/
|
||||
capInsets: EdgeInsetsPropType,
|
||||
style: StyleSheetPropType(ImageStylePropTypes),
|
||||
/**
|
||||
* testID - A unique identifier for this element to be used in UI Automation
|
||||
* A unique identifier for this element to be used in UI Automation
|
||||
* testing scripts.
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
|
||||
+7
-4
@@ -5,10 +5,13 @@
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ImageSource
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var REQUIRE_RE = /\brequire\s*?\(\s*?([\'"])([^"\']+)\1\s*?\)/g;
|
||||
|
||||
module.exports = REQUIRE_RE;
|
||||
type ImageSource = {
|
||||
uri: string;
|
||||
isStatic: boolean;
|
||||
};
|
||||
@@ -14,15 +14,17 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTImageLoader.h"
|
||||
#import "RCTImageLoader.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@implementation RCTCameraRollManager
|
||||
|
||||
- (void)saveImageWithTag:(NSString *)imageTag successCallback:(RCTResponseSenderBlock)successCallback errorCallback:(RCTResponseSenderBlock)errorCallback
|
||||
{
|
||||
RCT_EXPORT();
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
RCT_EXPORT_METHOD(saveImageWithTag:(NSString *)imageTag
|
||||
successCallback:(RCTResponseSenderBlock)successCallback
|
||||
errorCallback:(RCTResponseSenderBlock)errorCallback)
|
||||
{
|
||||
[RCTImageLoader loadImageWithTag:imageTag callback:^(NSError *loadError, UIImage *loadedImage) {
|
||||
if (loadError) {
|
||||
errorCallback(@[[loadError localizedDescription]]);
|
||||
@@ -59,10 +61,10 @@
|
||||
}]);
|
||||
}
|
||||
|
||||
- (void)getPhotos:(NSDictionary *)params callback:(RCTResponseSenderBlock)callback errorCallback:(RCTResponseSenderBlock)errorCallback
|
||||
RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
||||
callback:(RCTResponseSenderBlock)callback
|
||||
errorCallback:(RCTResponseSenderBlock)errorCallback)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
NSUInteger first = [params[@"first"] integerValue];
|
||||
NSString *afterCursor = params[@"after"];
|
||||
NSString *groupTypesStr = params[@"groupTypes"];
|
||||
|
||||
@@ -81,7 +81,9 @@
|
||||
1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */,
|
||||
58B5115E1A9E6B3D00147676 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
58B5115E1A9E6B3D00147676 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -77,13 +77,14 @@ static NSString *RCTCacheKeyForURL(NSURL *url)
|
||||
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTCachedDataDownloadBlock runBlocks = ^(BOOL cached, NSData *data, NSError *error) {
|
||||
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSArray *blocks = strongSelf->_pendingBlocks[cacheKey];
|
||||
[strongSelf->_pendingBlocks removeObjectForKey:cacheKey];
|
||||
for (RCTCachedDataDownloadBlock block in blocks) {
|
||||
block(cached, data, error);
|
||||
}
|
||||
dispatch_async(_processingQueue, ^{
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSArray *blocks = strongSelf->_pendingBlocks[cacheKey];
|
||||
[strongSelf->_pendingBlocks removeObjectForKey:cacheKey];
|
||||
for (RCTCachedDataDownloadBlock block in blocks) {
|
||||
block(cached, data, error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if ([_cache hasDataForKey:cacheKey]) {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
@implementation RCTNetworkImageViewManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
RCTNetworkImageView *view = [[RCTNetworkImageView alloc] initWithFrame:CGRectZero imageDownloader:[RCTImageDownloader sharedInstance]];
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
@implementation RCTStaticImageManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTStaticImage alloc] init];
|
||||
|
||||
@@ -36,12 +36,12 @@ function handleException(e: Exception) {
|
||||
);
|
||||
|
||||
if (RCTExceptionsManager) {
|
||||
RCTExceptionsManager.reportUnhandledException(e.message, format(stack));
|
||||
RCTExceptionsManager.reportUnhandledException(e.message, stack);
|
||||
if (__DEV__) {
|
||||
(sourceMapPromise = sourceMapPromise || loadSourceMap())
|
||||
.then(map => {
|
||||
var prettyStack = parseErrorStack(e, map);
|
||||
RCTExceptionsManager.updateExceptionMessage(e.message, format(prettyStack));
|
||||
RCTExceptionsManager.updateExceptionMessage(e.message, prettyStack);
|
||||
})
|
||||
.then(null, error => {
|
||||
console.error('#CLOWNTOWN (error while displaying error): ' + error.message);
|
||||
@@ -71,13 +71,4 @@ function fillSpaces(n) {
|
||||
return new Array(n + 1).join(' ');
|
||||
}
|
||||
|
||||
// HACK(frantic) Android currently expects stack trace to be a string #5920439
|
||||
function format(stack) {
|
||||
if (Platform.OS === 'android') {
|
||||
return stackToString(stack);
|
||||
} else {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { handleException };
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
@interface RCTLinkingManager : NSObject <RCTBridgeModule>
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)URL
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation;
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,6 +18,8 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification";
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
@@ -35,11 +37,11 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification";
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
openURL:(NSURL *)URL
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation
|
||||
{
|
||||
NSDictionary *payload = @{@"url": [url absoluteString]};
|
||||
NSDictionary *payload = @{@"url": [URL absoluteString]};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification
|
||||
object:self
|
||||
userInfo:payload];
|
||||
@@ -52,27 +54,22 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification";
|
||||
body:[notification userInfo]];
|
||||
}
|
||||
|
||||
- (void)openURL:(NSString *)url
|
||||
RCT_EXPORT_METHOD(openURL:(NSURL *)URL)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
|
||||
[[UIApplication sharedApplication] openURL:URL];
|
||||
}
|
||||
|
||||
- (void)canOpenURL:(NSString *)url
|
||||
callback:(RCTResponseSenderBlock)callback
|
||||
RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
BOOL supported = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]];
|
||||
callback(@[@(supported)]);
|
||||
BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:URL];
|
||||
callback(@[@(canOpen)]);
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return @{
|
||||
@"initialURL": [[_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsURLKey] absoluteString] ?: [NSNull null]
|
||||
};
|
||||
NSURL *initialURL = _bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
return @{@"initialURL": [initialURL absoluteString] ?: [NSNull null]};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,39 +10,31 @@
|
||||
#import "RCTDataManager.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTDataManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
/**
|
||||
* Executes a network request.
|
||||
* The responseSender block won't be called on same thread as called.
|
||||
*/
|
||||
- (void)executeQuery:(NSString *)queryType
|
||||
query:(id)query
|
||||
queryHash:(__unused NSString *)queryHash
|
||||
responseSender:(RCTResponseSenderBlock)responseSender
|
||||
RCT_EXPORT_METHOD(queryData:(NSString *)queryType
|
||||
withQuery:(NSDictionary *)query
|
||||
queryHash:(__unused NSString *)queryHash
|
||||
responseSender:(RCTResponseSenderBlock)responseSender)
|
||||
{
|
||||
RCT_EXPORT(queryData);
|
||||
|
||||
if ([queryType isEqualToString:@"http"]) {
|
||||
|
||||
// Parse query
|
||||
NSDictionary *queryDict = query;
|
||||
if ([query isKindOfClass:[NSString class]]) {
|
||||
// TODO: it would be more efficient just to send a dictionary
|
||||
queryDict = RCTJSONParse(query, NULL);
|
||||
}
|
||||
|
||||
// Build request
|
||||
NSURL *url = [NSURL URLWithString:queryDict[@"url"]];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
request.HTTPMethod = queryDict[@"method"] ?: @"GET";
|
||||
request.allHTTPHeaderFields = queryDict[@"headers"];
|
||||
if ([queryDict[@"data"] isKindOfClass:[NSString class]]) {
|
||||
request.HTTPBody = [queryDict[@"data"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
NSURL *URL = [RCTConvert NSURL:query[@"url"]];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
|
||||
request.HTTPMethod = [RCTConvert NSString:query[@"method"]] ?: @"GET";
|
||||
request.allHTTPHeaderFields = [RCTConvert NSDictionary:query[@"headers"]];
|
||||
request.HTTPBody = [RCTConvert NSData:query[@"data"]];
|
||||
|
||||
// Build data task
|
||||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *connectionError) {
|
||||
@@ -50,18 +42,27 @@
|
||||
// Build response
|
||||
NSDictionary *responseJSON;
|
||||
if (connectionError == nil) {
|
||||
NSStringEncoding encoding;
|
||||
NSStringEncoding encoding = NSUTF8StringEncoding;
|
||||
if (response.textEncodingName) {
|
||||
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
} else {
|
||||
encoding = NSUTF8StringEncoding;
|
||||
}
|
||||
int responseCode = (int)[((NSHTTPURLResponse *)response) statusCode];
|
||||
NSString *returnData = [[NSString alloc] initWithData:data encoding:encoding];
|
||||
responseJSON = @{@"status": @(responseCode), @"responseText": returnData};
|
||||
NSHTTPURLResponse *httpResponse = nil;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
// Might be a local file request
|
||||
httpResponse = (NSHTTPURLResponse *)response;
|
||||
}
|
||||
responseJSON = @{
|
||||
@"status": @([httpResponse statusCode] ?: 200),
|
||||
@"responseHeaders": [httpResponse allHeaderFields] ?: @{},
|
||||
@"responseText": [[NSString alloc] initWithData:data encoding:encoding] ?: @""
|
||||
};
|
||||
} else {
|
||||
responseJSON = @{@"status": @0, @"responseText": [connectionError localizedDescription]};
|
||||
responseJSON = @{
|
||||
@"status": @0,
|
||||
@"responseHeaders": @{},
|
||||
@"responseText": [connectionError localizedDescription]
|
||||
};
|
||||
}
|
||||
|
||||
// Send response (won't be sent on same thread as caller)
|
||||
|
||||
@@ -51,7 +51,9 @@
|
||||
1372B7361AB03E7B00659ED6 /* RCTReachability.m */,
|
||||
58B511DC1A9E6C8500147676 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
58B511DC1A9E6C8500147676 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -53,6 +53,8 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithHost:(NSString *)host
|
||||
@@ -81,11 +83,9 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC
|
||||
#pragma mark - Public API
|
||||
|
||||
// TODO: remove error callback - not needed except by Subscribable interface
|
||||
- (void)getCurrentReachability:(RCTResponseSenderBlock)getSuccess
|
||||
withErrorCallback:(__unused RCTResponseSenderBlock)getError
|
||||
RCT_EXPORT_METHOD(getCurrentReachability:(RCTResponseSenderBlock)getSuccess
|
||||
withErrorCallback:(__unused RCTResponseSenderBlock)getError)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
getSuccess(@[@{@"network_reachability": _status}]);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,13 @@ class XMLHttpRequest extends XMLHttpRequestBase {
|
||||
sendImpl(method: ?string, url: ?string, headers: Object, data: any): void {
|
||||
RCTDataManager.queryData(
|
||||
'http',
|
||||
JSON.stringify({
|
||||
{
|
||||
method: method,
|
||||
url: url,
|
||||
data: data,
|
||||
headers: headers,
|
||||
}),
|
||||
},
|
||||
// TODO: Do we need this? is it used anywhere?
|
||||
'h' + crc32(method + '|' + url + '|' + data),
|
||||
(result) => {
|
||||
result = JSON.parse(result);
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule Portal
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
||||
var _portalRef: any;
|
||||
|
||||
/*
|
||||
* A container that renders all the modals on top of everything else in the application.
|
||||
*
|
||||
* Portal makes it possible for application code to pass modal views all the way up to
|
||||
* the root element created in `renderApplication`.
|
||||
*
|
||||
* Never use `<Portal>` in your code. There is only one Portal instance rendered
|
||||
* by the top-level `renderApplication`.
|
||||
*/
|
||||
var Portal = React.createClass({
|
||||
statics: {
|
||||
showModal: function(component) {
|
||||
if (!_portalRef) {
|
||||
console.error('Calling showModal but no Portal has been rendered');
|
||||
return;
|
||||
}
|
||||
_portalRef.setState({modal: component});
|
||||
},
|
||||
|
||||
closeModal: function() {
|
||||
if (!_portalRef) {
|
||||
console.error('Calling closeModal but no Portal has been rendered');
|
||||
return;
|
||||
}
|
||||
_portalRef.setState({modal: null});
|
||||
},
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {modal: (null: any)};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
_portalRef = this;
|
||||
if (!this.state.modal) {
|
||||
return <View />;
|
||||
}
|
||||
return (
|
||||
<View style={styles.modalsContainer}>
|
||||
{this.state.modal}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
modalsContainer: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Portal;
|
||||
@@ -54,7 +54,9 @@
|
||||
148699CE1ABD045300480536 /* RCTPushNotificationManager.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
|
||||
NSDictionary *_initialNotification;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (instancetype)init
|
||||
@@ -66,29 +68,23 @@ NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
|
||||
/**
|
||||
* Update the application icon badge number on the home screen
|
||||
*/
|
||||
+ (void)setApplicationIconBadgeNumber:(NSInteger)number
|
||||
RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(NSInteger)number)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
[UIApplication sharedApplication].applicationIconBadgeNumber = number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current application icon badge number on the home screen
|
||||
*/
|
||||
+ (void)getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback
|
||||
RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
callback(@[
|
||||
@([UIApplication sharedApplication].applicationIconBadgeNumber)
|
||||
]);
|
||||
}
|
||||
|
||||
+ (void)requestPermissions
|
||||
RCT_EXPORT_METHOD(requestPermissions)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
|
||||
|
||||
// if we are targeting iOS 7, *and* the new UIUserNotificationSettings
|
||||
@@ -104,13 +100,10 @@ NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
|
||||
UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert;
|
||||
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
|
||||
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
|
||||
|
||||
}
|
||||
|
||||
+ (void)checkPermissions:(RCTResponseSenderBlock)callback
|
||||
RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
NSMutableDictionary *permissions = [[NSMutableDictionary alloc] init];
|
||||
|
||||
UIUserNotificationType types = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];
|
||||
|
||||
@@ -65,7 +65,9 @@
|
||||
58E64FE31AB964CD007446E2 /* FBSnapshotTestCase */,
|
||||
580C37701AB104AF0015E709 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
580C37701AB104AF0015E709 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
// This is used to give meaningful names to snapshot image files.
|
||||
@property (nonatomic, assign) SEL testSelector;
|
||||
|
||||
@property (nonatomic, weak) UIView *view;
|
||||
|
||||
- (instancetype)initWithSnapshotController:(FBSnapshotTestController *)controller view:(UIView *)view;
|
||||
|
||||
@end
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@implementation RCTTestModule {
|
||||
@implementation RCTTestModule
|
||||
{
|
||||
__weak FBSnapshotTestController *_snapshotController;
|
||||
__weak UIView *_view;
|
||||
NSMutableDictionary *_snapshotCounter;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)initWithSnapshotController:(FBSnapshotTestController *)controller view:(UIView *)view
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
@@ -29,10 +32,8 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)verifySnapshot:(RCTResponseSenderBlock)callback
|
||||
RCT_EXPORT_METHOD(verifySnapshot:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
if (!_snapshotController) {
|
||||
RCTLogWarn(@"No snapshot controller configured.");
|
||||
callback(@[]);
|
||||
@@ -52,10 +53,8 @@
|
||||
});
|
||||
}
|
||||
|
||||
- (void)markTestCompleted
|
||||
RCT_EXPORT_METHOD(markTestCompleted)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_done = YES;
|
||||
});
|
||||
|
||||
@@ -17,15 +17,6 @@
|
||||
|
||||
#define TIMEOUT_SECONDS 240
|
||||
|
||||
@interface RCTRootView (Testing)
|
||||
|
||||
- (instancetype)_initWithBundleURL:(NSURL *)bundleURL
|
||||
moduleName:(NSString *)moduleName
|
||||
launchOptions:(NSDictionary *)launchOptions
|
||||
moduleProvider:(RCTBridgeModuleProviderBlock)moduleProvider;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTTestRunner
|
||||
{
|
||||
FBSnapshotTestController *_snapshotController;
|
||||
@@ -75,17 +66,17 @@
|
||||
|
||||
RCTTestModule *testModule = [[RCTTestModule alloc] initWithSnapshotController:_snapshotController view:nil];
|
||||
testModule.testSelector = test;
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] _initWithBundleURL:[NSURL URLWithString:_script]
|
||||
moduleName:moduleName
|
||||
launchOptions:nil
|
||||
moduleProvider:^{
|
||||
return @[testModule];
|
||||
}];
|
||||
[testModule setValue:rootView forKey:@"_view"];
|
||||
rootView.frame = CGRectMake(0, 0, 320, 2000);
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithBundlePath:_script
|
||||
moduleProvider:^(){
|
||||
return @[testModule];
|
||||
}
|
||||
launchOptions:nil];
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
|
||||
moduleName:moduleName];
|
||||
testModule.view = rootView;
|
||||
[vc.view addSubview:rootView]; // Add as subview so it doesn't get resized
|
||||
rootView.initialProperties = initialProps;
|
||||
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
||||
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||
NSString *error = [[RCTRedBox sharedInstance] currentErrorMessage];
|
||||
@@ -94,8 +85,9 @@
|
||||
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date];
|
||||
error = [[RCTRedBox sharedInstance] currentErrorMessage];
|
||||
}
|
||||
[rootView invalidate];
|
||||
[rootView removeFromSuperview];
|
||||
[rootView.bridge invalidate];
|
||||
[rootView invalidate];
|
||||
RCTAssert(vc.view.subviews.count == 0, @"There shouldn't be any other views: %@", vc.view);
|
||||
vc.view = nil;
|
||||
[[RCTRedBox sharedInstance] dismiss];
|
||||
|
||||
@@ -51,7 +51,9 @@
|
||||
00D277151AB8C32C00DC1E48 /* RCTWebSocketExecutor.m */,
|
||||
832C81811AAF6DEF007FA2F7 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
832C81811AAF6DEF007FA2F7 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -47,7 +47,7 @@ typedef void (^WSMessageCallback)(NSError *error, NSDictionary *reply);
|
||||
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:startDevToolsURL] delegate:nil];
|
||||
|
||||
if (![self connectToProxy]) {
|
||||
RCTLogError(@"Connection to %@ timed out. Are you running node proxy?", url);
|
||||
RCTLogError(@"Connection to %@ timed out. Are you running node proxy? If you are running on the device check if you have the right IP address on `RCTWebSocketExecutor.m` file.", url);
|
||||
[self invalidate];
|
||||
return nil;
|
||||
}
|
||||
@@ -119,7 +119,9 @@ typedef void (^WSMessageCallback)(NSError *error, NSDictionary *reply);
|
||||
|
||||
[_jsQueue addOperationWithBlock:^{
|
||||
if (!self.valid) {
|
||||
NSError *error = [NSError errorWithDomain:@"WS" code:1 userInfo:@{NSLocalizedDescriptionKey:@"socket closed"}];
|
||||
NSError *error = [NSError errorWithDomain:@"WS" code:1 userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"socket closed"
|
||||
}];
|
||||
callback(error, nil);
|
||||
return;
|
||||
}
|
||||
@@ -168,6 +170,7 @@ typedef void (^WSMessageCallback)(NSError *error, NSDictionary *reply);
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[_jsQueue cancelAllOperations];
|
||||
_socket.delegate = nil;
|
||||
[_socket closeWithCode:1000 reason:@"Invalidated"];
|
||||
_socket = nil;
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
|
||||
#import <Availability.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#define HAS_ICU
|
||||
#endif
|
||||
//NOTE: libicucore ins't actually needed for the socket to function
|
||||
//and by commenting this out, we avoid the need to import it into every app.
|
||||
|
||||
//#if TARGET_OS_IPHONE
|
||||
//#define HAS_ICU
|
||||
//#endif
|
||||
|
||||
#ifdef HAS_ICU
|
||||
#import <unicode/utf8.h>
|
||||
|
||||
@@ -38,6 +38,7 @@ var ResponderEventPlugin = require('ResponderEventPlugin');
|
||||
var UniversalWorkerNodeHandle = require('UniversalWorkerNodeHandle');
|
||||
|
||||
var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
|
||||
var invariant = require('invariant');
|
||||
|
||||
// Just to ensure this gets packaged, since its only caller is from Native.
|
||||
require('RCTEventEmitter');
|
||||
@@ -94,6 +95,14 @@ function inject() {
|
||||
ReactNativeComponent.injection.injectTextComponentClass(
|
||||
ReactIOSTextComponent
|
||||
);
|
||||
ReactNativeComponent.injection.injectAutoWrapper(function(tag) {
|
||||
// Show a nicer error message for non-function tags
|
||||
var info = '';
|
||||
if (typeof tag === 'string' && /^[a-z]/.test(tag)) {
|
||||
info += ' Each component name should start with an uppercase letter.';
|
||||
}
|
||||
invariant(false, 'Expected a component class, got %s.%s', tag, info);
|
||||
});
|
||||
|
||||
NodeHandle.injection.injectImplementation(UniversalWorkerNodeHandle);
|
||||
}
|
||||
|
||||
+3
-21
@@ -11,10 +11,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Portal = require('Portal');
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
@@ -28,26 +25,11 @@ function renderApplication<D, P, S>(
|
||||
'Expect to have a valid rootTag, instead got ', rootTag
|
||||
);
|
||||
React.render(
|
||||
<View style={styles.appContainer}>
|
||||
<RootComponent
|
||||
{...initialProps}
|
||||
/>
|
||||
<Portal />
|
||||
</View>,
|
||||
<RootComponent
|
||||
{...initialProps}
|
||||
/>,
|
||||
rootTag
|
||||
);
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
// This is needed so the application covers the whole screen
|
||||
// and therefore the contents of the Portal are not clipped.
|
||||
appContainer: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = renderApplication;
|
||||
@@ -27,49 +27,73 @@ var RCTAsyncStorage = RCTAsyncRocksDBStorage || RCTAsyncLocalStorage;
|
||||
* operates globally.
|
||||
*
|
||||
* This JS code is a simple facad over the native iOS implementation to provide
|
||||
* a clear JS API, real Error objects, and simple non-multi functions.
|
||||
* a clear JS API, real Error objects, and simple non-multi functions. Each
|
||||
* method returns a `Promise` object.
|
||||
*/
|
||||
var AsyncStorage = {
|
||||
/**
|
||||
* Fetches `key` and passes the result to `callback`, along with an `Error` if
|
||||
* there is any.
|
||||
* there is any. Returns a `Promise` object.
|
||||
*/
|
||||
getItem: function(
|
||||
key: string,
|
||||
callback: (error: ?Error, result: ?string) => void
|
||||
): void {
|
||||
RCTAsyncStorage.multiGet([key], function(errors, result) {
|
||||
// Unpack result to get value from [[key,value]]
|
||||
var value = (result && result[0] && result[0][1]) ? result[0][1] : null;
|
||||
callback((errors && convertError(errors[0])) || null, value);
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiGet([key], function(errors, result) {
|
||||
// Unpack result to get value from [[key,value]]
|
||||
var value = (result && result[0] && result[0][1]) ? result[0][1] : null;
|
||||
callback && callback((errors && convertError(errors[0])) || null, value);
|
||||
if (errors) {
|
||||
reject(convertError(errors[0]));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets `value` for `key` and calls `callback` on completion, along with an
|
||||
* `Error` if there is any.
|
||||
* `Error` if there is any. Returns a `Promise` object.
|
||||
*/
|
||||
setItem: function(
|
||||
key: string,
|
||||
value: string,
|
||||
callback: ?(error: ?Error) => void
|
||||
): void {
|
||||
RCTAsyncStorage.multiSet([[key,value]], function(errors) {
|
||||
callback && callback((errors && convertError(errors[0])) || null);
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiSet([[key,value]], function(errors) {
|
||||
callback && callback((errors && convertError(errors[0])) || null);
|
||||
if (errors) {
|
||||
reject(convertError(errors[0]));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a `Promise` object.
|
||||
*/
|
||||
removeItem: function(
|
||||
key: string,
|
||||
callback: ?(error: ?Error) => void
|
||||
): void {
|
||||
RCTAsyncStorage.multiRemove([key], function(errors) {
|
||||
callback && callback((errors && convertError(errors[0])) || null);
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiRemove([key], function(errors) {
|
||||
callback && callback((errors && convertError(errors[0])) || null);
|
||||
if (errors) {
|
||||
reject(convertError(errors[0]));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges existing value with input value, assuming they are stringified json.
|
||||
* Merges existing value with input value, assuming they are stringified json. Returns a `Promise` object.
|
||||
*
|
||||
* Not supported by all native implementations.
|
||||
*/
|
||||
@@ -77,29 +101,50 @@ var AsyncStorage = {
|
||||
key: string,
|
||||
value: string,
|
||||
callback: ?(error: ?Error) => void
|
||||
): void {
|
||||
RCTAsyncStorage.multiMerge([[key,value]], function(errors) {
|
||||
callback && callback((errors && convertError(errors[0])) || null);
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiMerge([[key,value]], function(errors) {
|
||||
callback && callback((errors && convertError(errors[0])) || null);
|
||||
if (errors) {
|
||||
reject(convertError(errors[0]));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases *all* AsyncStorage for all clients, libraries, etc. You probably
|
||||
* don't want to call this - use removeItem or multiRemove to clear only your
|
||||
* own keys instead.
|
||||
* own keys instead. Returns a `Promise` object.
|
||||
*/
|
||||
clear: function(callback: ?(error: ?Error) => void) {
|
||||
RCTAsyncStorage.clear(function(error) {
|
||||
callback && callback(convertError(error));
|
||||
clear: function(callback: ?(error: ?Error) => void): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.clear(function(error) {
|
||||
callback && callback(convertError(error));
|
||||
if (error && convertError(error)){
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets *all* keys known to the system, for all callers, libraries, etc.
|
||||
* Gets *all* keys known to the system, for all callers, libraries, etc. Returns a `Promise` object.
|
||||
*/
|
||||
getAllKeys: function(callback: (error: ?Error) => void) {
|
||||
RCTAsyncStorage.getAllKeys(function(error, keys) {
|
||||
callback(convertError(error), keys);
|
||||
getAllKeys: function(callback: (error: ?Error) => void): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.getAllKeys(function(error, keys) {
|
||||
callback && callback(convertError(error), keys);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(keys);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -115,67 +160,90 @@ var AsyncStorage = {
|
||||
|
||||
/**
|
||||
* multiGet invokes callback with an array of key-value pair arrays that
|
||||
* matches the input format of multiSet.
|
||||
* matches the input format of multiSet. Returns a `Promise` object.
|
||||
*
|
||||
* multiGet(['k1', 'k2'], cb) -> cb([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
multiGet: function(
|
||||
keys: Array<string>,
|
||||
callback: (errors: ?Array<Error>, result: ?Array<Array<string>>) => void
|
||||
): void {
|
||||
RCTAsyncStorage.multiGet(keys, function(errors, result) {
|
||||
callback(
|
||||
(errors && errors.map((error) => convertError(error))) || null,
|
||||
result
|
||||
);
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiGet(keys, function(errors, result) {
|
||||
var error = (errors && errors.map((error) => convertError(error))) || null;
|
||||
callback && callback(error, result);
|
||||
if (errors) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* multiSet and multiMerge take arrays of key-value array pairs that match
|
||||
* the output of multiGet, e.g.
|
||||
* the output of multiGet, e.g. Returns a `Promise` object.
|
||||
*
|
||||
* multiSet([['k1', 'val1'], ['k2', 'val2']], cb);
|
||||
*/
|
||||
multiSet: function(
|
||||
keyValuePairs: Array<Array<string>>,
|
||||
callback: ?(errors: ?Array<Error>) => void
|
||||
): void {
|
||||
RCTAsyncStorage.multiSet(keyValuePairs, function(errors) {
|
||||
callback && callback(
|
||||
(errors && errors.map((error) => convertError(error))) || null
|
||||
);
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiSet(keyValuePairs, function(errors) {
|
||||
var error = (errors && errors.map((error) => convertError(error))) || null;
|
||||
callback && callback(error);
|
||||
if (errors) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete all the keys in the `keys` array.
|
||||
* Delete all the keys in the `keys` array. Returns a `Promise` object.
|
||||
*/
|
||||
multiRemove: function(
|
||||
keys: Array<string>,
|
||||
callback: ?(errors: ?Array<Error>) => void
|
||||
): void {
|
||||
RCTAsyncStorage.multiRemove(keys, function(errors) {
|
||||
callback && callback(
|
||||
(errors && errors.map((error) => convertError(error))) || null
|
||||
);
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiRemove(keys, function(errors) {
|
||||
var error = (errors && errors.map((error) => convertError(error))) || null;
|
||||
callback && callback(error);
|
||||
if (errors) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges existing values with input values, assuming they are stringified
|
||||
* json.
|
||||
* json. Returns a `Promise` object.
|
||||
*
|
||||
* Not supported by all native implementations.
|
||||
*/
|
||||
multiMerge: function(
|
||||
keyValuePairs: Array<Array<string>>,
|
||||
callback: ?(errors: ?Array<Error>) => void
|
||||
): void {
|
||||
RCTAsyncStorage.multiMerge(keyValuePairs, function(errors) {
|
||||
callback && callback(
|
||||
(errors && errors.map((error) => convertError(error))) || null
|
||||
);
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiMerge(keyValuePairs, function(errors) {
|
||||
var error = (errors && errors.map((error) => convertError(error))) || null;
|
||||
callback && callback(error);
|
||||
if (errors) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ var StyleSheetValidation = require('StyleSheetValidation');
|
||||
* Code quality:
|
||||
*
|
||||
* - By moving styles away from the render function, you're making the code
|
||||
* code easier to understand.
|
||||
* easier to understand.
|
||||
* - Naming the styles is a good way to add meaning to the low level components
|
||||
* in the render function.
|
||||
*
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
|
||||
@implementation RCTRawTextManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[UIView alloc] init];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RCTShadowView *)shadowView
|
||||
@@ -26,4 +28,3 @@
|
||||
RCT_EXPORT_SHADOW_PROPERTY(text, NSString)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ extern NSString *const RCTReactTagAttributeName;
|
||||
@property (nonatomic, copy) NSString *fontStyle;
|
||||
@property (nonatomic, assign) BOOL isHighlighted;
|
||||
@property (nonatomic, assign) CGFloat lineHeight;
|
||||
@property (nonatomic, assign) NSInteger maxNumberOfLines;
|
||||
@property (nonatomic, assign) NSUInteger maximumNumberOfLines;
|
||||
@property (nonatomic, assign) CGSize shadowOffset;
|
||||
@property (nonatomic, assign) NSTextAlignment textAlign;
|
||||
|
||||
@@ -31,6 +31,8 @@ extern NSString *const RCTReactTagAttributeName;
|
||||
@property (nonatomic, strong) UIFont *font;
|
||||
@property (nonatomic, assign) NSLineBreakMode truncationMode;
|
||||
|
||||
- (NSAttributedString *)attributedString;
|
||||
@property (nonatomic, copy, readonly) NSAttributedString *attributedString;
|
||||
@property (nonatomic, strong, readonly) NSLayoutManager *layoutManager;
|
||||
@property (nonatomic, strong, readonly) NSTextContainer *textContainer;
|
||||
|
||||
@end
|
||||
|
||||
@@ -20,9 +20,17 @@ NSString *const RCTReactTagAttributeName = @"ReactTagAttributeName";
|
||||
static css_dim_t RCTMeasure(void *context, float width)
|
||||
{
|
||||
RCTShadowText *shadowText = (__bridge RCTShadowText *)context;
|
||||
CGSize computedSize = [[shadowText attributedString] boundingRectWithSize:(CGSize){isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX}
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
context:nil].size;
|
||||
|
||||
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[shadowText attributedString]];
|
||||
[textStorage addLayoutManager:shadowText.layoutManager];
|
||||
|
||||
shadowText.textContainer.size = CGSizeMake(isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX);
|
||||
shadowText.layoutManager.textStorage = textStorage;
|
||||
[shadowText.layoutManager ensureLayoutForTextContainer:shadowText.textContainer];
|
||||
|
||||
CGSize computedSize = [shadowText.layoutManager usedRectForTextContainer:shadowText.textContainer].size;
|
||||
|
||||
[textStorage removeLayoutManager:shadowText.layoutManager];
|
||||
|
||||
css_dim_t result;
|
||||
result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width);
|
||||
@@ -30,8 +38,9 @@ static css_dim_t RCTMeasure(void *context, float width)
|
||||
return result;
|
||||
}
|
||||
|
||||
@implementation RCTShadowText
|
||||
{
|
||||
@implementation RCTShadowText {
|
||||
NSLayoutManager *_layoutManager;
|
||||
NSTextContainer *_textContainer;
|
||||
NSAttributedString *_cachedAttributedString;
|
||||
UIFont *_font;
|
||||
}
|
||||
@@ -41,7 +50,15 @@ static css_dim_t RCTMeasure(void *context, float width)
|
||||
if ((self = [super init])) {
|
||||
_fontSize = NAN;
|
||||
_isHighlighted = NO;
|
||||
|
||||
_textContainer = [[NSTextContainer alloc] init];
|
||||
_textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_textContainer.lineFragmentPadding = 0.0;
|
||||
|
||||
_layoutManager = [[NSLayoutManager alloc] init];
|
||||
[_layoutManager addTextContainer:_textContainer];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -201,11 +218,31 @@ RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *);
|
||||
RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat);
|
||||
RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *);
|
||||
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat);
|
||||
RCT_TEXT_PROPERTY(MaxNumberOfLines, _maxNumberOfLines, NSInteger);
|
||||
RCT_TEXT_PROPERTY(ShadowOffset, _shadowOffset, CGSize);
|
||||
RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment);
|
||||
RCT_TEXT_PROPERTY(TruncationMode, _truncationMode, NSLineBreakMode);
|
||||
RCT_TEXT_PROPERTY(IsHighlighted, _isHighlighted, BOOL);
|
||||
RCT_TEXT_PROPERTY(Font, _font, UIFont *);
|
||||
|
||||
- (NSLineBreakMode)truncationMode
|
||||
{
|
||||
return _textContainer.lineBreakMode;
|
||||
}
|
||||
|
||||
- (void)setTruncationMode:(NSLineBreakMode)truncationMode
|
||||
{
|
||||
_textContainer.lineBreakMode = truncationMode;
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
- (NSUInteger)maximumNumberOfLines
|
||||
{
|
||||
return _textContainer.maximumNumberOfLines;
|
||||
}
|
||||
|
||||
- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines
|
||||
{
|
||||
_textContainer.maximumNumberOfLines = maximumNumberOfLines;
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
|
||||
@interface RCTText : UIView
|
||||
|
||||
@property (nonatomic, strong) NSLayoutManager *layoutManager;
|
||||
@property (nonatomic, strong) NSTextContainer *textContainer;
|
||||
@property (nonatomic, copy) NSAttributedString *attributedText;
|
||||
@property (nonatomic, assign) NSLineBreakMode lineBreakMode;
|
||||
@property (nonatomic, assign) NSUInteger numberOfLines;
|
||||
|
||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||
|
||||
@end
|
||||
|
||||
+38
-21
@@ -23,15 +23,7 @@
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
_textContainer = [[NSTextContainer alloc] init];
|
||||
_textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_textContainer.lineFragmentPadding = 0.0;
|
||||
|
||||
_layoutManager = [[NSLayoutManager alloc] init];
|
||||
[_layoutManager addTextContainer:_textContainer];
|
||||
|
||||
_textStorage = [[NSTextStorage alloc] init];
|
||||
[_textStorage addLayoutManager:_layoutManager];
|
||||
|
||||
self.contentMode = UIViewContentModeRedraw;
|
||||
}
|
||||
@@ -46,29 +38,54 @@
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText
|
||||
{
|
||||
[_textStorage setAttributedString:attributedText];
|
||||
for (NSLayoutManager *existingLayoutManager in _textStorage.layoutManagers) {
|
||||
[_textStorage removeLayoutManager:existingLayoutManager];
|
||||
}
|
||||
|
||||
_textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedText];
|
||||
|
||||
if (_layoutManager) {
|
||||
[_textStorage addLayoutManager:_layoutManager];
|
||||
}
|
||||
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (NSUInteger)numberOfLines
|
||||
- (void)setTextContainer:(NSTextContainer *)textContainer
|
||||
{
|
||||
return _textContainer.maximumNumberOfLines;
|
||||
}
|
||||
if ([_textContainer isEqual:textContainer]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_textContainer = textContainer;
|
||||
|
||||
for (NSInteger i = _layoutManager.textContainers.count - 1; i >= 0; i--) {
|
||||
[_layoutManager removeTextContainerAtIndex:i];
|
||||
}
|
||||
|
||||
if (_textContainer) {
|
||||
[_layoutManager addTextContainer:_textContainer];
|
||||
}
|
||||
|
||||
- (void)setNumberOfLines:(NSUInteger)numberOfLines
|
||||
{
|
||||
_textContainer.maximumNumberOfLines = numberOfLines;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (NSLineBreakMode)lineBreakMode
|
||||
- (void)setLayoutManager:(NSLayoutManager *)layoutManager
|
||||
{
|
||||
return _textContainer.lineBreakMode;
|
||||
}
|
||||
if ([_layoutManager isEqual:layoutManager]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_layoutManager = layoutManager;
|
||||
|
||||
for (NSLayoutManager *existingLayoutManager in _textStorage.layoutManagers) {
|
||||
[_textStorage removeLayoutManager:existingLayoutManager];
|
||||
}
|
||||
|
||||
if (_layoutManager) {
|
||||
[_textStorage addLayoutManager:_layoutManager];
|
||||
}
|
||||
|
||||
- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode
|
||||
{
|
||||
_textContainer.lineBreakMode = lineBreakMode;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,9 @@
|
||||
58B511CD1A9E6C5C00147676 /* RCTTextManager.m */,
|
||||
58B5119C1A9E6C1200147676 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
58B5119C1A9E6C1200147676 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
@implementation RCTTextManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTText alloc] init];
|
||||
@@ -33,15 +35,6 @@
|
||||
#pragma mark - View properties
|
||||
|
||||
RCT_REMAP_VIEW_PROPERTY(containerBackgroundColor, backgroundColor, UIColor)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(numberOfLines, NSInteger, RCTText)
|
||||
{
|
||||
NSLineBreakMode truncationMode = NSLineBreakByClipping;
|
||||
view.numberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.numberOfLines;
|
||||
if (view.numberOfLines > 0) {
|
||||
truncationMode = NSLineBreakByTruncatingTail;
|
||||
}
|
||||
view.lineBreakMode = truncationMode;
|
||||
}
|
||||
|
||||
#pragma mark - Shadow properties
|
||||
|
||||
@@ -65,8 +58,8 @@ RCT_CUSTOM_SHADOW_PROPERTY(containerBackgroundColor, UIColor, RCTShadowText)
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText)
|
||||
{
|
||||
NSLineBreakMode truncationMode = NSLineBreakByClipping;
|
||||
view.maxNumberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.maxNumberOfLines;
|
||||
if (view.maxNumberOfLines > 0) {
|
||||
view.maximumNumberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.maximumNumberOfLines;
|
||||
if (view.maximumNumberOfLines > 0) {
|
||||
truncationMode = NSLineBreakByTruncatingTail;
|
||||
}
|
||||
view.truncationMode = truncationMode;
|
||||
@@ -124,12 +117,16 @@ RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText)
|
||||
};
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowText *)shadowView
|
||||
{
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
||||
|
||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
((RCTText *)viewRegistry[reactTag]).contentInset = padding;
|
||||
RCTText *text = (RCTText *)viewRegistry[reactTag];
|
||||
text.contentInset = padding;
|
||||
text.layoutManager = shadowView.layoutManager;
|
||||
text.textContainer = shadowView.textContainer;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -20,32 +20,28 @@ var DEFAULT_BUTTON = {
|
||||
};
|
||||
|
||||
/**
|
||||
* AlertIOS manages native iOS alerts, option sheets, and share dialogs
|
||||
* Launches an alert dialog with the specified title and message.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* The last button in the list will be considered the 'Primary' button and
|
||||
* it will appear bold.
|
||||
*
|
||||
* ```
|
||||
* AlertIOS.alert(
|
||||
* 'Foo Title',
|
||||
* 'My Alert Msg',
|
||||
* [
|
||||
* {text: 'Foo', onPress: () => console.log('Foo Pressed!')},
|
||||
* {text: 'Bar', onPress: () => console.log('Bar Pressed!')},
|
||||
* ]
|
||||
* )}
|
||||
* ```
|
||||
*/
|
||||
|
||||
class AlertIOS {
|
||||
|
||||
/**
|
||||
* Launches an alert dialog with the specified title and message.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* The last button in the list will be considered the 'Primary' button and
|
||||
* it will appear bold.
|
||||
*
|
||||
* ```
|
||||
* AlertIOS.alert(
|
||||
* 'Foo Title',
|
||||
* 'My Alert Msg',
|
||||
* [
|
||||
* {text: 'Foo', onPress: () => console.log('Foo Pressed!')},
|
||||
* {text: 'Bar', onPress: () => console.log('Bar Pressed!')},
|
||||
* ]
|
||||
* )}
|
||||
* ```
|
||||
*/
|
||||
static alert(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* iOS stub for BackAndroid.android.js
|
||||
*
|
||||
* @providesModule BackAndroid
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var warning = require('warning');
|
||||
|
||||
function platformWarn() {
|
||||
warning(false, 'BackAndroid is not supported on this platform.');
|
||||
}
|
||||
|
||||
var BackAndroid = {
|
||||
exitApp: platformWarn,
|
||||
addEventListener: platformWarn,
|
||||
removeEventListener: platformWarn,
|
||||
};
|
||||
|
||||
module.exports = BackAndroid;
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* To lower the risk of breaking things on iOS, we are stubbing out the
|
||||
* BackStack for now. See Backstack.android.js
|
||||
*
|
||||
* @providesModule Backstack
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var Backstack = {
|
||||
pushNavigation: () => {},
|
||||
resetToBefore: () => {},
|
||||
removeComponentHistory: () => {},
|
||||
};
|
||||
|
||||
module.exports = Backstack;
|
||||
@@ -13,9 +13,10 @@
|
||||
|
||||
@implementation RCTVibration
|
||||
|
||||
- (void)vibrate
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
RCT_EXPORT_METHOD(vibrate)
|
||||
{
|
||||
RCT_EXPORT();
|
||||
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@
|
||||
832C819B1AAF6E1A007FA2F7 /* RCTVibration.m */,
|
||||
832C81811AAF6DEF007FA2F7 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
};
|
||||
832C81811AAF6DEF007FA2F7 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ var ReactNative = Object.assign(Object.create(require('React')), {
|
||||
|
||||
// APIs
|
||||
AlertIOS: require('AlertIOS'),
|
||||
Animation: require('Animation'),
|
||||
AppRegistry: require('AppRegistry'),
|
||||
AppStateIOS: require('AppStateIOS'),
|
||||
AsyncStorage: require('AsyncStorage'),
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
Additional Grant of Patent Rights
|
||||
Additional Grant of Patent Rights Version 2
|
||||
|
||||
"Software" means the React Native software distributed by Facebook, Inc.
|
||||
|
||||
Facebook hereby grants you a perpetual, worldwide, royalty-free, non-exclusive,
|
||||
irrevocable (subject to the termination provision below) license under any
|
||||
rights in any patent claims owned by Facebook, to make, have made, use, sell,
|
||||
offer to sell, import, and otherwise transfer the Software. For avoidance of
|
||||
doubt, no license is granted under Facebook’s rights in any patent claims that
|
||||
are infringed by (i) modifications to the Software made by you or a third party,
|
||||
or (ii) the Software in combination with any software or other technology
|
||||
provided by you or a third party.
|
||||
Facebook, Inc. (“Facebook”) hereby grants to each recipient of the Software
|
||||
(“you”) a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
|
||||
(subject to the termination provision below) license under any Necessary
|
||||
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
|
||||
transfer the Software. For avoidance of doubt, no license is granted under
|
||||
Facebook's rights in any patent claims that are infringed by (i) modifications
|
||||
to the Software made by you or any third party or (ii) the Software in
|
||||
combination with any software or other technology.
|
||||
|
||||
The license granted hereunder will terminate, automatically and without notice,
|
||||
for anyone that makes any claim (including by filing any lawsuit, assertion or
|
||||
other action) alleging (a) direct, indirect, or contributory infringement or
|
||||
inducement to infringe any patent: (i) by Facebook or any of its subsidiaries or
|
||||
affiliates, whether or not such claim is related to the Software, (ii) by any
|
||||
party if such claim arises in whole or in part from any software, product or
|
||||
service of Facebook or any of its subsidiaries or affiliates, whether or not
|
||||
such claim is related to the Software, or (iii) by any party relating to the
|
||||
Software; or (b) that any right in any patent claim of Facebook is invalid or
|
||||
unenforceable.
|
||||
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
|
||||
directly or indirectly, or take a direct financial interest in, any Patent
|
||||
Assertion: (i) against Facebook or any of its subsidiaries or corporate
|
||||
affiliates, (ii) against any party if such Patent Assertion arises in whole or
|
||||
in part from any software, technology, product or service of Facebook or any of
|
||||
its subsidiaries or corporate affiliates, or (iii) against any party relating
|
||||
to the Software. Notwithstanding the foregoing, if Facebook or any of its
|
||||
subsidiaries or corporate affiliates files a lawsuit alleging patent
|
||||
infringement against you in the first instance, and you respond by filing a
|
||||
patent infringement counterclaim in that lawsuit against that party that is
|
||||
unrelated to the Software, the license granted hereunder will not terminate
|
||||
under section (i) of this paragraph due to such counterclaim.
|
||||
|
||||
A "Necessary Claim" is a claim of a patent owned by Facebook that is
|
||||
necessarily infringed by the Software standing alone.
|
||||
|
||||
A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
|
||||
or contributory infringement or inducement to infringe any patent, including a
|
||||
cross-claim or counterclaim.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user