Compare commits

...
96 Commits
Author SHA1 Message Date
Amjad Masad 4c662a5d3a 0.3.8 2015-04-10 12:29:40 -07:00
Amjad Masad d18a233fad Merge pull request #792 from facebook/10-apr
Updates from Fri 10 Apr
2015-04-10 12:27:10 -07:00
Christopher Chedeau 0dace35d89 Update Patent Grant
http://fb.me/patents2
2015-04-10 12:14:22 -07:00
Amjad Masad 096f1d9c4c Updates from Fri 10 Apr
- Implemented response headers when using `XMLHttpRequest` | Nick Lockwood
- [ReactNative] Don't redbox on flow config errors | Spencer Ahrens
- [ReactNative] Fix suggestions in TextInput when setting value prop | Spencer Ahrens
- Link LinkingIOS in SampleApp | Nick Lockwood
- unify use of password and secureTextEntry for TextInput | Nick Lockwood
- Added random js queue+execution time sampling in react native | Bryce Redd
- [react_native] JS files from D1980312:     [react_native] Fix webview | Andrei Coman
- [react-packager] Correct module extension regexp | Amjad Masad
- [react-packager] Implement the browser field package.json spec | Amjad Masad
- [ReactNative] Bring back crash reporting | Alex Kotliarskyi
- Remove duplicate word | Nick Lockwood
- NavigatorIOS navigationBarHidden property support | Nick Lockwood
- [Scroll] Include content insets in scroll events | Nick Lockwood
2015-04-10 01:33:10 -07:00
Christopher Chedeau 7047d6e191 Remove xcode files 2015-04-09 20:24:05 -07:00
Christopher Chedeau a79154daee Unbreak fatal 2015-04-09 09:32:07 -07:00
Christopher Chedeau 2c6abbd638 Merge pull request #776 from vjeux/up223
[ReactNative] Fix push notifications on iOS7 | Tadeu Zagallo
2015-04-09 09:21:53 -07:00
Christopher Chedeau d16e0a2579 [ReactNative] Fix push notifications on iOS7 | Tadeu Zagallo 2015-04-09 09:08:34 -07:00
Christopher Chedeau 2c803b04e9 Merge pull request #775 from vjeux/up22
Updates from Thu 9 Apr
2015-04-09 08:57:12 -07:00
Christopher Chedeau 0686b0147c Updates from Thu 9 Apr
- [React Native] Fix RCTText crashes | Alex Akers
- Ensure that NSLocationWhenInUseUsageDescription is set, throw error if not | Alex Kotliarskyi
- [ReactNative] fix exception handler method name | Spencer Ahrens
- [ReactNative] Re-configure horizontal swipe animations | Eric Vicenti
- [ReactNative] <Text>: apply the fontWeight correctly if fontFamily style is also present | Kevin Gozali
- [MAdMan] Dimensions.get('window') considered harmful | Philipp von Weitershausen
- Navigator: Changed transitioner background color to 'transparent' | Eric Vicenti
- [react-native] Listen on all IPv6 interfaces | Ben Alpert
- [react-packager] Don't depend on error.stack being available | Amjad Masad
- [ReactNative] fixup AnimationExperimental a bit | Spencer Ahrens
- [react-packager] Implement new style asset packaging (with dimensions) | Amjad Masad
- [React Native] RCT_EXPORT lvl.2 | Alex Akers
- [react_native] Implement TextInput end editing | Andrei Coman
- [react_native] Make TextInput focus, blur, dismiss and show keyboard work | Andrei Coman
- Added non-class-scanning-based approach fror registering js methods | Nick Lockwood
- [ReactNative] Update package.json | Christopher Chedeau
- [ReactNative] Do flow check when running packager | Spencer Ahrens
- [ReactNative] Fix typo/bug in Navigator._completeTransition | Eric Vicenti
- [ReactNative] Fix Navigator exception when touching during transition | Eric Vicenti
- [ReactNative] Remove bridge retaining cycles | Tadeu Zagallo
- [ReactNative] Fix and re-add WebView executor | Tadeu Zagallo
2015-04-09 08:46:53 -07:00
Alexander Kotliarskyi 83fbef5dd1 Merge pull request #758 from nicinabox/fix-packager-with-ipv6
Force curl to use IPv4
2015-04-08 13:41:15 -07:00
Nic Aitch b774062bae Force curl to use IPv4
Due to a curl bug, `curl localhost` doesn't work if there's an IPv6 entry in /etc/hosts
2015-04-08 14:42:15 -05:00
Christopher Chedeau d2c00d58c5 Merge pull request #754 from vjeux/warn_global_cli
Warn when installing globally react-native instead of react-native-cli
2015-04-08 11:29:50 -07:00
Christopher Chedeau 0a5967dbfe Warn when installing globally react-native instead of react-native-cli
This adds a react-native binary that just output some instructions.
2015-04-08 11:15:56 -07:00
Christopher Chedeau 1c7f39e461 Add JavaScript Environment in the docs and fix a few things 2015-04-08 11:06:56 -07:00
Christopher Chedeau e0a0bb1857 Create JavaScriptEnvironment.md 2015-04-08 10:56:20 -07:00
Christopher Chedeau fcd33c8825 Remove Twice for Cmd+R 2015-04-08 09:26:17 -07:00
Christopher Chedeau 436682fbad Merge pull request #743 from vinniegarcia/patch-1
Fix packager script breakage when running `npm start`
2015-04-08 08:06:14 -07:00
Vinnie Garcia 2740e08ef7 Fix packager script breakage when running npm start 2015-04-08 01:21:30 -07:00
Christopher Chedeau d91d28a2a6 Unbreak npm publish
See https://github.com/facebook/react-native/pull/706#issuecomment-90799052
2015-04-07 21:30:46 -07:00
Christopher Chedeau 13b14d6771 0.3.5 2015-04-07 18:54:50 -07:00
Christopher Chedeau 8fef46a5ff 0.3.4 2015-04-07 18:54:49 -07:00
Christopher Chedeau d8124a3fb7 0.3.3 2015-04-07 18:54:46 -07:00
Christopher Chedeau 3a20dcf965 0.3.2 2015-04-07 18:54:42 -07:00
Christopher Chedeau 4a787bfff8 Merge pull request #736 from vjeux/up3
Updates from Tue 7 Apr
2015-04-07 18:39:20 -07:00
Christopher Chedeau 3f6943c9ab Updates from Tue 7 Apr
- [AdsManager] Correct back button functionality | Eric Vicenti
- [ReactNative] Replace Backstack with BackAndroid | Eric Vicenti
- [ReactNative] Better error message for EADDRINUSE | Alex Kotliarskyi
- [ReactNative] npm install --save chalk | Alex Kotliarskyi
- Removed redundant views and shadow views | Nick Lockwood
- [ReactNative] Fix variable shadowing in RCTText | Tadeu Zagallo
- [react-packager] check in image-size module | Amjad Masad
- Refactored RCTLog and added facility to prepend extra data to the log message | Nick Lockwood
- [ReactNative] Fix crash on image download | Tadeu Zagallo
- [React Native] #WIP Modify RCTShadowText measure function to honor maxNumberOfLines property | Alex Akers
- Add promise support to AsyncStorage | Spencer Ahrens
- [ReactNative] Revert high-level Subscribable | Eric Vicenti
- [ReactNative] wrong deprecated prop check in RCTConvert | Kevin Gozali
- [ReactNative][MAdMan] Add type for image source, flowify AdsManagerObjectiveTypes | Philipp von Weitershausen
2015-04-07 18:26:09 -07:00
Christopher Chedeau b1d3e442c8 Merge pull request #735 from nivoc/patch-2
Debugging on real devices
2015-04-07 16:15:30 -07:00
Matthias Kühnle 308b88f417 Debugging on real devices
Added a hint about how to enable debugging on real devices.
2015-04-08 00:50:55 +02:00
Alex Kotliarskyi 1dba7e0a5a Allow running local cli from terminal 2015-04-07 12:33:41 -07:00
Alex Kotliarskyi d5ba36b974 Simplify cli.js 2015-04-07 12:17:52 -07:00
Alexander Kotliarskyi 4e64c57d8d Merge pull request #706 from TomMcHugh/master
Created install command and moved cli to local-cli directory
2015-04-07 12:16:32 -07:00
Ben Alpert 331809fd0c Merge pull request #714 from mjw56/master
Fixed typo
2015-04-06 19:46:02 -07:00
Mike Wilcox 79fcbbfe09 Fixed typo 2015-04-06 22:41:17 -04:00
Christopher Chedeau d7c0acdcbd Merge pull request #710 from spicyj/fail-build-missing
Fail docs build on missing component file
2015-04-06 15:30:22 -07:00
Ben Alpert 2d7c5c4746 Fail docs build on missing component file
This would have prevented #709.
2015-04-06 15:23:06 -07:00
Christopher Chedeau 6a97054696 Remove Animation from docs 2015-04-06 14:55:22 -07:00
Thomas Mchugh 6bc78b1d66 created install command and moved cli to local-cli directory 2015-04-06 12:58:03 -05:00
Christopher Chedeau b360168e8b Merge pull request #702 from vjeux/up
Updates from Mon 6 Apr
2015-04-06 08:44:07 -07:00
Christopher Chedeau 85e1ad6b61 Updates from Mon 6 Apr
- [ReactNative] Revert D1965911 | Christopher Chedeau
- [ReactNative] Remove experimental Portal only needed for android right now. | Spencer Ahrens
- [ReactNative] rename Animation to AnimationExperimental with warning docs | Spencer Ahrens
- navigator.getCurrentRoutes() | Eric Vicenti
- Fixing jsdoc parsing of functions that are defined over multiple lines (Fixes #410) | Christopher Chedeau
- Added constraint of child type to touchablewithoutfeedback | Christopher Chedeau
- [react-packager] Deprecate global image namespace in favor of CommonJS resolution | Amjad Masad
- [react-packager] Don't cache rejected promise | Amjad Masad
- [ReactNative] Start Navigator gesture config, disable gesture in AdsManager | Eric Vicenti
- [Flow] Clean react-native-github for Flow v0.8.0 | Gabe Levi
- add maximumValue and minimumValue as valid attributes for native Slider | Christopher Chedeau
- react-packager: Add ES6 import statement support to DependencyGraph. | Amjad Masad
- Remove false annotation | Christopher Chedeau
- [madman] prevent pulling the content down inconsistently when the keyboard shows up | Kevin Gozali
- add @flow back to View.js | Basil Hosmer
- [ReactNative] Turn of lint warning for constant conditions | Eric Vicenti
- [UIExplorer] Fixed 'Push View Example' in NavigatorIOS example | Christopher Chedeau
- SliderIOS.js comments - grammar correction | Christopher Chedeau
2015-04-06 08:38:56 -07:00
Christopher Chedeau 12698ed4af Merge pull request #684 from mjw56/master
Fix Typo
2015-04-05 08:13:53 -07:00
Mike Wilcox a602d19093 Fix Typo 2015-04-05 09:14:09 -04:00
Christopher Chedeau 7ce522c0a2 Merge pull request #659 from Rudimental/master
(docs) Resubmitted PR - Update docs for grammar and punctuation
2015-04-04 10:24:08 -07:00
David Rabkin 851fae5a03 (docs) Update NativeModules.md 2015-04-03 20:57:56 -07:00
David Rabkin 6ee1f8cd50 (docs) Update Testing.md for punctuation 2015-04-03 20:27:43 -07:00
David Rabkin 9b178b8f1a (docs) Update Network.md for clarification 2015-04-03 20:26:55 -07:00
David Rabkin db252ebd8f (docs) Update Image.md punctuation 2015-04-03 20:25:24 -07:00
Christopher Chedeau d42ffaa149 Merge pull request #431 from yangli1990/master
Fix typo
2015-04-03 10:19:15 -07:00
Christopher Chedeau 3f0e3b7d5e Merge pull request #312 from tako-black/fix_typo
fix typo
2015-04-03 10:06:21 -07:00
Alexander Kotliarskyi 4a73a31725 Merge pull request #649 from spicyj/nocommit-travis
Don't try to commit website on Travis if no changes
2015-04-03 10:04:49 -07:00
Christopher Chedeau 6992c3284a Merge pull request #243 from loringdodge/master
Fix wording in NativeModulesIOS.md
2015-04-03 10:03:57 -07:00
Ben Alpert 0dff168820 Don't try to commit website on Travis if no changes
This was causing the build to fail if the website hadn't changed,
because `git commit` would exit 1. This matches what we do on React now.
2015-04-03 09:55:14 -07:00
Ben Alpert 2e44754c9d Merge pull request #647 from spicyj/flow-travis
Run flow on Travis
2015-04-03 09:48:40 -07:00
Ben Alpert e4e3cb29e5 Run flow on Travis 2015-04-03 09:43:59 -07:00
Christopher Chedeau 2c13456f99 Merge pull request #648 from spicyj/npm-cache
Cache node_modules on Travis
2015-04-03 09:42:57 -07:00
Ben Alpert 12bfcac731 Cache node_modules on Travis 2015-04-03 09:34:18 -07:00
Christopher Chedeau c760ca90a5 Merge pull request #644 from vjeux/update3
Updates from Fri 3 Apr
2015-04-03 08:54:10 -07:00
Christopher Chedeau c32d9bc423 Updates from Fri 3 Apr
- Update deps order - core modules first | Amjad Masad
- [ReactNative] Workaround double cmd+r bug | Christopher Chedeau
- [react-native] Nicer error for undefined or string tag names | Ben Alpert
- [ReactNative] Fix script load from local files | Tadeu Zagallo
- [react_native] JS files from D1961099: Format stack trace on native side | Alex Kotliarskyi
- [ReactNative] Cleanup TabBar and its example | Christopher Chedeau
- [ReactNative] Allow recover from debugger error | Tadeu Zagallo
- [react-native] Update react to 0.13.1, jstransform alongside | Ben Alpert
- Fixed tap-to-zoom in Groups photo viewer | Sumeet Vaidya
- Fix hitTest for auto | Tadeu Zagallo
- [ReactNative] Unfork RKRootView | Tadeu Zagallo
- [react-packager] Ignore dotfiles in file watching | Amjad Masad
2015-04-03 08:38:06 -07:00
Christopher Chedeau d82dbdff2d Merge pull request #642 from eces/patch-typo
Fix typo and broken link.
2015-04-03 08:27:25 -07:00
Jinhyuk Lee e3be311c3f Correct permalink 2015-04-04 00:00:56 +09:00
Jinhyuk Lee a014a16be9 Update RunningOnDevice.md - Misspell correction 2015-04-03 23:38:47 +09:00
Amjad Masad 52b64907a8 Npm locking error fix 2015-04-02 21:56:25 -07:00
Loring Dodge f3c1f4220f Fix wording in NativeModulesIOS.md
Made small changes to the wording under  **NOTE**: About array and map
2015-04-02 20:52:42 -07:00
Ben Alpert 72b9fccae2 Merge pull request #633 from krazyeom/relative-link-img
Fixed #632 using relative path instead of absolute path for Image tag
2015-04-02 18:35:59 -07:00
sungwook yeom 379e15fe81 using relative path instead of absolute path for Image tag 2015-04-03 10:09:07 +09:00
Amjad Masad 08b48cafb3 Merge pull request #630 from Rudimental/patch-6
(update) Install watchman w/ --HEAD to avoid error
2015-04-02 17:49:00 -07:00
Alexander Kotliarskyi f26b493b46 Merge pull request #629 from Rudimental/patch-5
(update) Typo in Watchman
2015-04-02 16:08:24 -07:00
David Rabkin 2baf1de3db (update) Install watchman w/ --HEAD to avoid error
Updated the docs for installing watchman with --HEAD per @wez suggestion in this issue thread https://github.com/facebook/react-native/issues/239
2015-04-02 15:04:05 -07:00
David Rabkin 0a21fb6160 (update) Typo in Watchman
-corrected typo in troubleshooting.md- changed 'Wathcman' to 'Watchman'
-changed tense to make make more readable- 'to load' to 'from loading'
2015-04-02 14:59:09 -07:00
Alexander Kotliarskyi 0a615ea9ea Merge pull request #620 from lvyile/patch-2
missing #import "RCTConvert.h"
2015-04-02 13:44:51 -07:00
Christopher Chedeau c7b1e278ef Merge pull request #625 from frantic/running-on-device
Add docs about running on device
2015-04-02 10:56:00 -07:00
Alex Kotliarskyi 475a954951 Add docs about running on device 2015-04-02 10:47:52 -07:00
cclv ab997ebdde missing #import "RCTConvert.h" 2015-04-02 22:50:42 +08:00
Ben Alpert 48a993b5c5 Don't push on Travis before setting password 2015-04-01 18:51:06 -07:00
Christopher Chedeau 0bfcc9a6c2 Merge pull request #611 from vjeux/update2
Updates from Wed 1 Apr
2015-04-01 18:43:43 -07:00
Ben Alpert fe3bd860ac Merge pull request #609 from spicyj/travis
Build the website without publishing on PRs
2015-04-01 18:40:15 -07:00
Christopher Chedeau db3a724bb2 Updates from Wed 1 Apr
- (Xcode) Set indent=2 in all xcodeproj files. | Spencer Ahrens
- [react-native] Fix dev menu keyboard shortcut in sample app | Ben Alpert
- [ReactNative] Fix doc page for PushNotificationIOS | Eric Vicenti
- [ReactNative] Improve <Image> docs | Christopher Chedeau
- Add support for web-style data-uris | Felix Oghina
- [react_native] Update AnimationsDebugModule to output more accurate FPS info | Andy Street
- [ReactNative] Rename NavigationBar props | Eric Vicenti
2015-04-01 18:37:03 -07:00
Ben Alpert fae38f59d4 Build the website without publishing on PRs
To catch docgen errors.
2015-04-01 18:34:42 -07:00
Ben Alpert c6203f6516 Merge pull request #610 from spicyj/err
Die with status 1 when website generation fails
2015-04-01 18:34:27 -07:00
Ben Alpert d81cb6e162 Die with status 1 when website generation fails
So Travis won't wipe out the website on error. :\
2015-04-01 18:31:18 -07:00
Ben Alpert 443d44d8f4 Merge pull request #607 from spicyj/travis
Redo Travis config
2015-04-01 18:20:32 -07:00
Ben Alpert da9bd7b55a Redo Travis config
Now website build failures should fail the build, and Xcode tests should
run correctly (previously they didn't seem to be -- Travis was
complaining that no project or scheme was specified).
2015-04-01 18:15:47 -07:00
Christopher Chedeau adbdfe65e1 Merge pull request #606 from spicyj/server-fail
Fail generating website if any page throws an error
2015-04-01 17:57:25 -07:00
Ben Alpert efe446e962 Fail generating website if any page throws an error
This will prevent us from having pages on the site that just show error
stack traces -- instead, Travis will fail with an error and we'll
notice sooner.

I cleaned up this logic to use promises which was eay easier to track
for the error behavior and should be simpler to follow regardless.
2015-04-01 17:42:33 -07:00
Alexander Kotliarskyi bd8671f4da Merge pull request #577 from lvyile/patch-2
[Docs] Add missing #import "RCTLog.h"
2015-04-01 17:18:25 -07:00
Tadeu Zagallo 041770be62 Merge pull request #591 from facebook/revert-562-master
Revert "Fixed image link for AddToSearchPaths.png"
2015-04-01 20:50:33 +01:00
Tadeu Zagallo de8cfae5e4 Revert "Fixed image link for AddToSearchPaths.png" 2015-04-01 20:50:13 +01:00
Tadeu Zagallo 14475b7879 Merge pull request #590 from facebook/revert-561-patch-4
Revert "Fixed broken image for AddToBuildPhases.png"
2015-04-01 20:48:55 +01:00
Tadeu Zagallo 0024a1c0a0 Revert "Fixed broken image for AddToBuildPhases.png" 2015-04-01 20:47:20 +01:00
Christopher Chedeau a597809cfa Merge pull request #585 from tehfailsafe/patch-2
Add watchman permission errors
2015-04-01 12:16:13 -07:00
tehfailsafe 750c633940 Add watchman permission errors
Added quick reinstall --HEAD fix for watchman
https://github.com/facebook/watchman/issues/90
2015-04-01 10:22:08 -07:00
Amjad Masad f895ce7fbc 0.3.3 2015-04-01 09:26:46 -07:00
cclv 9c26742743 missing #import "RCTLog.h" 2015-04-01 21:41:39 +08:00
tako-black 399ace3733 change singular 2015-04-01 18:56:10 +09:00
tako-black 80baec3bb6 change words 2015-04-01 08:17:13 +09:00
Yang Li ab8d5cec83 Fix typo 2015-03-29 15:34:06 +11:00
tako-black fb9bacdf2d fix typo 2015-03-27 14:33:27 +09:00
191 changed files with 4763 additions and 2531 deletions
+1 -1
View File
@@ -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
View File
@@ -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="
+38 -28
View File
@@ -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
View File
@@ -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;
+2
View File
@@ -36,5 +36,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
</dict>
</plist>
+1 -1
View File
@@ -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;
+19 -24
View File
@@ -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}
/>
}
/>
+2 -1
View File
@@ -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,
+1 -1
View File
@@ -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({
+13 -31
View File
@@ -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;
+1 -1
View File
@@ -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;
@@ -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"
@@ -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;
@@ -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];
}
+7 -7
View File
@@ -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;
-40
View File
@@ -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;
-51
View File
@@ -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;
+32 -30
View File
@@ -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,
};
+8 -6
View File
@@ -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({
@@ -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 */,
@@ -12,6 +12,6 @@
#import "RCTBridgeModule.h"
@interface RCTAnimationManager : NSObject <RCTBridgeModule>
@interface RCTAnimationExperimentalManager : NSObject <RCTBridgeModule>
@end
@@ -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}
+19 -4
View File
@@ -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) {
+11 -2
View File
@@ -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',
});
+14 -295
View File
@@ -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}
+23 -17
View File
@@ -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
);
}
},
+1 -4
View File
@@ -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,
+1
View File
@@ -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 {
+263 -198
View File
@@ -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;
+3 -5
View File
@@ -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 */
+16 -9
View File
@@ -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
+11 -13
View File
@@ -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,
@@ -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;
};
+9 -7
View File
@@ -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;
+8 -7
View File
@@ -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]];
+2
View File
@@ -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 };
+4 -1
View File
@@ -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
+12 -15
View File
@@ -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
+28 -27
View File
@@ -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;
+4 -4
View File
@@ -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}]);
}
+3 -2
View File
@@ -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);
-70
View File
@@ -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;
+2
View File
@@ -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
+6 -7
View File
@@ -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;
});
+11 -19
View File
@@ -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;
+6 -3
View File
@@ -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);
}
@@ -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;
+120 -52
View File
@@ -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);
}
});
});
},
};
+1 -1
View File
@@ -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.
*
+3 -2
View File
@@ -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
+4 -2
View File
@@ -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
+44 -7
View File
@@ -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
+3 -2
View File
@@ -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
View File
@@ -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;
+10 -13
View File
@@ -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;
};
}
+19 -23
View File
@@ -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,
+21
View File
@@ -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;
-16
View File
@@ -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;
+3 -2
View File
@@ -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
View File
@@ -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'),
+28 -18
View File
@@ -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 Facebooks 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