Compare commits

...
306 Commits
Author SHA1 Message Date
James Ide 00079d2167 [0.10.1] Publish fix for new project 2015-08-28 12:58:54 -07:00
James Ide 31da1be640 Merge pull request #2474 from philikon/0.10-fix-headers-template
Fix header paths in Sample/template Xcode project to make a fresh project build again
2015-08-28 12:57:55 -07:00
Philipp von Weitershausen 816d919989 Print correct path to Xcode project after generating it 2015-08-27 17:04:23 -07:00
Philipp von Weitershausen 3365a6c4cf Fix header paths in Sample/template Xcode project to make a fresh project build again 2015-08-27 16:52:59 -07:00
James Ide 0b48e2d71a [0.10.0] Release 0.10.0 2015-08-26 16:46:13 -07:00
Ben Alpert 37386a8223 [ReactNative] Fix switching to null event listener 2015-08-26 16:39:09 -07:00
Ben Alpert 697e96bc10 [React Native] Fix infinite loop in css-layout
Summary:
Manual pick of https://github.com/facebook/css-layout/commit/9001a3d2 since we're still lagging behind the upstream. Fixes #1378.
2015-08-26 16:18:35 -07:00
James Ide b76ccca30a [Podspec] Remove AnimationExperimental subspec and fix Image subspec
Summary:
AnimationExperimental is gone; remove it. The Image subspec now depends on the networking code, so we have to pull in those files. These changes were necessary to publish 0.10.0-rc to CocoaPods, so would be convenient to get them into master for 0.11.

Fixes #2354
Closes https://github.com/facebook/react-native/pull/2341
Github Author: James Ide <ide@jameside.com>
2015-08-26 16:18:25 -07:00
James Ide 4facc5fc9c [Views] Change rasterization scale to the main screen's scale
Summary:
`view.screen` can be nil if the view has not yet been added to the view hierarchy (e.g. new view), so we should use `[UIScreen mainScreen]` instead.

In the future, if we need to support multiple screens, one possible fix is to set the rasterization scale in didMoveToWindow/Superview. For now we have just one screen, though.
Closes https://github.com/facebook/react-native/pull/2334
Github Author: James Ide <ide@jameside.com>
2015-08-26 16:17:41 -07:00
Nick Lockwood 720874fb10 Fix null values in network requests 2015-08-26 16:17:18 -07:00
James Ide d31d0141e4 [TextInput] Fix multiline TextInput so text and cursor stay in bounds
Summary:
With a multiline TextInput, the text is initially rendered correctly but once you try to edit it, the cursor slides off the top of the view and out of its bounds. Also if the TextInput is scrollable, you can scroll the text out of the bounds of the view, which looks buggy unless you clip the overflow. This occurs because the top content inset is applied to the RCTTextView instead of the UITextView's text container.

This diff fixes both bugs by applying the vertical insets to the UITextView's textContainerInset instead of the RCTTextView's frame (which is a wrapper around a real UITextView).

The left inset is still applied to the frame because there is a bug with the text rendering when the left textContainerInset is negative: the initial text doesn't show up until you focus the text view. The bug doesn't occur when setting the right textContainerInset, so I apply this workaround to only the left inset.

Closes https://github.com/facebook/react-native/pull/2297
Github Author: James Ide <ide@jameside.com>
2015-08-26 16:17:05 -07:00
James Ide 3051f9b888 [npm] Upgrade to stacktrace-parser 0.1.2, which supports io.js
Summary:
stacktrace-parser used to list only Node 0.10 under its list of supported engines. This new version includes Node 1.x and 2.x (i.e. io.js) as well, which addresses the warning during `npm install`.

There's no problem with using the older version of stacktrace-parser; this just clears the warning.

Closes https://github.com/facebook/react-native/pull/1738
Github Author: James Ide <ide@jameside.com>
2015-08-26 16:16:58 -07:00
Nick Lockwood e2e610dbe9 Fix ReactART 2015-08-26 16:16:23 -07:00
James Ide 2b0e8dd303 0.10.0-rc 2015-08-14 21:24:16 -07:00
James Ide 8ed0bafad2 [Podspec] Remove AnimationExperimental subspec and update Image subspec 2015-08-14 21:23:53 -07:00
James Ide 758ec76cb6 Merge pull request #2340 from chentsulin/patch-1
Add npm version badge
2015-08-14 20:55:09 -07:00
C. T. Lin 05b17a811e Add npm version badge 2015-08-15 11:42:33 +08:00
Christoph Pojer 5786a05e9e Merge pull request #2339 from ide/jest
[jest] Update jest-cli version to 0.5
2015-08-14 16:13:33 -07:00
James Ide 5153ce0a03 [jest] Update jest-cli version to 0.5
npm install will get a little nicer since it can read from npm instead of always having to redownload from github.
2015-08-14 14:49:37 -07:00
Tadeu Zagallo c9f5d229db Merge pull request #2323 from tadeuzagallo/Updates_Thu_13_Aug
Updates thu 13 aug
2015-08-13 14:50:09 +01:00
Tadeu Zagallo 12492d8d94 [ReactNative][Travis] Attempt to fix tests 2015-08-13 14:39:01 +01:00
Tadeu Zagallo eeb0adca43 [ReactNative][Website] Move bluebird to depencies instead of devDependencies 2015-08-13 14:10:56 +01:00
Tadeu Zagallo 28fb229266 [ReactNative] Fix e2e tests 2015-08-13 13:50:04 +01:00
Tadeu Zagallo 4daca092d2 [ReactNative][Website] Fix tests 2015-08-13 13:31:50 +01:00
Christopher Chedeau 5ebe0ed717 Bust jest caching and fix tests 2015-08-11 11:02:19 -07:00
Christopher Chedeau 0d636a017d Updates from Tue 11 Aug 2015-08-11 08:42:07 -07:00
Nick Lockwood 4d817245f7 Fix crash when using alert() polyfill due to null callback 2015-08-11 07:31:19 -08:00
Tadeu Zagallo e2d9ddce4c [ReactNative] Fix cast warning in RCTProfile
Summary:
Buck is more strict than our current xcodeproj configuration is keeps warning
about this implicit cast, it's quite annoying.
2015-08-11 06:49:39 -08:00
Nick Lockwood 48af214216 Simplified event registration
Summary:
Our events all follow a common pattern, so there's no good reason why the configuration should be so verbose. This diff eliminates that redundancy, and gives us the freedom to simplify the underlying mechanism in future without further churning the call sites.
2015-08-11 06:41:04 -08:00
Yusef Napora 2fe1ac2a83 [Image] Fix invalid CGContext when one dimension of destination Image size == 0
Summary:
Addresses #1534

When an image has a known width, but a height of 0 (which can happen if `flex: 1` is set on the `Image` element), `RCTDownloadManager` attempts to scale it to an invalid size, which results in a `NULL` `CGContextRef` and some scary warnings from UIKit:

```
 <Error>: CGContextTranslateCTM: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context  and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update.
```

This adds a check for zero width or height to the `RCTClipRect` function.  If either dimension is zero, it is calculated based on the aspect ratio of the source image.  This ensures that we don't try to create an invalid `CGContextRef`, and that images with an unknown dimension are still scaled, blended, etc.
Closes https://github.com/facebook/react-native/pull/2278
Github Author: Yusef Napora <yusef@napora.org>
2015-08-11 06:30:34 -08:00
Tom Hastjarjanto 2424a215e0 [Image] Fix default Image functionality
Summary:
In the latest 0.9.0-rc of React Native, the default image won't load due to a typo and a missing condition in `setImage`. This PR contains fixes for both of them.
Closes https://github.com/facebook/react-native/pull/2269
Github Author: Tom Hastjarjanto <tom@intellicode.nl>
2015-08-11 05:19:04 -08:00
Nick Lockwood f5d8e4d6e0 Fixed a strongSelf that was accidentally marked as __weak 2015-08-11 04:46:56 -08:00
Nick Lockwood 68f1a223fb Fixed bug when updaing props for RCTNetworkImageView 2015-08-11 04:19:28 -08:00
Evan Solomon 51ccdfa36a [Babel] Upgrade babel and regenerator to the latest version
Summary:
See https://github.com/babel/babel/commit/c0fd4c1f9e0b18231f585c4fa793e4cb0e01aed1
Closes https://github.com/facebook/react-native/pull/1753
Github Author: Evan Solomon <evan@evanalyze.com>

@allow-crlf-text
2015-08-10 20:37:39 -08:00
Dave Sibiski e163df3567 Adds process.env.NODE_ENV polyfill
Summary:
There are many libraries that use `NODE_ENV` to check whether the code is running in
"production" mode or not. This allows those library authors to not have to add conditionals
for React Native.

One such library is Redux: https://github.com/gaearon/react-redux/pull/40

Thanks to @brentvatne for providing the solution via this tweet (via his phone in the airport 😉): https://twitter.com/notbrent/status/630440250951749632

/cc @vjeux @gaearon @zpao @amasad
Closes https://github.com/facebook/react-native/pull/2279
Github Author: Dave Sibiski <dsibiski@gmail.com>
2015-08-10 19:51:05 -08:00
Martín Bigio a303a42e28 [react-packager] Add more granular Activity logging 2015-08-10 16:25:04 -08:00
Martín Bigio 2afeb8fbe6 [react-packager] Promote Cache to top level
Summary:
The cache is only used for JSTransformer at the moment. We're doing IO and some computation to get each module's name, whether is a haste or node module and it's dependencies. This work happens on startup so by caching this value we shouldbe able to reduce the start up time. Lets promote the Cache to the Packager level to be able to use it by any of the components of the packager. For now, on this diff we'll start using it to cache the mentioned fields.

Also we had to introduce the concept of fields in the cache as manually merging the date we had for each path is not possible as we're using promisses all around. With the new API, each field is a promise.

@amasad and I did some manual testing to measure the impact of this change and looks like it's saves 1 second when building the haste map (which represents 50% of the time). Overall this reduces 1 second of start up time which was currently about 8s on my mac book pro.
2015-08-10 16:25:03 -08:00
Christopher Chedeau 56059e92df Merge pull request #2288 from ide/iojs-docs
[docs] remove instructions to install node/io.js with brew
2015-08-10 15:46:36 -07:00
Nick Lockwood 5d20b9065d Fix RKHTTPRequestHandler crash 2015-08-10 13:46:47 -08:00
James Ide fd4e75644f [docs] remove instructions to install node/io.js with brew
nvm is way better than homebrew for managing node installations:

 - can install multiple versions
 - can choose which version of node to install
 - easy to clean up under ~/.nvm
 - there are still some rough edges with /usr/local on El Cap -- nvm avoids those altogether
2015-08-10 14:31:48 -07:00
Nick Lockwood 6e2e02ab08 Merge pull request #2281 from jacob-israel-turner/patch-2
Grammar
2015-08-10 22:00:03 +01:00
Nick Tuckett f309540f69 [react_native] JS files from D2327386: Simplified unimplemented SliderIOS on Android 2015-08-10 08:33:39 -08:00
Nick Lockwood b489c9aa43 Fixed perf issue due to use of regex in +[RCTConvert NSURL:] 2015-08-10 07:13:36 -08:00
Jacob Turner 5b5cadeb30 Grammar 2015-08-10 14:34:27 +01:00
Dorota Kapturkiewicz 59b974eafb [ReactNative] make AdsManager floater and TouchableHighlight accessible 2015-08-10 04:41:09 -08:00
Christopher Chedeau 50d5f86bf8 Merge pull request #2273 from vjeux/fix_az
[Website] Fix a-z order in props
2015-08-08 10:28:39 -07:00
Christopher Chedeau c1a0a72275 [Website] Fix a-z order in props 2015-08-08 10:28:05 -07:00
Nick Lockwood 94caefa9c2 Converted RCTDevLoadingView to a module and fixed url display when using delegate 2015-08-08 03:31:49 -08:00
Nick Lockwood b82ac9bf07 Reverted to pre-init of queues to fix UIExplorer tests. 2015-08-08 01:47:43 -08:00
Alex Kotliarskyi e21fb91786 [ReactNative] Show banner promoting DevTools
Summary:
When React DevTools is not installed, React prints a tiny warning to the console.
However, in debugger.html we have a lot of free space we could use to promote
React DevTools more actively.
2015-08-07 15:23:22 -08:00
Philipp von Weitershausen 0f5190e9f3 [React Native] Provide a way to get a list of registered apps in AppRegistry 2015-08-07 11:05:25 -08:00
Tadeu Zagallo 331e4a96e9 [ReactNative] Preload ModuleData queue
Summary:
The module's methodQueue wasn't being created when accessing the modules directly
on the bridge, without going through JS. Preload the queue for now to fix internal
breakages, but I'll figure out a better way to keep it lazy afterwards.
2015-08-07 11:05:09 -08:00
Ben Alpert 2d66e10ec4 [ReactNative] Delay sending touches to JS until dependent (native) gesture recognizers fail
Summary:
Previously, JS touches were being interpreted simultaneously with native gesture recognizers.
2015-08-07 10:56:26 -08:00
Alexander Kotliarskyi 3aec8ee72d Merge pull request #2267 from frantic/hide-npm-output
Hide npm output when doing react-native init
2015-08-07 11:37:49 -07:00
Felix Oghină 205a2c0bfc [react_native] JS files from D2318945: Implement a slider for React Native Android 2015-08-07 10:15:52 -08:00
Alex Kotliarskyi 2ddf3d942d Hide npm output when doing react-native init 2015-08-07 10:59:59 -07:00
Christopher Chedeau a91f44d7fd Merge pull request #2264 from vjeux/platform_props
[Website] Platform-specific props
2015-08-07 10:39:04 -07:00
Christopher Chedeau 6e9ceaa348 [Website] Platform-specific props
We are annotating platform-specific props with a special @platform tag in the docblocks. This pull request adds a little [ios] tag before the name and sorts the props such as generic ones are before platform-specific ones.
2015-08-07 10:31:07 -07:00
Tadeu Zagallo 6cd0709bc0 [ReactNative] Parellelise bridge startup
Summary:
Parallelise the bridge startup so we don't keep waiting for the source to load.
2015-08-07 06:48:30 -08:00
Nick Lockwood 3cef3010e6 Fix RCTAssert logic 2015-08-07 06:11:49 -08:00
Andy Street 1d452f3e4d [ReactNative] Remove testID from root view 2015-08-07 05:23:40 -08:00
Tj c933087567 CMD+R should post a refresh notification.
Summary:
For the sake of consistency, `cmd+r` should just post a refresh notification. This way other tools can also observe and react to the refresh without ugly hacks.
Closes https://github.com/facebook/react-native/pull/2175
Github Author: Tj <tfallon@mail.depaul.edu>
2015-08-07 04:39:21 -08:00
Vladislav Alexeev 9edad17a15 Use Dynamic Text Size Multipliers from Wilde in ReactNative Module 2015-08-07 03:18:20 -08:00
Nick Lockwood ffa7dbcbf1 Fixed onLayout redbox issue 2015-08-07 02:58:48 -08:00
Jason Zhekov a8211e32d2 Fix typo
Summary:
Closes https://github.com/facebook/react-native/pull/2055
Github Author: Jason Zhekov <jasssonpet@gmail.com>
2015-08-06 20:22:24 -08:00
Nick Lockwood deba13f698 Refactor RCTUIManager
Summary:
Moved the view creation & property binding logic out of RCTUIManager into a separate RCTComponentData class - this follows the pattern used with the bridge.

I've also updated the property  binding to use pre-allocated blocks for setting the values, which is more efficient than the previous system that re-contructed the selectors each time it was called. This should improve view update performance significantly.
2015-08-06 15:49:35 -08:00
James Ide 3309f6da80 Merge pull request #2252 from ide/iojs-docs
[Docs] Update setup instructions to install io.js 2.x instead of latest
2015-08-06 14:24:15 -07:00
James Ide 554f23276b [Docs] Update setup instructions to install io.js 2.x instead of latest
The ws module has not been updated to work with 3.x quite yet.
2015-08-06 14:22:45 -07:00
Martín Bigio aefdf82cdc [JSAppServer] Don't keep track of not found packages 2015-08-06 11:34:56 -08:00
Andrei Coman 327f84b967 [ReactNative] Sync [react_native] Implement TextInput editable 2015-08-06 11:07:06 -08:00
Tadeu Zagallo 48548fedbf [ReactNative] Fix RCTTextField crash on iOS7
Summary:
RCTTextField was its own delegate, that was causing a stall on the main thread,
see more here:
http://stackoverflow.com/questions/19758025/uitextfield-delegate-jumping-to-100-cpu-usage-and-crashing-upon-using-keyboard
2015-08-06 09:56:17 -08:00
Andrei Coman 0d981c06de [ReactNative] Sync [react_native] Text Input missing letters fix 2015-08-06 04:59:09 -08:00
Hedger Wang 8463625bd9 [Navigation]: Add a method to map the stack routes to an array.
Summary:
Add a method to map the stack routes to an array.
2015-08-06 02:01:59 -08:00
Thomas Aylott a6ff11f1d2 [react-packager] Fixes stack traces
Summary:
TransformErrors weren't showing stack traces, making it hard to debug problems in transformer code.
2015-08-05 12:46:30 -08:00
James Ide d4eac3ddca [RFC][Rendering] Add shouldRasterizeIOS to View components
Summary:
Exposes the `shouldRasterize` property. Fixes #1986.
Closes https://github.com/facebook/react-native/pull/2226
Github Author: James Ide <ide@jameside.com>
2015-08-04 15:37:38 -08:00
Tj d8f43105ed [ListView] Fix RCTScrollView stickyHeader touch passing.
Summary:
Point must be converted to the stickyHeader's coordinate space and then passed to the stickyHeaders hitTest:withEvent: method in order to be correctly routed to any subviews of the stickyHeader.

This resolves this [issue](https://github.com/facebook/react-native/issues/2075).
Closes https://github.com/facebook/react-native/pull/2224
Github Author: Tj <tfallon@Tjs-MBP.local>
2015-08-04 17:10:19 -08:00
Nick Lockwood f165bbaf4e Use +[RCTConvert NSURL:] everywhere instead of +[NSURL URLWithString:]
Summary:
RCTConvert's URL: method gracefully handles unescaped urls, local file urls, urls containing unicode, etc. URLWithString doesn't.
2015-08-04 16:26:16 -08:00
Hedger Wang 222594cb44 [Navigator] A method subtract to compute the diff between stack mutation.
Summary:
When mutation of a stack happens, we'd like to compute the diff of the stacks (before and after) so that
we can know which routes are removed in the new stack.

This diff adds a new method `substract` which does what we need.
2015-08-04 16:16:17 -08:00
Nick Lockwood 274769fd8c Added Unicode URL test 2015-08-04 15:37:05 -08:00
Nick Lockwood 9f81d6726c Merge pull request #2227 from harrykiselev/patch-1
Fix link to Apple Developer account registration.
2015-08-04 22:16:26 +01:00
Harry V. Kiselev 01deb0b03d Fix link to Apple Developer account registration. 2015-08-05 00:07:42 +03:00
Brent Vatne 257bbb5255 [Docs] Add newsletter to top-level header 2015-08-04 13:47:52 -07:00
Alex Akers bb883bfb61 Update offscreen image fix with window checking
Summary:
2015-08-04 08:05:12 -08:00
James Ide eab7b00cee [JSContext] Define ContextExecutor.setContextName for debugging
Summary:
Add a method that lets JS set the name of the JSContext for debugging purposes. I check `JSGlobalContextSetName` since it is not available on iOS 7.

Closes https://github.com/facebook/react-native/pull/2144
Github Author: James Ide <ide@jameside.com>
2015-08-04 06:08:14 -08:00
James Ide 47e1d1aef8 [Async] Enable async/await and update UIExplorer and tests
Summary:
- Enables async/await in .babelrc and transformer.js
- Adds regenerator to package.json. Users still need to explicitly require the regenerator runtime -- this is so that you only pay for what you use.
- Update AsyncStorage examples in UIExplorer to use async/await
- Update promise tests in UIExplorer to use async/await in addition to the promise API

Closes https://github.com/facebook/react-native/pull/1765
Github Author: James Ide <ide@jameside.com>
2015-08-04 05:35:13 -08:00
Amjad Masad 18452940f0 [react-packager] Add support for platform in the resolver
Summary:
Teach the resolver about platform-based resolution. The platform extension is inferred from the entry point.
It works for haste modules, as well as node-based resolution.
2015-08-03 18:29:20 -08:00
Christopher Chedeau afdeefc864 [Animated] Remove rebound dependency
Summary:
We already reimplement the spring computation, we were only using rebound for the tension/friction conversion. Turns out that it is quite small so we can just embed it. I'm doing this as I'm preparing for doing a web version of the feature and I'm trying to minimize the number of dependencies.

https://github.com/facebook/rebound-js/blob/master/rebound.js#L932
2015-08-03 15:24:40 -08:00
Nick Lockwood 6a4b83c16f Fixed retain cycle that caused RCTModuleMethods to leak
Summary:
Some of the log statements inside argument blocks in RCTModuleMethod were directly accessing ivars, thereby causing a retain cycle that retained the class. I've fixed this by making the access explicit via weakSelf.
2015-08-03 08:46:52 -08:00
Martin Konicek c63992437b [ReactNative] LayoutAnimation docs
Summary:
Improve https://facebook.github.io/react-native/docs/layoutanimation.html
2015-08-03 07:52:31 -08:00
=?UTF-8?q?=E9=9A=90=E9=A3=8E?= 44ce9194a2 Profiling will not work if the bundleURL has no port.
Summary:
If the bundleURL has no port(default 80) , the profile request URL will be `<scheme>://<host>:<null>/profile`, that will not work. So we should decide if the port exsits first.
Closes https://github.com/facebook/react-native/pull/2131
Github Author: =?UTF-8?q?=E9=9A=90=E9=A3=8E?= <yinfeng.fcx@alibaba-inc.com>
2015-08-03 07:51:33 -08:00
Alex Akers 3e21f39a77 Remove rendered RCTText contents when moving offscreen
Summary:
When an `RCTText` instance moves offscreen (possibly due to parent clipping), we unset the layer's contents until it comes onscreen again.
2015-08-03 05:43:16 -08:00
Alex Akers e9f69bc2e4 Reorganize SampleApp
Summary:
2015-08-03 03:32:03 -08:00
Dorota Kapturkiewicz e3ed572c01 [ReactNative] accessibility test app 2015-08-03 01:42:35 -08:00
Brent Vatne e2a2472af4 Merge pull request #2194 from ide/animation-docs
[Docs] Remove sections on AnimationExperimental and Pop
2015-08-02 16:16:13 -07:00
Brent Vatne 5222be0edf [Docs] Fix typo in docs index 2015-08-02 16:14:50 -07:00
James Ide 0b5e229ed2 Merge pull request #2205 from muan/title
[Docs] Add dynamic page title
2015-08-02 15:04:03 -07:00
Mu-An Chiou 129d1548a8 Add dynamic page title
Currently all the pages have the same title, making it hard to
tell which page is while when user has multiple tabs opened.
This adds 'title' to Site so a page title can be passed in and
prepended to the default title
2015-08-02 22:07:53 +08:00
James Ide bc56eb1439 Merge pull request #2201 from threepointone/patch-1
doc typo
2015-08-01 12:34:42 -07:00
Sunil Pai 5a918227f2 doc typo
match member names
2015-08-01 23:43:29 +05:30
Nick Lockwood 95d1fd142e Fixed null argument errors for timers and layout animations 2015-08-01 06:40:27 -08:00
Philipp von Weitershausen 3e79838a31 [React Native] sync JS for D2287297 2015-07-31 21:31:46 -08:00
Kevin Gozali ee8b50c955 [ReactNative] fix up XMLHttpRequest failures 2015-07-31 21:31:45 -08:00
James Ide 4a93d23af2 [Docs] Remove sections on AnimationExperimental and Pop
AnimationExperimental has been removed and Pop has been superseded by Animated.
2015-07-31 17:17:22 -07:00
Philipp von Weitershausen d858d0c4e0 [ReactNative][Android] JS for D2294884: Allow XHR to send native files, as HTTP body or via multipart FormData 2015-07-31 15:50:49 -08:00
Brent Vatne f41663a2df [WebView] Replace RKWebView with RCTWebView in log message
Summary:
Easy merge, just a small change in log message
Closes https://github.com/facebook/react-native/pull/2061
Github Author: Brent Vatne <brentvatne@gmail.com>
2015-07-31 13:49:14 -08:00
Nick Lockwood 1fb26d4319 Cleanup 2015-07-31 11:33:22 -08:00
Krzysztof Magiera b807c51bbe [ReactNative] Fix TextInput Focus issue 2015-07-31 11:03:41 -08:00
Vladislav Alexeev 53fb5b6cee Dynamic Text Sizes for Text component
Summary:
Dynamic Text Sizes for Text component.
Text gains new prop - allowFontScaling (false by default).
There is also AccessibilityManager module that allows you to tune multipliers per each content size category.
2015-07-31 07:42:41 -08:00
Nick Lockwood 407eb4ce85 NSNumber arguments must now be nonnull
Summary:
The bridge implementation on React Android does not currently support boxed numeric/boolean types (the equivalent of NSNumber arguments on iOS), nor does Java support Objective-C's nil messaging system that transparently casts nil to zero, false, etc for primitive types.

To avoid platform incompatibilities, we now treat all primitive arguments as non-nullable rather than silently converting NSNull -> nil -> 0/false.

We also now enforce that NSNumber * objects must be explicitly marked as `nonnull` (this restriction may be lifted in future if/when Android supports boxed numbers).

Other object types are still assumed to be nullable unless specifically annotated with `nonnull`.
2015-07-31 06:57:08 -08:00
Eric Vicenti 41dd6fe6ea [ReactNative][Navigator] Fix disabled scene height change
Summary:
Disabling the scene this way would make the scene height go to zero and mess up the scroll position. By setting the bottom to the same distance, the view does not get resized and the scroll position is preserved through a scene disable cycle.
2015-07-30 12:58:45 -08:00
Christopher Chedeau b10a1549c6 Merge pull request #2179 from vjeux/fix_flow
Install flow from npm/flow-bin instead of brew
2015-07-30 13:39:22 -07:00
Christopher Chedeau c627600ef3 Try to fix flow 2015-07-30 13:24:14 -07:00
Alex Akers f2b3057a7c Fix border width precedence issue 2015-07-30 06:18:17 -08:00
Felix Oghina 90b80e375c [reactnative] add launchAndroidPackager.command 2015-07-30 03:35:29 -08:00
Tadeu Zagallo cf22111cee [ReactNative] Prevent RCTDevLoadingView from creating UIWindow on unit tests
Summary:
`RCTDevLoadingView` was being created from a constructor function and creating
a `UIWindow` when the bridge started loading. The `UIWindow` was crashing on
the unit tests since there's no host app.
2015-07-30 03:10:53 -08:00
Dorota Kapturkiewicz 8a272bc873 [ReactNative] implement accessibilityLiveRegion and accessibilityComponentType 2015-07-30 01:40:37 -08:00
Gabe Levi d8a13ec3d5 [Flow] Deploy v0.14.0 2015-07-29 20:29:44 -08:00
Philipp von Weitershausen 4bfeeaa90d [ReactNative] Improve Flow definitions and code comments in XHR FormData polyfill 2015-07-29 20:03:57 -08:00
Zack Gomez 60c8abcc70 Revert "[ReactNative] revert "prevent ScrollView content offset from changing during layout"" 2015-07-29 17:23:18 -08:00
Hedger Wang 4b16e4d550 [Navigator]: Add a method keyOf to NavigationRouteStack.
Summary:
# Summary

Add a method `keyOf` to NavigationRouteStack.

The method `keyOf` returns a key that is associated with the route.
The a route is added to a stack, the stack creats an unique key for it and
will keep the key for the route until the route is rmeoved from the stack.

The stack also passes the keys to its derived stack (the new stack created by the
mutation API such as `push`, `pop`...etc).

The key for the route persists until the initial stack and its derived stack no longer
contains this route.

# Why Do We Need This?

Navigator has needs to use an unique key to manage the scenes rendered.
The problem is that `route` itself isn't a very reliable thing to be used as the key.

Consider this example:

```
// `scene_1` animates into the viewport.
navigator.push('scene_1');

setTimeout(() => {
 // `scene_1` animates off the viewport.
 navigator.pop();
}, 100);

setTimeout(() => {
 // Should we bring in a new scene or bring back the one that was previously popped?
 navigator.push('scene_1');
}, 200);

```

Because we currently use `route` itself as a key for the scene, we'd have to block a route
until its scene is completely off the components tree even the route itself is no longer
in the stack otherwise we'd see strange animation of jumping scenes.

# What's Next

We're hoping that we can build pure reactive view for NavigationRouteStack easily.
The naive implementation of  NavigationRouteStackView may look like this:

```
class NavigationRouteStackView {
  constructor() {
    this.state = {
      staleScenes: {},
    };
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.stack !== this.props.stack) {
      var stale;
      var staleScenes = {...this.state.staleScenes};
      this.props.stack.forEach((route, index, key) => {
        if (nextProps.stack.keyOf(route) !== key) {
          stale = true;
          staleScenes[key] = {route, index, key, stale};
        }
      });
      if (stale) {
        this.setState({
          staleScenes,
        });
      }
    }
  }

  render() {
    var scenes = [];

    this.props.stack.forEach((route, index, key) => {
      scenes.push({route, index, key});
    });

    Object.keys(this.state.staleScenes).forEach(key => {
      scenes.push(this.state.staleScenes[key]);
    });

    scenes.sort(stableSortByIndex);

    return <View>{scenes.map(renderScene)}</View>;
  }
}

```
2015-07-29 16:55:21 -08:00
Christopher Chedeau fc9150c674 Merge pull request #2169 from ide/iojs-docs
[Docs] Explain more about nvm in the io.js setup docs
2015-07-29 17:40:41 -07:00
James Ide de13b59dd2 [Docs] Explain more about nvm in the io.js setup docs
Clarify that io.js is the modern version of Node and explain a little more about how to get set up with nvm, and that it lets you switch between multiple io.js/node versions.
2015-07-29 17:29:18 -07:00
Philipp von Weitershausen 37636fc59a [React Native] open source ImageEditingManager native module 2015-07-29 15:57:48 -08:00
James Ide 0f32bea634 Merge pull request #2143 from milieu/patch-1
Fix broken links in docs/Testing.md
2015-07-29 13:55:22 -07:00
Hedger Wang 809a2dc1d6 [Navigator: Prevent user from over-popping the routes.
Summary:
If user taps the back button quickly, the app crashes becuase "pop"
internally only checks `this.state.presentedIndex` which does not
always update when transtion happens.

This diff addresses this issue.
2015-07-29 11:42:34 -08:00
Jing Chen 8416691719 [events] Move start point of EventsDashboard to work with early boot 2015-07-29 10:47:13 -08:00
Pieter De Baets d6de865236 Cleanup AppRegistry types
Summary:
Found some missing/wrong types when playing around with the AppRegistry for routing work in the main app.
2015-07-29 09:55:24 -08:00
Dorota Kapturkiewicz e073fdc4c7 [ReactNative] add accessibilityComponentType 2015-07-29 09:20:23 -08:00
Felix Oghina dfa43f0ce5 [react_native] JS files from D2282690: make back button work in launcher app 2015-07-29 06:35:46 -08:00
Nick Lockwood 95b9dd3a88 Added support for method argument nullability
Summary:
This diff adds support for enforcing nullability in the arguments for exported methods.

We previously supported use of the nullable/nonnull attributes on method arguments, but didn't do anything to ensure that they were respected.

Now, if an argument is marked as nonnull, and a null value is sent for that argument, it will display a redbox.

In future, nonnull will be assumed by default, but for now we assume that un-annotated arguments can be null (to avoid breaking existing code).
2015-07-29 05:50:27 -08:00
Ben Alpert e0ea046092 [ReactNative] Fix ResponderEventPlugin after React upgrade 2015-07-29 02:47:55 -08:00
Kevin Gozali 87ce2d635b [ReactNative] revert "prevent ScrollView content offset from changing during layout" 2015-07-28 19:39:56 -08:00
Kevin Gozali f1b22e87f1 [madman] version mobile configs disk caching 2015-07-28 18:11:34 -08:00
Tadeu Zagallo 127f7095dc [ReactNative] Add RCTBridgeDelegate
Summary:
Add a new bridge delegate protocol to allow a more flexible bridge configuration.

For now it just support the pre-existent configurations + providing the JavaScript
source to the bridge, that should allow pre-loading sources.
2015-07-28 15:57:02 -08:00
Zack Gomez 068f396c9b [ReactNative] prevent ScrollView content offset from changing during layout
Summary:
-[UIScrollView setFrame:] changes the content offset, which we want to do manually.  Preserve the old offset during layout.
2015-07-28 14:36:27 -08:00
Nick Lockwood b812b0ee2e Added unit tests for RCTModuleMethod parsing, and fixed some edge cases 2015-07-28 13:07:52 -08:00
Alex Kotliarskyi e964e741ef [RN] Add loading view for development mode
Summary:
It's very useful to know where the app is being loaded from and
can help debug various issues.
2015-07-28 11:17:06 -08:00
Martin Konicek 5164aa290b Merge pull request #2138 from mkonicek/master
[docs] Make it clear which Guides are platform-specific
2015-07-28 17:42:12 +01:00
Alex Akers 7d19ff3dcb Add <Modal /> component
Summary:
Create Modal component that can be used to present content modally.
2015-07-28 07:21:50 -08:00
Tadeu Zagallo f53c95c743 [ReactNative] Proxy bundleURL on RCTBatchedBridge
Summary:
Fixes #2126

`RCTBatchedBridge` didn't implement `bundleURL`, that was available on the public
bridge interface, so it'd always be `nil`, and setting it would just be ignored.
2015-07-28 05:59:45 -08:00
Martin Konicek 7fcd85cc53 Merge pull request #2145 from facebook/404-fix
Adding 404.js to catch 404s on website
2015-07-28 14:38:26 +01:00
Matthew Johnston 7a806f6d89 Update 404.js 2015-07-28 14:34:47 +01:00
Matthew Johnston 379444515b Adding 404.js to catch 404s on website 2015-07-28 14:22:13 +01:00
Meredith Leu f502e17cc7 Fix broken links 2015-07-27 18:34:12 -07:00
Alexander Kotliarskyi 757d6d204a Merge pull request #2136 from frantic/updates-2015-07-27
Updates 2015 07 27
2015-07-27 15:10:57 -07:00
Nick Lockwood 1d852624fd Refactored RCTImageDownloader to use RCTNetworking instead of a separate download system 2015-07-27 13:47:52 -08:00
Alex Kotliarskyi 62b7920b9d More updates 2015-07-27 14:47:32 -07:00
Ben Alpert 3cff9be3d1 [ReactNative] Update project template .flowconfig 2015-07-27 13:41:55 -08:00
Bill Fisher 3b83853713 [ReactNative] fix onMoveShouldSetPanResponderCapture
Summary:
Typo in implementation prevented onMoveShouldSetPanResponderCapture from working.
2015-07-27 12:06:40 -08:00
Rui Chen 842ce51099 [Treehouse RN] Make smoothScrollTo works by not calling it twice 2015-07-27 11:25:28 -08:00
Alex Kotliarskyi 9649e7cc8d More updates 2015-07-27 12:24:01 -07:00
Alex Kotliarskyi 58a403d3c8 [ReactNative] Pin babel version
Summary:
Currently minor version babel updates add and remove transforms, but internal
version is checked in and pinned to 5.6.4. Until we figure out how to update
internal deps systematically, we need to make sure OSS edition of RN matches
internal, otherwise we get test failures due to package version mismatches.
2015-07-27 11:20:22 -08:00
Christopher Chedeau 18e6094cab [ReactNative] Add overflow to the whitelisted Image props
Summary:
For some reason we're now spamming the logs everytime we render an Image because overflow is not defined in the whitelist. overflow: 'hidden' is needed for network images with cover mode.

The way we currently define those is not optimal where we try to factor as many things as possible into distinct propTypes. However for Text we're not even using this but we are getting all the ones from View (which many do not apply) and remove some that aren't needed.

It may be useful to cleanup this in the future but in the short term, it's better to remove this warning that doesn't have much value anyway.
2015-07-27 11:06:27 -08:00
Martin Konicek 33e62f71f4 [ReactNative] CameraRoll docs
Summary:
In preparation for open sourcing React Native for Android, document which parts of the `CameraRoll` API are platform-specific.

Renders like this: http://imgur.com/rbpXHKf

We should improve docs generation for `@param`s.
2015-07-27 09:46:35 -08:00
Martin Konicek b7253dc604 [ReactNative] PixelRatio docs
Summary:
Update docs for `PixelRatio` in preparation for open sourcing React Native for Android.

See http://stackoverflow.com/questions/11581649/about-android-image-and-asset-sizes
2015-07-27 09:40:08 -08:00
Martin Konicek 01911e5e06 [docs] Make it clear which Guides are platform-specific
This is in preparation for open sourcing React Native for Android.
Also add hyphens to URLs for consistency. This can break people's
browser bookmarks but it's better to be consistent and we can
redirect to docs: https://github.com/facebook/react-native/issues/2137

Test plan: Ran the website locally using `cd website; npm start`
and checked all pages render correctly.
http://i.imgur.com/RrPNgRr.png

Will update "Getting Started", "Tutorial", "Debugging" and
"Testing" separately.
2015-07-27 18:17:54 +01:00
Nick Lockwood 09bb761b25 Fixed RCTDownloadTaskWrapper crash on iOS7
Summary:
This is a quick fix for the RCTDownloadTaskWrapper crashing on iOS 7. The issue was that the object returned by -[NSURLSession downloadTaskWithURL:] on iOS was not actually a subclass of NSURLSessionTask, so the category that adds associated blocks was not working. I've fixed that by making it a category on NSObject instead.
2015-07-27 09:02:38 -08:00
Alex Kotliarskyi 2eb401a496 Updates from 2015-07-27 2015-07-27 10:01:30 -07:00
Nick Lockwood 7996c32211 Unregistered modules will now only error when called, not on bridge init
Summary:
Occasionally people create RCTBridgeModule subclasses or base classes that are not intended to be accessed from JS, and so they don't export them. This was previously flagged as an error by the system. I've now downgraded this error to a warning at startup, and deferred the redbox error until the module is directly accessed by native or JS code.
2015-07-27 08:58:47 -08:00
Nick Lockwood 2c5290946b Converted RCTImageLoader to be a bridge module 2015-07-27 08:52:20 -08:00
Alex Akers d178e27939 Fix UIExplorer freezing / persistence 2015-07-27 08:35:34 -08:00
Nick Lockwood f9abb5aae2 Fixed missing redbox at startup
Summary:
Fixed bug where redbox errors thrown during startup would be dismissed as soon as JS loaded, so they would never be seen.
2015-07-27 07:23:08 -08:00
Nick Lockwood 0e5422f36c Fixed text highlight alignment when text has nonzero padding.
Summary:
Previously the text highlight overlay did not take padding into account in its positioning, so it would be misaligned for padded text. This fixes that.
2015-07-27 07:11:29 -08:00
Martin Konicek c3d194d1f8 [ReactNative] TextInput docs 2015-07-27 06:45:13 -08:00
Martin Konicek f69e33e6a8 [ReactNative] Text docs
Summary:
In preparation for open sourcing React Native for Android, document which Text props are platform-specific.
2015-07-27 06:38:27 -08:00
Nick Lockwood aca76c75dd Fix gzip on iOS 9 2015-07-27 01:18:40 -08:00
Spencer Ahrens f1bd1cbc94 [ReactNative] cleanup native components 2015-07-24 18:54:35 -08:00
James Ide 53222f0dda [Packager] Include Content-Type headers with bundle and source maps
Summary:
The packager did not send back the Content-Type headers. Adding these.

Closes https://github.com/facebook/react-native/pull/2029
Github Author: James Ide <ide@jameside.com>
2015-07-24 18:46:17 -08:00
Nick Lockwood 5f0317291b Switched to OSS <Text> implementation for internal apps 2015-07-24 17:30:43 -08:00
Hedger Wang ea5276ed24 [Navigator] Port navigation APIs from Navigator to NavigationContext 2015-07-24 17:10:41 -08:00
James Ide c8373d2ad6 [Chrome Debugger] Update ws dependency to 0.7.2
Summary:
ws 0.7.2 officially supports io.js

Closes https://github.com/facebook/react-native/pull/2013
Github Author: James Ide <ide@jameside.com>
2015-07-24 15:52:49 -08:00
James Ide bf7e2a85d0 [Navigator] Vertically hide disabled scenes and use pointerEvents="none"
Summary:
Hides disabled scenes using `top` instead of `left`, which fixes a bug with the native UITabBar. When the UITabBar's width is zeroed because the scene has `left: SCREEN_WIDTH, right: 0` applied, this triggers a bug with the kerning of the tab titles. Instead, zeroing the height by setting `top: SCREEN_HEIGHT` avoids the bug.

Also applies `pointerEvents="none"` to disabled scenes so that views in the off-screen scenes definitely don't receive touches, which was occurring before.

Fixes #1401, fixes #2011

Closes https://github.com/facebook/react-native/pull/2104
Github Author: James Ide <ide@jameside.com>
2015-07-24 14:45:19 -08:00
Martin Konicek d10e4dbf0f [ReactNative] Use ASCII double quotes
Summary:
We mix ASCII double quotes with left and right quotes: https://github.com/facebook/react-native/blob/master/Libraries/CustomComponents/ListView/ListView.js#L13

Let's be consistent.
2015-07-24 13:16:25 -08:00
Michael Mitchell a1612a7dd2 [ReactNative] Delete AnimationExperimental and AnimationUtils
Summary:
AnimationExperimental is deprecated and being removed in favor of the Animated library.
Refer to https://facebook.github.io/react-native/docs/animations.html for information on how to use Animated
and https://facebook.github.io/react-native/docs/animations.html#animationexperimental-deprecated for the reasoning behind this.
2015-07-24 10:29:44 -08:00
Matej Hamas 000ab1139f [ReactNative] Adding clear function to the react native TextInput component. 2015-07-24 09:46:01 -08:00
Nick Lockwood 81dd9c27ea Optimized property setting and conversion 2015-07-24 09:37:28 -08:00
Tadeu Zagallo 4499f28c1d [ReactNative] Remove wrong lock from RCTProfile
Summary:
Remove sad lock from RCTProfileForwardInvocation that was locking concurrent
calls and messing with the profiler.
2015-07-24 09:14:46 -08:00
Nick Lockwood ef5cec4f08 Text highlighting on iOS
Summary:
This diff implements highlighting of tapped text subranges for the iOS `<Text>` component, styled to match how iOS webkit views highlight links (translucent grey overlay with rounded corners).

Highlighting is enabled by default for any `<Text>` component which has an onPress handler. To disable the highlight, add `suppressHighlighting={true}` to the component props.
2015-07-24 08:41:58 -08:00
Martin Konicek 7c87952e43 [ReactNative] Image docs
Summary:
In preparation for open sourcing React Native for Android, document which Image props are platform-specific.
2015-07-24 06:21:49 -08:00
lh_wang 1b06e41c83 fix examaple TabBarIOSExample bug; show click num when user click second & third tabbar
Summary:
Closes https://github.com/facebook/react-native/pull/1522
Github Author: lh_wang <lh_wang@Ctrip.com>
2015-07-23 19:12:42 -08:00
Rui Chen accf6f12e4 Make the scrollResponderScrollNativeHandleToKeyboard works on Android 2015-07-23 18:14:43 -08:00
Ben Alpert e01f90784f [ReactNative] Update core RN modules to work with React 0.14-beta1 2015-07-23 18:07:59 -08:00
Ben Alpert 710d29efae [ReactNative] Update React to 0.14-beta1 2015-07-23 18:07:58 -08:00
Jared Forsyth 9e4af68d91 [react-native] enable react devtools from JavascriptCore 2015-07-23 17:14:09 -08:00
Mark Miyashita 10ffe170c2 [StatusBar] Add ability to toggle the network activity indicator in the status bar + example in UIExplorer
Summary:
Added the ability to turn on and off the network activity indicator using:

```
StatusBarIOS.setNetworkActivityIndicatorVisible(true)
```
and
```
StatusBarIOS.setNetworkActivityIndicatorVisible(false)
```

Also added an example to the UIExplorer example app.

Fix #986
Closes https://github.com/facebook/react-native/pull/2079
Github Author: Mark Miyashita <negativetwelve@gmail.com>
2015-07-23 16:22:56 -08:00
Christopher 3fa3963465 [MovieApp] Add key to image components inside of MovieCell.
Summary:
Closes https://github.com/facebook/react-native/pull/2065
Github Author: Christopher <hello@blick-labs.com>
2015-07-23 15:09:42 -08:00
Andrei Coman aefb3d3628 [ReactNative] Use new animation library for 2048 game demo 2015-07-23 12:36:31 -08:00
Alexander Kotliarskyi a803e3217e Merge pull request #2105 from frantic/updates-23-jul
Updates 23 jul
2015-07-23 13:21:22 -07:00
Alex Kotliarskyi 03dccfbf71 Updates from Thu, July 23 2015-07-23 13:09:48 -07:00
Daniel Brockman 064dafa618 #!/bin/bash => #!/usr/bin/env bash
Summary:
This change makes `npm start` work correctly on e.g. NixOS.
Closes https://github.com/facebook/react-native/pull/2006
Github Author: Daniel Brockman <daniel@brockman.se>
2015-07-23 11:17:22 -08:00
Andrei Coman 9c8ba9502c [react_native] Refactor UIExplorerList to fix UIExplorerApp 2015-07-23 06:58:18 -08:00
Martin Konicek 19a8eff668 [ReactNative] ScrollView docs
Summary:
In preparation for open sourcing React Native for Android, document which ScrollView props are platform-specific.
2015-07-23 06:56:46 -08:00
Nick Lockwood 9d19829742 Refactored networking logic out into RCTDownloadTask 2015-07-23 04:00:31 -08:00
Christopher Chedeau 6fea7c2846 [ReactNative] Remove all the flow errors 2015-07-22 18:11:09 -08:00
Christopher Chedeau fea2db42fd [Animated] Send a final update with toValue for spring
Summary:
Animated.spring is not guarantee to stabilize at exactly toValue (determined by restDisplacementThreshold). It is a bit annoying that the last value is not toValue, it makes the logs harder to read and also prevents you from writing code like value === toValue. Instead you need to track it down somewhere else.
2015-07-22 17:33:00 -08:00
Christopher Chedeau b34892eb80 [ReactNative][BREAKING_CHANGE] Remove cloneElement from TouchableBounce 2015-07-22 17:22:35 -08:00
Jared Forsyth e06af51cf9 [react-native] inspector + devtools, naming things 2015-07-22 16:50:32 -08:00
Spencer Ahrens a671c58189 Merge pull request #2094 from sahrens/Updates_from_thurs_July_23rd
Updates from Thursday July 23rd
2015-07-23 01:39:57 +02:00
Rui Chen 93bbc6482d Unbreak the dismissing keyboard behavior on Android 2015-07-22 15:24:42 -08:00
Spencer Ahrens bb7aa500d7 Updates from Thurs July 23rd 2015-07-23 01:16:05 +02:00
Spencer Ahrens cec5360f1b [RN] Introduce initialValue prop to fix TextInputExamples
Summary:
Some of the examples relied on the fact that TextInput wasn't a controlled
component before.  This introduces a new `initialValue` prop which behaves the
way the `value` prop used to - that is, you could type without updating it and
it wouldn't get reset, thus acting as just an initial value - and switches the
examples to use it where appropriate.
2015-07-22 14:21:16 -08:00
Tadeu Zagallo 49b55804b1 [ReactNative] Fix crash when reload during profile (attempt #2)
Summary:
Fixes #1642

When reloading during profiling, the profile wouldn't unhook from the instance
being deallocated.
2015-07-22 11:02:32 -08:00
Hedger Wang 8dd1256c25 [Navigation] Implements NavigationContextFuture 2015-07-22 10:50:51 -08:00
Hedger Wang 280347d66a [Navigator]: Add method indexOf and slice to NavigationRouteStack 2015-07-22 10:46:57 -08:00
Felix Oghina 214c47ed76 [reactnative] move .android.js files to oss 2015-07-22 08:44:07 -08:00
Brent Vatne 75b220d6ce Merge pull request #2081 from MattFoley/patch-1
Enum Constants Explanation
2015-07-21 20:57:14 -07:00
Rui Chen 4e33b801ca Allow Animated.parallel to accept empty element in the array 2015-07-21 15:32:08 -08:00
Tj 1e5f22dc9c Updating for Code Review
Using UIStatusBarAnimation for real world example
2015-07-21 18:11:58 -04:00
Eric Vicenti 35aa9f3b97 [ReactNative] Remove idStack from Navigator
Summary:
We want to transition to NavigationRouteStack, so we need to simplify and remove all uses of idStack
2015-07-21 13:25:42 -08:00
Tj 4d3c3e625e Enum Constants Explanation
Explains the use of `RCT_ENUM_CONVERTER` for using `NS_ENUM` arguments in exported functions
2015-07-21 16:46:40 -04:00
Spencer Ahrens 961c1eb429 [ReactNative] TextInput bug fixes and features
Summary:
This introduces event counts to make sure JS doesn't set out of date values on
native text inputs, which can cause dropped characters and can mess with
autocomplete, and obviates the need for the input buffering which added lag and
complexity to the component.  Made sure to test simulated super-slow JS text
event processing to make sure characters aren't dropped, as well as typing
obviously correctable words and making sure autocomplete works as expected.

TextInput is now a controlled input by default without causing any issues for
most cases, so I removed the `controlled` prop.

Fixes selection state jumping by restoring it after setting new text values, so
highlighting the middle of some text in the new ReWrite example and hitting
space will replace that selection with an underscore and keep the cursor at a
sensible position as expected, instead of jumping to the end.

Ads `maxLength` prop to support the most commonly needed syncronous behavior:
preventing the user from typing too many characters.  It can also be used to
prevent users from continuing to type after entering special characters by
changing it to the current length after a regex match.  Made sure to verify it
works well with pasted input (including in the middle of existing text),
truncating it and collapsing the selection the same way it does on the web.

Fixes bug in TextEventsExample where it wouldn't show the submit and end events,
even though there were firing correctly.
2015-07-21 12:45:07 -08:00
Eric Vicenti 4f904b5d68 [ReactNative] Remove Navigator onItemRef
Summary:
Re-landing D2229686 after fixing bugs mentioned in D2250586

onItemRef is old and no longer needed now that the parent renders the scenes. This removes it from Navigator and all of our clients.

This is a breaking change to users of Navigator, but it is easy to transition to a ref in renderScene instead
2015-07-21 09:28:29 -08:00
Alex Akers 98ab7581a5 Remove images from offscreen image views 2015-07-21 09:02:28 -08:00
Nick Lockwood 85cb35c514 Fixed rotation and scaling issues when loading ALAssets using RCTImageLoader 2015-07-21 05:32:49 -08:00
Adam Roth 9c73e2ff7a [Image] Improved loading of Assets Library and Photos Framework images.
Summary:
Update to https://github.com/facebook/react-native/pull/1969

--
Recent improvements allow RCTImageLoader to select a more appropriate sized image based on the layout dimensions. Sizes:

	- asset.thumbnail
	- asset.aspectRatioThumbnail
	- asset.defaultRepresentation.fullScreenImage
	- asset.defaultRepresentation.fullResolutionImage

Prior, only the fullResolutionImage was used. This was memory intensive and resulted in crashes when loading several large images at once. The updated implementation works well, but can be made more efficient:

Consider loading 10 8MP (3264x2448) images in 150x150 pixel containers. The target size (150x150) is larger than asset.thumbnail (approx 100x100), therefore the fullScreenImage representation is used instead (approx 1334x1000).

This commit will scale the asset to the minimum size required while taking into account original aspect ratio and device scale. Memory usage is considerably lower and many more images can be loaded in
sequence without having to worry
Closes https://github.com/facebook/react-native/pull/2008
Github Author: Adam Roth <adamjroth@gmail.com>
2015-07-21 05:32:48 -08:00
Philipp von Weitershausen 9c5fe3612d [React Native][iOS] XHR upload progress events 2015-07-20 22:48:55 -08:00
Philipp von Weitershausen 151ddd9e42 [React Native] open source ImageStoreManager native module and plug into RCTImageLoader 2015-07-20 22:48:54 -08:00
Hedger Wang 6e2f07fb81 [Navigator] Add a callback that is called after emitting an event.
Summary:
While adeveloper requests the emitter to emit an event, the emitter
may not emit the event immediately instead of putting the request
into a queue and process it later.

This diff allows the developer to provide a callback which will be called
when the event has been emitted.

For instance:

```
class NavigationContext {
  push(nextRoute) {
    var nextStack = this._stack.push(nextRoute);
    this.emit(
      'change',
      {
        reason: 'push',
        nextStack: nextStack,
        nextRoute: nextRoute,
      },
      this._onPush
    );
  }

  _onPush(event){
    if (event.defaultPrevented) {
      return;
    }
    this._stack = event.nextStack;
    this.emit('change');
  }
}
```
2015-07-20 21:49:58 -08:00
Alex Kotliarskyi 9105b22925 [RN] Remove 14 more unused node_modules 2015-07-20 20:00:27 -08:00
Alex Kotliarskyi e54e452026 [RN] Remove extra copy of stacktrace-parser 2015-07-20 20:00:19 -08:00
Alex Kotliarskyi bf7497a418 [RN] Move ws to react-native-github 2015-07-20 20:00:18 -08:00
Tadeu Zagallo 270e09d46e [ReactNative][Profiler] Fix NSProcessInfo instacrash on iOS7
Summary:
`NSProcessInfo operatingSystemVersion` was being used to check the system version
for the Legacy Profiler on `RCTContextExecutor` but it's only available on iOS8+

Change it to `[UIDevice systemVersion]`
2015-07-20 19:36:13 -08:00
Christopher Chedeau 725053acfe [Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated
Summary:
Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great.

The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages:
- We no longer use cloneElement so we're no longer messing up with elements that are not our own.
- Refs are now working correctly for children elements
- No longer need to enforce that there's only one child and that this child is a native element

The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process.

I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well.

**Upgrade Path:**

If the child is a View, move the style of the View to TouchableOpacity and remove the View itself.

```
<TouchableOpacity onPress={...}>
  <View style={...}>
    ...
  </View>
</TouchableOpacity>

-->

<TouchableOpacity onPress={...} style={...}>
  ...
</TouchableOpacity>
```

If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 16:44:36 -08:00
Matheus Santos 6213950334 Added transitions 'VerticalUpSwipeJump' and 'VerticalDownSwipeJump' in…
Summary:
… NavigatorSceneConfigs
Closes https://github.com/facebook/react-native/pull/1822
Github Author: Matheus Santos <matheus.santos@struct.com.br>
2015-07-20 12:53:54 -08:00
Martín Bigio 95dc38929c [rn] revert D2201593 2015-07-20 11:48:08 -08:00
Martín Bigio 0d91b93aba [LAUNCH-BLOCKER] Revert JS callsite of D2201593 2015-07-20 11:48:07 -08:00
Tadeu Zagallo 72f7a1be6f [ReactNative] Add JavaScriptCore legacy profiler 2015-07-20 09:58:45 -08:00
Nick Lockwood 5db42643cf Added JSONKit support
Summary:
React Native will now use JSONKit if it's already available in the project, otherwise it will fall back to using NSJSONSerialization as before. This provides a small performance boost to JSON parsing in some cases.
2015-07-20 09:52:31 -08:00
Spencer Ahrens e3afc4efd8 Merge pull request #2049 from negativetwelve/docs/animated-input-events
[Docs] Fix Animated example for onScroll and onPanResponderMove to match paragraph description
2015-07-20 19:25:23 +02:00
Tadeu Zagallo f2d65ea85b [ReactNative] Fix RCTJavaScriptContext deallocation (attempt #2)
Summary:
The context wasn't being explicitly released before, since it'd be immediately
released. Now that the executors are bridge modules, it was only being deallocated
when the modules were released, what caused the threads to not be released at all.
2015-07-20 02:40:05 -08:00
Mark Miyashita f3dd07531c [Docs] Animated example for onScroll and onPanResponderMove should match the description in the paragraph above 2015-07-19 17:05:25 -07:00
Elliot Lynde 4a262e0f2b [React Native][Pokes Dashboard] Fix crash 2015-07-17 17:00:47 -08:00
Eric Vicenti c28d33f3a2 [ReactNative] Remove dependencies on Navigator idStack
Summary:
idStack is going away soon. This removes all references to it. Looking at the internal state of navigator will make you have a bad time.

The biggest change is switching to the new component-freezing techinique in the navigation bars. This way we avoid dependence on the idStack to provide a scalar ID for each route.
2015-07-17 15:28:57 -08:00
Eric Vicenti c4389c006f [ReactNative] Max RedBox windowLevel to always appear on top 2015-07-17 13:24:52 -08:00
Bill Fisher b56717ede9 [Storyline] SliderIOS responds to UIControlEventTouchUpOutside 2015-07-17 13:01:08 -08:00
DengYun 3c78aa6d25 [Bridge] remove unused semaphore
Summary:
dispatch_semaphore_wait with DISPATCH_TIME_NOW don't wait for the semaphore. It just test whether there's a signal and return the result.

So the line "dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);" does nothing here. It seems like a bug, but each executing in js thread will be queued, so it's safe to remove the unused semaphore instead of wait for it.
Closes https://github.com/facebook/react-native/pull/1915
Github Author: DengYun <tdzl2003@gmail.com>
2015-07-17 10:01:26 -08:00
Nick Lockwood 61c648d564 Merged RCTNetworkImageView functionality into RCTStaticImage
Summary:
RCTNetworkImageView and RCTStaticImage had significant overlap in functionality, but each had a different subset of features and bugs.

This diff merges most of the functionality of RCTNetworkImageView into RCTStaticImage, eliminating some bugs in the former, such as constant redrawing when properties were changed.

I've also removed the onLoadAbort event for now (as it wasn't implemented), and renamed the other events to match the web specs for `<img>` and XHMLHttpRequest. The API is essentially what Adobe proposed here: http://blogs.adobe.com/webplatform/2012/01/13/html5-image-progress-events/

The following features have not yet been ported from RCTNetworkImageView:

- Background color compositing. It's not clear that this adds much value and it increases memory consumption, etc.
- Image request cancelling when images are removed from view. Again, it's not clear if this is a huge benefit, but if it is it should be combined with other optimisations, such as unloading offscreen images.

(Note that this only affects the open source fork. For now, internal apps will still use FBNetworkImageView for remote images.)
2015-07-17 07:43:16 -08:00
Nick Lockwood 82a774e92b Added unit tests for gzip functionality 2015-07-17 04:55:27 -08:00
Spencer Ahrens fc23d0c40d Merge pull request #2030 from sahrens/Updates_From_Fri_July_17th
Updates from fri july 17th
2015-07-17 14:26:44 +02:00
Spencer Ahrens 4a5f12aa29 Updates from Fri July 17th 2015-07-17 14:14:30 +02:00
Spencer Ahrens c43e93d1b4 Reverted ca9d1b3bf5a6f46ec29babe8685634690ff9a2bc to unbreak groups 2015-07-17 04:03:01 -08:00
Christopher Chedeau 2771fc4c52 Add link for releases 2015-07-16 19:41:22 -07:00
Kevin Gozali 77a0cf27f2 [ReactNative] Revert onItemRef removal 2015-07-16 17:49:56 -08:00
Kevin Gozali 5ec60effea [ReactNative] revert 'unbreak navigation due to onItemRef removal' 2015-07-16 17:49:56 -08:00
Hedger Wang 326a66ba07 [ReactNative][Navigation]: Add method preventDefault() to NavigationEvent 2015-07-16 17:35:21 -08:00
Spencer Ahrens 4b82673484 revert [React Native] Fix scroll view sticky headers 2015-07-16 16:31:53 -08:00
Martín Bigio ab9a87c33a [cg] Perf logging 2015-07-16 14:24:49 -08:00
Hedger Wang fca16fbe40 [ReactNative] unbreak navigation due to onItemRef removal 2015-07-16 13:41:20 -08:00
Sean Powell fa4c570d33 Support debugger reconnection when the packager goes down.
Summary:
This should resolve the issue highlighted in #1709
Closes https://github.com/facebook/react-native/pull/1992
Github Author: Sean Powell <sean@longdivision.co.uk>
2015-07-16 13:32:01 -08:00
Nick Lockwood 81ad713f5f Added Gzip support
Summary:
Added Gzip function to RCTUtils. This uses dlopen to load the zlib library at runtime so there's no need to link it into your project.

The main reason for this feature is to support gzipping of HTTP request bodies. Now, if you add 'Content-Encoding:gzip' to your request headers when using XMLHttpRequest, your request body will be automatically gzipped on the native side before sending.

(Note: Gzip decoding of *response* bodies is handled automatically by iOS, and was already available).
2015-07-16 09:38:20 -08:00
Frédéric Sagnes 36f76e5893 Improve the performance of React Native tests 2015-07-16 08:41:06 -08:00
Adam Farhi f74efc13ed Add ListView Grid Layout Example to UIExplorer Examples
Summary:
On @brentvatne suggestion:
https://github.com/facebook/react-native/issues/1276#issuecomment-112161358

I imported my Grid Layout example (https://github.com/yelled3/react-native-grid-example) into UIExplorer.

![demo-grid](https://cloud.githubusercontent.com/assets/818124/8229685/c4325dd4-15c1-11e5-8817-cdf270ab1003.gif)
Closes https://github.com/facebook/react-native/pull/1668
Github Author: Adam Farhi <yelled3@gmail.com>
2015-07-16 06:02:15 -08:00
James Ide b362072c83 [Rendering] Set allowsEdgeAntialiasing for transformed views
Summary:
By default, the edges of rotated layers on iOS have jagged edges because they are not antialiased. Setting `allowsEdgeAntialiasing` makes them look a lot nicer by smoothing out the jaggies. This is particularly important for UIs like Tinder cards, for example.

Closes https://github.com/facebook/react-native/pull/1999
Github Author: James Ide <ide@jameside.com>
2015-07-16 04:33:03 -08:00
Mr Speaker 2cb634bb0b Nav ios hide hairline
Summary:
For the current project I am working on I needed to add a boolean option (`shadowHidden`) to remove the 1px hairline shadow from the NavigationIOS component:

`<NavigatorIOS shadowHidden={true} initialRoute={...} />`

By default, or with `shadowHidden={false}` it looks like this:

![withhairline](https://cloud.githubusercontent.com/assets/129330/8145401/d2704956-11d4-11e5-86e8-a75435b68480.png)
Setting the shadow hidden  with `shadowHidden={true}` looks like this:

![nohairline2](https://cloud.githubusercontent.com/assets/129330/8145405/148ed56e-11d5-11e5-85d6-f8cd3453d5ac.png)

The code it uses to do the actual hiding is... a bit of a hack (*I* think), but it's the only way currently to do it, and is the way they do it in the iPhone calendar app: iterating through the subviews and removing the shadow image.

This removes the shadow *and* keeps the translucent blurry effect with a ScrollView (see this [SO discussion](http://stackoverflow.com/questions/18160173/how-to-remove-uinavigationbar-inner
Closes https://github.com/facebook/react-native/pull/1615
Github Author: Mr Speaker <mrspeaker@gmail.com>
2015-07-16 04:31:17 -08:00
Mike Armstrong 9f94dd457a Camera node for scene hierarchy 2015-07-16 01:57:00 -08:00
John Ku 72694a722e Include jestSupport for npm package
Summary:
https://github.com/facebook/react-native/pull/1639 was closed for unknown reason. This will help unit test with Jest without manually copying them into node_modules/react-native.
Closes https://github.com/facebook/react-native/pull/1848
Github Author: John Ku <john.cmyk@gmail.com>
2015-07-15 19:00:15 -08:00
Mr Speaker 09236ccbe7 Allow horizontal ListView. Rename height -> size
Summary:
Infinite scrolling in horizontal ListViews. Rather than just using height and Y offset to determine when to load more rows, it checks `props.horizontal` and switches between width/height and offset X/Y accordingly.

This changed required some renaming. However, the only change external to `ListView.js` is exporting `contentSize` instead of `contentHeight` from the `getMetrics()` function. (This is not part of the API, but is used "for perf investigations or analytics" and isn't reference in the repo).

I believe this change works as expected (and the xcode tests pass) though it's possible that there may more complexity in this issue that I have overlooked.
Closes https://github.com/facebook/react-native/pull/1786
Github Author: Mr Speaker <mrspeaker@gmail.com>
2015-07-15 10:13:12 -08:00
Spencer Ahrens 28b9a6f69e Merge pull request #2007 from sahrens/Updates_from_Wed_July_15th
Updates from wed july 15th
2015-07-15 19:26:26 +02:00
Spencer Ahrens 902ffbbfb8 Updates from Wed July 15th 2015-07-15 19:05:08 +02:00
Christopher 5006eca4a8 [TextInput] Remove focus on TextInput if property editable is …
Summary:
Clicking on a TextField with editable set to false still would trigger a keyboard event.
In this case that the TextField was a multiline, it would scroll you down to the bottom.

Closes https://github.com/facebook/react-native/pull/1855
Github Author: Christopher <hello@blick-labs.com>
2015-07-15 08:37:22 -08:00
Dave Sibiski 70feab9d50 [Packager] Fix when loading a path that can't be handled
Summary:
[Packager] Adds `NotFoundError` when loading a path that can't be handled

Resolves https://github.com/facebook/react-native/issues/1944
Closes https://github.com/facebook/react-native/pull/1948
Github Author: Dave Sibiski <dsibiski@gmail.com>
2015-07-15 08:25:31 -08:00
chirag04 915540d237 [transform] add perspective property to transform
Summary:
![matrixflip](https://cloud.githubusercontent.com/assets/1509831/8701143/607b069c-2b10-11e5-9c54-8b9767e74e16.gif)

cc @sahrens @vjeux
Closes https://github.com/facebook/react-native/pull/1980
Github Author: chirag04 <jain_chirag04@yahoo.com>
2015-07-15 08:08:29 -08:00
Matt Revell 64c00dcd7a Issues/#1689 text input warning
Summary:
This should close issue 1689. Using Object.assign to pass Flow checks.
Closes https://github.com/facebook/react-native/pull/1956
Github Author: Matt Revell <mattrevell82@me.com>
2015-07-15 06:33:03 -08:00
Dorota Kapturkiewicz ba433be886 [ReactNative] Make ErrorCookie work. 2015-07-15 04:04:09 -08:00
Brent Vatne 94e2e51876 Merge pull request #1949 from sjmueller/master
Clarify docs to use animated over react-tween-state & rebound
2015-07-14 23:16:36 -07:00
chirag04 548549ea9f [style] expose rotate x, y, z transforms
Summary:
Added rotateX, rotateY, rotateZ transforms.

cc @sahrens
Closes https://github.com/facebook/react-native/pull/1976
Github Author: chirag04 <jain_chirag04@yahoo.com>
2015-07-14 19:58:26 -08:00
James Ide 19ddbac113 [Versions] Update versions in package.json and podspec to 0.7.1
Summary:
When developing against master I want to rely on the version numbers being reasonably accurate.
Closes https://github.com/facebook/react-native/pull/1804
Github Author: James Ide <ide@jameside.com>
2015-07-14 17:57:07 -08:00
chirag04 04fa09afef [Style] added backface-visibility. fixes #1973
Summary:
default behavior remains same(backface visible).

cc @sahrens
Closes https://github.com/facebook/react-native/pull/1977
Github Author: chirag04 <jain_chirag04@yahoo.com>
2015-07-14 17:38:05 -08:00
Spencer Ahrens 5d4140c513 [ReactNative] deepDiff by default 2015-07-14 17:32:25 -08:00
Pylipala 7a23651ee1 Fix the main.jsbundle url in comment
Summary:
Fix the url in comment
Closes https://github.com/facebook/react-native/pull/1959
Github Author: Pylipala <pylipala@gmail.com>
2015-07-14 16:44:35 -08:00
Tadeu Zagallo d30ada61f0 [ReactNative] Remove unused executor context id
Summary:
Remove `RCTGetExecutorID` and `RCTSetExecutorID`, it wasn't used anymore since
the bridge was refactored into `RCTBridge` and `RCTBatchedBridge`.
2015-07-14 16:40:21 -08:00
Tadeu Zagallo 9799c215cb [ReactNative] Add JS errors handling to iOS
Summary:
Every once in a while a guard is forgotten somewhere and the redbox is gone. I
want to remove the guards, but for that the stack traces have to be symbolicated
on the native side. So for now it just adds yet another check, in case a guard
is missing on JS.
2015-07-14 16:24:06 -08:00
Spencer Ahrens 3e8604d5d8 Merge pull request #1982 from IskenHuang/feature/bundle_cli
add cli bundle url params
2015-07-15 02:07:30 +02:00
Kevin Gozali 2e9d156fad [ReactNative] revert D2233419: 'Dynamic Text Sizes for Text component' 2015-07-14 15:55:06 -08:00
Eric Vicenti c3e75393ab [ReactNative] Remove Navigator onItemRef
Summary:
onItemRef is old and no longer needed now that the parent renders the scenes. This removes it from Navigator and all of our clients.

This is a breaking change to users of Navigator, but it is easy to transition to a ref in renderScene instead
2015-07-14 14:56:16 -08:00
Dmitriy Loktev 74f8055692 [Image] Add examples to UIExplorer, fix some bugs
Summary:
Add two simple examples.
![image](https://cloud.githubusercontent.com/assets/1004115/8674092/03052652-2a55-11e5-8531-8f01c7970af4.png)

Also:
- do not start the task at DownloadTaskWrapper (it starts from ImageDowloader if image isn't cached yet);
- fire `onLoadAbor`t event;
- send more readable `error.localizedDescription` with `onLoadError`;
- rename `onLoaded` to `onLoadEnd`
Closes https://github.com/facebook/react-native/pull/1987
Github Author: Dmitriy Loktev <unknownliveid@hotmail.com>
2015-07-14 14:33:02 -08:00
Nick Lockwood d1a82c4af1 Fixed image downloader bug
Summary:
RCTImageDownloader was ignoring server response codes. When receiving a response other than 200 it would treat this as success, meaning the image would never load, nor report failure.

This diff fixes that by treating responses with non-200 status as an error so that error handler is called.
2015-07-14 13:04:34 -08:00
Jared Forsyth a43987309d [react native] prepare the react native inspector for the new react devtools
Summary:
These are the changes needed for full interop with the (as yet unreleased) new
version of React Devtools.

- the on-device inspector is minimized when devtools is open
- devtools highlight -> device and device touch -> devtools select works
- editing react native styles :)
2015-07-14 12:35:43 -08:00
Spencer Ahrens 52f599a080 Merge pull request #1988 from sahrens/Updates_from_Tue_July_14th
Updates from tue july 14th
2015-07-14 21:04:23 +02:00
Spencer Ahrens 34c2157b66 Updates from Tue July 14th 2015-07-14 20:37:13 +02:00
Forbes Lindesay ebd046ae21 Remove bluebird 2015-07-14 10:25:16 -08:00
Jacob Rosenthal a8cb47e011 add local notification api schedule and present
Summary:
Add local notifications to the push library.
```
  var PushNotificationIOS = React.PushNotificationIOS;
  PushNotificationIOS.requestPermissions();

  var notification = {
    "fireDate": Date.now() + 10000,
    "alertBody":"Whats up pumpkin"
  };

  PushNotificationIOS.scheduleLocalNotification(notification);
 //lock screen or move away from app
```

Apple has another api for pushing immediately instead of scheduling, like when your background delegate has been called with some new data (bluetooth, location, etc)
```
  var PushNotificationIOS = React.PushNotificationIOS;
  PushNotificationIOS.requestPermissions();

  var notification = {
    "alertBody":"Whats up pumpkin"
  };

  PushNotificationIOS.presentLocalNotification(notification);
 //lock screen or move away from app
```

Closed https://github.com/facebook/react-native/pull/843 looks related:

See https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/ for much more available in
Closes https://github.com/facebook/react-native/pull/1616
Github Author: Jacob Rosenthal <jakerosenthal@gmail.com>
2015-07-14 09:08:30 -08:00
Jean Regisser 9936a2406d [TabBarIOS] Add translucent property to TabBarIOS
Summary:
To be on par with NavigatorIOS, I added the translucent property to TabBarIOS.

Usage:
```
<TabBarIOS
 translucent={false} // default is true
/>
```
Closes https://github.com/facebook/react-native/pull/1937
Github Author: Jean Regisser <jean.regisser@gmail.com>
2015-07-14 08:02:44 -08:00
Martín Bigio 691a1dafd1 s/RKScrollViewManager/RCTScrollViewManager 2015-07-14 07:52:30 -08:00
Nick Lockwood 0c61b49f0a Improved RCTCache performance + fixed border color crash
Summary:
RCTCache had really bad insertion performance when the cache was full due to having to LRU-sort the entries. This was making color
animations very slow.

I've fixed this in two ways:

1) by removing the sort and doing a linear search to remove old entries, which changes insertion perf to O(n) in the worst case instead of O(n log n) or even (n2).

2) by reducing the size of the color cache to 128 from 1024, which should be fine for normal use, without penalising animation performance.

Separately, border colors were not being retained, which caused crashes when the color cache was cleared. I've fixed that by retaining the border colors inside RCTView.
2015-07-14 04:56:25 -08:00
Nick Lockwood b34a85f4da Merged RCTStaticImage with FB internal version
Summary:
Merged RCTStaticImage with our internal RKStaticImage and ported over logic where assets are loaded at the optimal size and reloaded if the view size changes.
2015-07-14 04:06:18 -08:00
Alex Akers a65bbe14d3 [React Native] Update additional callsites with RCTResponseErrorBlock 2015-07-14 03:59:18 -08:00
Vladislav Alexeev be2cabc3f8 Dynamic Text Sizes for Text component
Summary:
Dynamic Text Sizes for Text component.
Text gains new prop - allowFontScaling (true by default).
There is also AccessibilityManager module that allows you to tune multipliers per each content size category, but predefined multipliers are there.
This could potentially break some apps so please test carefully.
2015-07-14 03:13:32 -08:00
Alex Akers eb06659711 [React Native] Update code comments about layout-only view 2015-07-14 02:52:45 -08:00
Isken Huang 6357931b61 add cli bundle url params 2015-07-14 10:40:39 +08:00
Nathan Spaun 875ab073ea [Treehouse] Sync from android permalink keyboard layout changes 2015-07-13 18:29:54 -08:00
Spencer Ahrens 0d153562be Merge pull request #1978 from sahrens/Updates_for_Mon_July_13th
Updates for mon july 13th
2015-07-14 02:18:18 +02:00
Spencer Ahrens 31b63ade13 Updates_for_Mon_July_13th 2015-07-14 00:27:24 +02:00
Spencer Ahrens 509e9c96e2 [ReactNative] Disable flaky Layout only node scenario tests 2015-07-13 14:20:10 -08:00
Param Aggarwal e1ce646275 Stray import statement - RCTTouchHandler
Summary:
Closes https://github.com/facebook/react-native/pull/1970
Github Author: Param Aggarwal <paramaggarwal@gmail.com>
2015-07-13 12:47:18 -08:00
Spencer Ahrens e5f7200213 [ReactNative] Fix crash in ListView 2015-07-13 12:36:56 -08:00
James Ide 9f07b9a2b6 [ListView] Operate on the true scroll responder instead of the scroll component
Summary:
When composing scroll views, `this.refs[SCROLLVIEW_REF]` may refer to another higher-order scroll component instead of a ScrollView. This can cause issues if you expect to need it to be a ScrollView backed by an RCTScrollView.

The solution is to call `getScrollResponder()` - as long as all higher-order scroll components implement this method, it will make its way down to the true ScrollView, which is what ListView wants here.

Closes https://github.com/facebook/react-native/pull/1927
Github Author: James Ide <ide@jameside.com>
2015-07-13 12:36:55 -08:00
James Ide d5943b0e47 [Image] Add support for tintColor to remote images
Summary:
Remote images now support the `tintColor` prop.

Also picked nicer demo colors for the UIExplorer example.

Fixes #1867

Closes https://github.com/facebook/react-native/pull/1932
Github Author: James Ide <ide@jameside.com>
2015-07-13 10:36:32 -08:00
Nick Lockwood 90dd7a13f0 Added support for URLs pointing to files inside application home 2015-07-13 10:34:48 -08:00
Alex Kotliarskyi d1b14ef062 [React Native] Log to ASL
Summary:
By default we were just writing all log messages to stderr. This also
adds support for [Apple System Log](https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/asl_log.3.html) that
can be viewed using standard tools.
2015-07-13 09:52:16 -08:00
Krzysztof Magiera 47315af069 [ReactNative] Fix SPY_MODE to show all JS->N communication. 2015-07-13 09:43:04 -08:00
Nick Lockwood 01151f8c7a Added LRU cache to fix out of memory issues with color caching 2015-07-13 08:50:00 -08:00
Alex Akers 4beef5bec3 [React Native] Re-enable testScenario{1,2}
Summary:
The tests are passing locally. Not sure why they weren't passing on FB CI.
2015-07-13 07:16:52 -08:00
Spencer Ahrens 9c4b9973f9 [ReactNative] add _flowconfig and _gitignore to SampleApp 2015-07-13 06:39:18 -08:00
Sam Mueller f732e46581 Clarify docs to use animated over react-tween-state & rebound 2015-07-10 14:04:45 -07:00
Hedger Wang 2cbcfcea88 [Android][JS]: supports prop "html" in WebView 2015-07-10 12:33:16 -08:00
362 changed files with 12574 additions and 7564 deletions
+9 -8
View File
@@ -9,11 +9,10 @@
# Ignore react-tools where there are overlaps, but don't ignore anything that
# react-native relies on
.*/node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js
.*/node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js
.*/node_modules/react-tools/src/browser/ui/React.js
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
.*/node_modules/react-tools/src/event/EventPropagators.js
.*/node_modules/react-tools/src/React.js
.*/node_modules/react-tools/src/renderers/shared/event/EventPropagators.js
.*/node_modules/react-tools/src/renderers/shared/event/eventPlugins/ResponderEventPlugin.js
.*/node_modules/react-tools/src/shared/vendor/core/ExecutionEnvironment.js
# Ignore commoner tests
.*/node_modules/commoner/test/.*
@@ -36,13 +35,15 @@ Examples/UIExplorer/ImageMocks.js
[options]
module.system=haste
munge_underscores=true
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version]
0.13.1
0.14.0
+5 -4
View File
@@ -10,19 +10,20 @@ cache:
before_install:
- brew update
install:
- brew reinstall flow xctool nvm
- brew reinstall xctool nvm
- rm -Rf `node -p "require('os').tmpDir()"`/jest_preprocess_cache
- npm install -g flow-bin@`node -p "require('fs').readFileSync('.flowconfig', 'utf8').split('[version]')[1].trim()"`
- mkdir -p .nvm
- cp $(brew --prefix nvm)/nvm-exec .nvm/
- export NVM_DIR=.nvm
- source $(brew --prefix nvm)/nvm.sh
- nvm install iojs-v2
- nvm use iojs-v2
- npm config set spin=false
- npm install
script:
- |
nvm use iojs-v2
if [ "$TEST_TYPE" = objc ]
then
@@ -37,7 +38,7 @@ script:
then
cd website
npm install
$(which npm) install
./setup.sh
if [ "$TRAVIS_PULL_REQUEST" = false ] && [ "$TRAVIS_BRANCH" = master ]; then
# Automatically publish the website
+39 -41
View File
@@ -24,7 +24,7 @@ var {
View,
} = React;
var AnimationExperimental = require('AnimationExperimental');
var Animated = require('Animated');
var GameBoard = require('GameBoard');
var TouchableBounce = require('TouchableBounce');
@@ -53,58 +53,58 @@ class Board extends React.Component {
}
class Tile extends React.Component {
static _getPosition(index): number {
return BOARD_PADDING + (index * (CELL_SIZE + CELL_MARGIN * 2) + CELL_MARGIN);
}
constructor(props: {}) {
super(props);
var tile = this.props.tile;
this.state = {
opacity: new Animated.Value(0),
top: new Animated.Value(Tile._getPosition(tile.toRow())),
left: new Animated.Value(Tile._getPosition(tile.toColumn())),
};
}
calculateOffset(): {top: number; left: number; opacity: number} {
var tile = this.props.tile;
var pos = (i) => {
return BOARD_PADDING + (i * (CELL_SIZE + CELL_MARGIN * 2) + CELL_MARGIN);
};
var animationPosition = (i) => {
return pos(i) + (CELL_SIZE / 2);
};
var offset = {
top: pos(tile.toRow()),
left: pos(tile.toColumn()),
opacity: 1,
top: this.state.top,
left: this.state.left,
opacity: this.state.opacity,
};
if (tile.isNew()) {
offset.opacity = 0;
} else {
var point = {
x: animationPosition(tile.toColumn()),
y: animationPosition(tile.toRow()),
};
AnimationExperimental.startAnimation({
node: this.refs['this'],
Animated.timing(this.state.opacity, {
duration: 100,
easing: 'easeInOutQuad',
property: 'position',
toValue: point,
});
toValue: 1,
}).start();
} else {
Animated.parallel([
Animated.timing(offset.top, {
duration: 100,
toValue: Tile._getPosition(tile.toRow()),
}),
Animated.timing(offset.left, {
duration: 100,
toValue: Tile._getPosition(tile.toColumn()),
}),
]).start();
}
return offset;
}
componentDidMount() {
AnimationExperimental.startAnimation({
node: this.refs['this'],
duration: 100,
easing: 'easeInOutQuad',
property: 'opacity',
toValue: 1,
});
}
render() {
var tile = this.props.tile;
var tileStyles = [
styles.tile,
styles['tile' + tile.value],
this.calculateOffset()
this.calculateOffset(),
];
var textStyles = [
@@ -115,9 +115,9 @@ class Tile extends React.Component {
];
return (
<View ref="this" style={tileStyles}>
<Animated.View style={tileStyles}>
<Text style={textStyles}>{tile.value}</Text>
</View>
</Animated.View>
);
}
}
@@ -136,10 +136,8 @@ class GameEndOverlay extends React.Component {
return (
<View style={styles.overlay}>
<Text style={styles.overlayMessage}>{message}</Text>
<TouchableBounce onPress={this.props.onRestart}>
<View style={styles.tryAgain}>
<Text style={styles.tryAgainText}>Try Again?</Text>
</View>
<TouchableBounce onPress={this.props.onRestart} style={styles.tryAgain}>
<Text style={styles.tryAgainText}>Try Again?</Text>
</TouchableBounce>
</View>
);
+2 -1
View File
@@ -262,6 +262,7 @@ var SearchScreen = React.createClass({
) {
return (
<MovieCell
key={movie.id}
onSelect={() => this.selectMovie(movie)}
onHighlight={() => highlightRowFunc(sectionID, rowID)}
onUnhighlight={() => highlightRowFunc(null, null)}
@@ -307,7 +308,7 @@ var NoMovies = React.createClass({
render: function() {
var text = '';
if (this.props.filter) {
text = `No results for ${this.props.filter}`;
text = `No results for "${this.props.filter}"`;
} else if (!this.props.isLoading) {
// If we're looking at the latest movies, aren't currently loading, and
// still have no results, show a message
+10 -8
View File
@@ -9,11 +9,11 @@
# Ignore react-tools where there are overlaps, but don't ignore anything that
# react-native relies on
.*/node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js
.*/node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js
.*/node_modules/react-tools/src/browser/ui/React.js
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
.*/node_modules/react-tools/src/event/EventPropagators.js
.*/node_modules/react-tools/src/React.js
.*/node_modules/react-tools/src/renderers/shared/event/EventPropagators.js
.*/node_modules/react-tools/src/renderers/shared/event/eventPlugins/ResponderEventPlugin.js
.*/node_modules/react-tools/src/shared/vendor/core/ExecutionEnvironment.js
# Ignore commoner tests
.*/node_modules/commoner/test/.*
@@ -32,13 +32,15 @@ node_modules/react-native/Libraries/react-native/react-native-interface.js
[options]
module.system=haste
munge_underscores=true
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version]
0.13.1
0.14.0
@@ -106,27 +106,27 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
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>"; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../../Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
00E356EE1AD99517003FC87E /* SampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* SampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleAppTests.m; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* SampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = "<group>"; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = iOS/Images.xcassets; sourceTree = "<group>"; };
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>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = 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 */
/* Begin PBXFrameworksBuildPhase section */
@@ -233,15 +233,15 @@
13B07FAE1A68108700A75B9A /* SampleApp */ = {
isa = PBXGroup;
children = (
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FB71A68108700A75B9A /* main.m */,
);
name = SampleApp;
path = SampleApp;
sourceTree = "<group>";
};
146834001AC3E56700842450 /* Products */ = {
@@ -289,8 +289,8 @@
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* SampleApp */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* SampleAppTests */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */,
);
indentWidth = 2;
@@ -548,7 +548,6 @@
13B07FB21A68108700A75B9A /* Base */,
);
name = LaunchScreen.xib;
path = iOS;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
@@ -598,9 +597,9 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/../../../React/**",
);
INFOPLIST_FILE = "iOS/Info.plist";
INFOPLIST_FILE = "$(SRCROOT)/SampleApp/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = SampleApp;
@@ -614,9 +613,9 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/../../../React/**",
);
INFOPLIST_FILE = "iOS/Info.plist";
INFOPLIST_FILE = "$(SRCROOT)/SampleApp/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = SampleApp;
@@ -660,7 +659,7 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/../../../React/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
@@ -700,7 +699,7 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/../../../React/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "0630"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -20,20 +20,6 @@
ReferencedContainer = "container:SampleApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "SampleAppTests.xctest"
BlueprintName = "SampleAppTests"
ReferencedContainer = "container:SampleApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
@@ -42,16 +28,6 @@
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "SampleAppTests.xctest"
BlueprintName = "SampleAppTests"
ReferencedContainer = "container:SampleApp.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1510" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
@@ -0,0 +1,163 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
'use strict';
var React = require('react-native');
var {
StyleSheet,
Text,
View,
TouchableWithoutFeedback,
} = React;
var ToastAndroid = require('ToastAndroid');
var AccessibilityInfo = require('AccessibilityInfo');
var UIExplorerBlock = require('./UIExplorerBlock');
var UIExplorerPage = require('./UIExplorerPage');
var AccessibilityAndroidExample = React.createClass({
statics: {
title: 'Accessibility',
description: 'Examples of using Accessibility API.',
},
getInitialState: function() {
return {
count: 0,
talkbackEnabled: false,
};
},
componentDidMount: function() {
AccessibilityInfo.addEventListener(
'change',
this._handleTouchExplorationChange
);
AccessibilityInfo.fetch().done((enabled) => {
this.setState({
count: this.state.count,
talkbackEnabled: enabled}); }
);
},
componentWillUnmount: function() {
AccessibilityInfo.removeEventListener(
'change',
this._handleTouchExplorationChange
);
},
_handleTouchExplorationChange: function(isEnabled) {
this.setState({
count: this.state.count,
talkbackEnabled: isEnabled,
});
},
_showAccessibilityToast: function() {
var text = 'TouchExploration is ' + (this.state.talkbackEnabled ? 'enabled' : 'disabled');
ToastAndroid.show(text, ToastAndroid.SHORT);
},
_addOne: function() {
this.setState({
count: ++this.state.count,
talkbackEnabled: this.state.talkbackEnabled,
});
},
render: function() {
return (
<UIExplorerPage title={'Accessibility'}>
<UIExplorerBlock title="Nonaccessible view with TextViews">
<View>
<Text style={{color: 'green',}}>
This is
</Text>
<Text style={{color: 'blue'}}>
nontouchable normal view.
</Text>
</View>
</UIExplorerBlock>
<UIExplorerBlock title="Accessible view with TextViews wihout label">
<View accessible={true}>
<Text style={{color: 'green',}}>
This is
</Text>
<Text style={{color: 'blue'}}>
nontouchable accessible view without label.
</Text>
</View>
</UIExplorerBlock>
<UIExplorerBlock title="Accessible view with TextViews with label">
<View accessible={true}
accessibilityLabel="I have label, so I read it instead of embedded text.">
<Text style={{color: 'green',}}>
This is
</Text>
<Text style={{color: 'blue'}}>
nontouchable accessible view with label.
</Text>
</View>
</UIExplorerBlock>
<UIExplorerBlock title="Touchable with component type = button">
<TouchableWithoutFeedback
onPress={() => ToastAndroid.show('Toasts work by default', ToastAndroid.SHORT)}>
<View style={styles.embedded} accessibilityComponentType="button">
<Text>Click me</Text>
<Text>Or not</Text>
</View>
</TouchableWithoutFeedback>
</UIExplorerBlock>
<UIExplorerBlock title="LiveRegion">
<TouchableWithoutFeedback onPress={this._addOne}>
<View style={styles.embedded} accessibilityComponentType="button">
<Text>Click me</Text>
</View>
</TouchableWithoutFeedback>
<Text accessibilityLiveRegion="polite">
Clicked {this.state.count} times
</Text>
</UIExplorerBlock>
<UIExplorerBlock title="Check if TouchExploration is enabled">
<TouchableWithoutFeedback onPress={this._showAccessibilityToast}>
<View style={styles.embedded}>
<Text>
Click to check TouchExploration
</Text>
</View>
</TouchableWithoutFeedback>
</UIExplorerBlock>
</UIExplorerPage>
);
},
});
var styles = StyleSheet.create({
embedded: {
backgroundColor: 'yellow',
padding:10,
},
});
module.exports = AccessibilityAndroidExample;
@@ -38,9 +38,11 @@ class Circle extends React.Component {
super();
this._onLongPress = this._onLongPress.bind(this);
this._toggleIsActive = this._toggleIsActive.bind(this);
this.state = {isActive: false};
this.state.pan = new Animated.ValueXY(); // Vectors reduce boilerplate. (step1: uncomment)
this.state.pop = new Animated.Value(0); // Initial value. (step2a: uncomment)
this.state = {
isActive: false,
pan: new Animated.ValueXY(), // Vectors reduce boilerplate. (step1: uncomment)
pop: new Animated.Value(0), // Initial value. (step2a: uncomment)
};
}
_onLongPress(): void {
@@ -0,0 +1,98 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @flow
*/
'use strict';
var React = require('react-native');
var {
Image,
StyleSheet,
View,
ScrollView
} = React;
var AssetScaledImageExample = React.createClass({
getInitialState() {
return {
asset: this.props.asset
};
},
render() {
var image = this.state.asset.node.image;
return (
<ScrollView>
<View style={styles.row}>
<Image source={image} style={styles.imageWide}/>
</View>
<View style={styles.row}>
<Image source={image} style={styles.imageThumb}/>
<Image source={image} style={styles.imageThumb}/>
<Image source={image} style={styles.imageThumb}/>
</View>
<View style={styles.row}>
<Image source={image} style={styles.imageT1}/>
<Image source={image} style={styles.imageT2}/>
</View>
</ScrollView>
);
},
});
var styles = StyleSheet.create({
row: {
padding: 5,
flex: 1,
flexDirection: 'row',
alignSelf: 'center',
},
textColumn: {
flex: 1,
flexDirection: 'column',
},
imageWide: {
borderWidth: 1,
borderColor: 'black',
width: 320,
height: 240,
margin: 5,
},
imageThumb: {
borderWidth: 1,
borderColor: 'black',
width: 100,
height: 100,
margin: 5,
},
imageT1: {
borderWidth: 1,
borderColor: 'black',
width: 212,
height: 320,
margin: 5,
},
imageT2: {
borderWidth: 1,
borderColor: 'black',
width: 100,
height: 320,
margin: 5,
},
});
exports.title = '<AssetScaledImageExample>';
exports.description = 'Example component that displays the automatic scaling capabilities of the <Image /> tag';
module.exports = AssetScaledImageExample;
+30 -21
View File
@@ -29,18 +29,23 @@ var COLORS = ['red', 'orange', 'yellow', 'green', 'blue'];
var BasicStorageExample = React.createClass({
componentDidMount() {
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();
this._loadInitialState().done();
},
async _loadInitialState() {
try {
var value = await AsyncStorage.getItem(STORAGE_KEY);
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);
}
},
getInitialState() {
return {
selectedValue: COLORS[0],
@@ -80,19 +85,23 @@ var BasicStorageExample = React.createClass({
);
},
_onValueChange(selectedValue) {
async _onValueChange(selectedValue) {
this.setState({selectedValue});
AsyncStorage.setItem(STORAGE_KEY, selectedValue)
.then(() => this._appendMessage('Saved selection to disk: ' + selectedValue))
.catch((error) => this._appendMessage('AsyncStorage error: ' + error.message))
.done();
try {
await AsyncStorage.setItem(STORAGE_KEY, selectedValue);
this._appendMessage('Saved selection to disk: ' + selectedValue);
} catch (error) {
this._appendMessage('AsyncStorage error: ' + error.message);
}
},
_removeStorage() {
AsyncStorage.removeItem(STORAGE_KEY)
.then(() => this._appendMessage('Selection removed from disk.'))
.catch((error) => { this._appendMessage('AsyncStorage error: ' + error.message) })
.done();
async _removeStorage() {
try {
await AsyncStorage.removeItem(STORAGE_KEY);
this._appendMessage('Selection removed from disk.');
} catch (error) {
this._appendMessage('AsyncStorage error: ' + error.message);
}
},
_appendMessage(message) {
+26 -13
View File
@@ -24,9 +24,11 @@ var {
SwitchIOS,
Text,
View,
TouchableOpacity
} = React;
var CameraRollView = require('./CameraRollView.ios');
var AssetScaledImageExampleView = require('./AssetScaledImageExample');
var CAMERA_ROLL_VIEW = 'camera_roll_view';
@@ -54,7 +56,7 @@ var CameraRollExample = React.createClass({
<Text>{'Group Type: ' + this.state.groupTypes}</Text>
<CameraRollView
ref={CAMERA_ROLL_VIEW}
batchSize={5}
batchSize={20}
groupTypes={this.state.groupTypes}
renderImage={this._renderImage}
/>
@@ -62,24 +64,35 @@ var CameraRollExample = React.createClass({
);
},
loadAsset(asset){
this.props.navigator.push({
title: 'Camera Roll Image',
component: AssetScaledImageExampleView,
backButtonTitle: 'Back',
passProps: { asset: asset },
});
},
_renderImage(asset) {
var imageSize = this.state.bigImages ? 150 : 75;
var imageStyle = [styles.image, {width: imageSize, height: imageSize}];
var location = asset.node.location.longitude ?
JSON.stringify(asset.node.location) : 'Unknown location';
return (
<View key={asset} style={styles.row}>
<Image
source={asset.node.image}
style={imageStyle}
/>
<View style={styles.info}>
<Text style={styles.url}>{asset.node.image.uri}</Text>
<Text>{location}</Text>
<Text>{asset.node.group_name}</Text>
<Text>{new Date(asset.node.timestamp).toString()}</Text>
<TouchableOpacity onPress={ this.loadAsset.bind( this, asset ) }>
<View key={asset} style={styles.row}>
<Image
source={asset.node.image}
style={imageStyle}
/>
<View style={styles.info}>
<Text style={styles.url}>{asset.node.image.uri}</Text>
<Text>{location}</Text>
<Text>{asset.node.group_name}</Text>
<Text>{new Date(asset.node.timestamp).toString()}</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
},
@@ -115,7 +128,7 @@ var styles = StyleSheet.create({
},
});
exports.title = '<CameraRollView>';
exports.title = 'Camera Roll';
exports.description = 'Example component that uses CameraRoll to list user\'s photos';
exports.examples = [
{
+305
View File
@@ -0,0 +1,305 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @flow
*/
'use strict';
var React = require('react-native');
var {
CameraRoll,
Image,
NativeModules,
ScrollView,
StyleSheet,
Text,
TouchableHighlight,
View,
} = React;
var ImageEditingManager = NativeModules.ImageEditingManager;
var RCTScrollViewConsts = NativeModules.UIManager.RCTScrollView.Constants;
var PAGE_SIZE = 20;
type ImageOffset = {
x: number;
y: number;
};
type ImageSize = {
width: number;
height: number;
};
type TransformData = {
offset: ImageOffset;
size: ImageSize;
}
class SquareImageCropper extends React.Component {
_isMounted: boolean;
_transformData: TransformData;
constructor(props) {
super(props);
this._isMounted = true;
this.state = {
randomPhoto: null,
measuredSize: null,
croppedImageURI: null,
cropError: null,
};
this._fetchRandomPhoto();
}
_fetchRandomPhoto() {
CameraRoll.getPhotos(
{first: PAGE_SIZE},
(data) => {
if (!this._isMounted) {
return;
}
var edges = data.edges;
var edge = edges[Math.floor(Math.random() * edges.length)];
var randomPhoto = edge && edge.node && edge.node.image;
if (randomPhoto) {
this.setState({randomPhoto});
}
},
(error) => undefined
);
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
if (!this.state.measuredSize) {
return (
<View
style={styles.container}
onLayout={(event) => {
var measuredWidth = event.nativeEvent.layout.width;
if (!measuredWidth) {
return;
}
this.setState({
measuredSize: {width: measuredWidth, height: measuredWidth},
});
}}
/>
);
}
if (!this.state.croppedImageURI) {
return this._renderImageCropper();
}
return this._renderCroppedImage();
}
_renderImageCropper() {
if (!this.state.randomPhoto) {
return (
<View style={styles.container} />
);
}
var error = null;
if (this.state.cropError) {
error = (
<Text>{this.state.cropError.message}</Text>
);
}
return (
<View style={styles.container}>
<Text>Drag the image within the square to crop:</Text>
<ImageCropper
image={this.state.randomPhoto}
size={this.state.measuredSize}
style={[styles.imageCropper, this.state.measuredSize]}
onTransformDataChange={(data) => this._transformData = data}
/>
<TouchableHighlight
style={styles.cropButtonTouchable}
onPress={this._crop.bind(this)}>
<View style={styles.cropButton}>
<Text style={styles.cropButtonLabel}>
Crop
</Text>
</View>
</TouchableHighlight>
{error}
</View>
);
}
_renderCroppedImage() {
return (
<View style={styles.container}>
<Text>Here is the cropped image:</Text>
<Image
source={{uri: this.state.croppedImageURI}}
style={[styles.imageCropper, this.state.measuredSize]}
/>
<TouchableHighlight
style={styles.cropButtonTouchable}
onPress={this._reset.bind(this)}>
<View style={styles.cropButton}>
<Text style={styles.cropButtonLabel}>
Try again
</Text>
</View>
</TouchableHighlight>
</View>
);
}
_crop() {
ImageEditingManager.cropImage(
this.state.randomPhoto.uri,
this._transformData,
(croppedImageURI) => this.setState({croppedImageURI}),
(cropError) => this.setState({cropError})
);
}
_reset() {
this.setState({
randomPhoto: null,
croppedImageURI: null,
cropError: null,
});
this._fetchRandomPhoto();
}
}
class ImageCropper extends React.Component {
_scaledImageSize: ImageSize;
_contentOffset: ImageOffset;
componentWillMount() {
// Scale an image to the minimum size that is large enough to completely
// fill the crop box.
var widthRatio = this.props.image.width / this.props.size.width;
var heightRatio = this.props.image.height / this.props.size.height;
if (widthRatio < heightRatio) {
this._scaledImageSize = {
width: this.props.size.width,
height: this.props.image.height / widthRatio,
};
} else {
this._scaledImageSize = {
width: this.props.image.width / heightRatio,
height: this.props.size.height,
};
}
this._contentOffset = {
x: (this._scaledImageSize.width - this.props.size.width) / 2,
y: (this._scaledImageSize.height - this.props.size.height) / 2,
};
this._updateTransformData(
this._contentOffset,
this._scaledImageSize,
this.props.size
);
}
_onScroll(event) {
this._updateTransformData(
event.nativeEvent.contentOffset,
event.nativeEvent.contentSize,
event.nativeEvent.layoutMeasurement
);
}
_updateTransformData(offset, scaledImageSize, croppedImageSize) {
var offsetRatioX = offset.x / scaledImageSize.width;
var offsetRatioY = offset.y / scaledImageSize.height;
var sizeRatioX = croppedImageSize.width / scaledImageSize.width;
var sizeRatioY = croppedImageSize.height / scaledImageSize.height;
this.props.onTransformDataChange && this.props.onTransformDataChange({
offset: {
x: this.props.image.width * offsetRatioX,
y: this.props.image.height * offsetRatioY,
},
size: {
width: this.props.image.width * sizeRatioX,
height: this.props.image.height * sizeRatioY,
},
});
}
render() {
var decelerationRate =
RCTScrollViewConsts && RCTScrollViewConsts.DecelerationRate ?
RCTScrollViewConsts.DecelerationRate.Fast :
0;
return (
<ScrollView
alwaysBounceVertical={true}
automaticallyAdjustContentInsets={false}
contentOffset={this._contentOffset}
decelerationRate={decelerationRate}
horizontal={true}
maximumZoomScale={3.0}
onMomentumScrollEnd={this._onScroll.bind(this)}
onScrollEndDrag={this._onScroll.bind(this)}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
style={this.props.style}
scrollEventThrottle={16}>
<Image source={this.props.image} style={this._scaledImageSize} />
</ScrollView>
);
}
}
exports.framework = 'React';
exports.title = 'ImageEditingManager';
exports.description = 'Cropping and scaling with ImageEditingManager';
exports.examples = [{
title: 'Image Cropping',
render() {
return <SquareImageCropper/>;
}
}];
var styles = StyleSheet.create({
container: {
flex: 1,
alignSelf: 'stretch',
},
imageCropper: {
alignSelf: 'center',
marginTop: 12,
},
cropButtonTouchable: {
alignSelf: 'center',
marginTop: 12,
},
cropButton: {
padding: 12,
backgroundColor: 'blue',
borderRadius: 4,
},
cropButtonLabel: {
color: 'white',
fontSize: 16,
fontWeight: '500',
},
});
+96 -17
View File
@@ -21,10 +21,41 @@ var {
StyleSheet,
Text,
View,
ActivityIndicatorIOS
} = React;
var ImageCapInsetsExample = require('./ImageCapInsetsExample');
var NetworkImageExample = React.createClass({
watchID: (null: ?number),
getInitialState: function() {
return {
error: false,
loading: false,
progress: 0
};
},
render: function() {
var loader = this.state.loading ?
<View style={styles.progress}>
<Text>{this.state.progress}%</Text>
<ActivityIndicatorIOS style={{marginLeft:5}}/>
</View> : null;
return this.state.error ?
<Text>{this.state.error}</Text> :
<Image
source={this.props.source}
style={[styles.base, {overflow: 'visible'}]}
onLoadStart={(e) => this.setState({loading: true})}
onError={(e) => this.setState({error: e.nativeEvent.error, loading: false})}
onProgress={(e) => this.setState({progress: Math.round(100 * e.nativeEvent.loaded / e.nativeEvent.total)})}
onLoad={() => this.setState({loading: false, error: false})}>
{loader}
</Image>;
}
});
exports.displayName = (undefined: ?string);
exports.framework = 'React';
exports.title = '<Image>';
@@ -59,6 +90,22 @@ exports.examples = [
);
},
},
{
title: 'Error Handler',
render: function() {
return (
<NetworkImageExample source={{uri: 'http://TYPO_ERROR_facebook.github.io/react/img/logo_og.png'}} />
);
},
},
{
title: 'Image Download Progress',
render: function() {
return (
<NetworkImageExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1'}}/>
);
},
},
{
title: 'Border Color',
render: function() {
@@ -194,23 +241,46 @@ exports.examples = [
'pixels to the tint color.',
render: function() {
return (
<View style={styles.horizontal}>
<Image
source={require('image!uie_thumb_normal')}
style={[styles.icon, {tintColor: 'blue' }]}
/>
<Image
source={require('image!uie_thumb_normal')}
style={[styles.icon, styles.leftMargin, {tintColor: 'green' }]}
/>
<Image
source={require('image!uie_thumb_normal')}
style={[styles.icon, styles.leftMargin, {tintColor: 'red' }]}
/>
<Image
source={require('image!uie_thumb_normal')}
style={[styles.icon, styles.leftMargin, {tintColor: 'black' }]}
/>
<View>
<View style={styles.horizontal}>
<Image
source={require('image!uie_thumb_normal')}
style={[styles.icon, {tintColor: '#5ac8fa' }]}
/>
<Image
source={require('image!uie_thumb_normal')}
style={[styles.icon, styles.leftMargin, {tintColor: '#4cd964' }]}
/>
<Image
source={require('image!uie_thumb_normal')}
style={[styles.icon, styles.leftMargin, {tintColor: '#ff2d55' }]}
/>
<Image
source={require('image!uie_thumb_normal')}
style={[styles.icon, styles.leftMargin, {tintColor: '#8e8e93' }]}
/>
</View>
<Text style={styles.sectionText}>
It also works with downloaded images:
</Text>
<View style={styles.horizontal}>
<Image
source={smallImage}
style={[styles.base, {tintColor: '#5ac8fa' }]}
/>
<Image
source={smallImage}
style={[styles.base, styles.leftMargin, {tintColor: '#4cd964' }]}
/>
<Image
source={smallImage}
style={[styles.base, styles.leftMargin, {tintColor: '#ff2d55' }]}
/>
<Image
source={smallImage}
style={[styles.base, styles.leftMargin, {tintColor: '#8e8e93' }]}
/>
</View>
</View>
);
},
@@ -277,12 +347,21 @@ var styles = StyleSheet.create({
width: 38,
height: 38,
},
progress: {
flex: 1,
alignItems: 'center',
flexDirection: 'row',
width: 100
},
leftMargin: {
marginLeft: 10,
},
background: {
backgroundColor: '#222222'
},
sectionText: {
marginVertical: 6,
},
nestedText: {
marginLeft: 12,
marginTop: 20,
+1 -2
View File
@@ -49,8 +49,7 @@ var LayoutEventExample = React.createClass({
() => {
console.log('layout animation done.');
this.addWrapText();
},
(error) => { throw new Error(JSON.stringify(error)); }
}
);
this.setState({
viewStyle: {
@@ -0,0 +1,149 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @flow
*/
'use strict';
var React = require('react-native');
var {
Image,
ListView,
TouchableHighlight,
StyleSheet,
Text,
View,
} = React;
var THUMB_URLS = [
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851549_767334479959628_274486868_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851579_767334503292959_179092627_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851589_767334513292958_1747022277_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851563_767334559959620_1193692107_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851593_767334566626286_1953955109_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851591_767334523292957_797560749_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851567_767334529959623_843148472_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851548_767334489959627_794462220_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851575_767334539959622_441598241_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851573_767334549959621_534583464_n.png',
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851583_767334573292952_1519550680_n.png',
];
var ListViewGridLayoutExample = React.createClass({
statics: {
title: '<ListView> - Grid Layout',
description: 'Flexbox grid layout.'
},
getInitialState: function() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
return {
dataSource: ds.cloneWithRows(this._genRows({})),
};
},
_pressData: ({}: {[key: number]: boolean}),
componentWillMount: function() {
this._pressData = {};
},
render: function() {
return (
// ListView wraps ScrollView and so takes on its properties.
// With that in mind you can use the ScrollView's contentContainerStyle prop to style the items.
<ListView
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
);
},
_renderRow: function(rowData: string, sectionID: number, rowID: number) {
var rowHash = Math.abs(hashCode(rowData));
var imgSource = {
uri: THUMB_URLS[rowHash % THUMB_URLS.length],
};
return (
<TouchableHighlight onPress={() => this._pressRow(rowID)} underlayColor="transparent">
<View>
<View style={styles.row}>
<Image style={styles.thumb} source={imgSource} />
<Text style={styles.text}>
{rowData}
</Text>
</View>
</View>
</TouchableHighlight>
);
},
_genRows: function(pressData: {[key: number]: boolean}): Array<string> {
var dataBlob = [];
for (var ii = 0; ii < 100; ii++) {
var pressedText = pressData[ii] ? ' (X)' : '';
dataBlob.push('Cell ' + ii + pressedText);
}
return dataBlob;
},
_pressRow: function(rowID: number) {
this._pressData[rowID] = !this._pressData[rowID];
this.setState({dataSource: this.state.dataSource.cloneWithRows(
this._genRows(this._pressData)
)});
},
});
/* eslint no-bitwise: 0 */
var hashCode = function(str) {
var hash = 15;
for (var ii = str.length - 1; ii >= 0; ii--) {
hash = ((hash << 5) - hash) + str.charCodeAt(ii);
}
return hash;
};
var styles = StyleSheet.create({
list: {
justifyContent: 'space-around',
flexDirection: 'row',
flexWrap: 'wrap'
},
row: {
justifyContent: 'center',
padding: 5,
margin: 3,
width: 100,
height: 100,
backgroundColor: '#F6F6F6',
alignItems: 'center',
borderWidth: 1,
borderRadius: 5,
borderColor: '#CCC'
},
thumb: {
width: 64,
height: 64
},
text: {
flex: 1,
marginTop: 5,
fontWeight: 'bold'
},
});
module.exports = ListViewGridLayoutExample;
+19 -21
View File
@@ -48,19 +48,19 @@ var Thumb = React.createClass({
},
render: function() {
return (
<TouchableOpacity onPress={this._onPressThumb}>
<View style={[styles.buttonContents, {flexDirection: this.state.dir}]}>
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
{this.state.dir === 'column' ?
<Text>
Oooo, look at this new text! So awesome it may just be crazy.
Let me keep typing here so it wraps at least one line.
</Text> :
<Text />
}
</View>
<TouchableOpacity
onPress={this._onPressThumb}
style={[styles.buttonContents, {flexDirection: this.state.dir}]}>
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
{this.state.dir === 'column' ?
<Text>
Oooo, look at this new text! So awesome it may just be crazy.
Let me keep typing here so it wraps at least one line.
</Text> :
<Text />
}
</TouchableOpacity>
);
}
@@ -127,14 +127,12 @@ var ListViewPagingExample = React.createClass({
<View><Text style={styles.text}>1 Like</Text></View> :
null;
return (
<TouchableOpacity onPress={this._onPressHeader}>
<View style={styles.header}>
{headerLikeText}
<View>
<Text style={styles.text}>
Table Header (click me)
</Text>
</View>
<TouchableOpacity onPress={this._onPressHeader} style={styles.header}>
{headerLikeText}
<View>
<Text style={styles.text}>
Table Header (click me)
</Text>
</View>
</TouchableOpacity>
);
+99
View File
@@ -0,0 +1,99 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @flow
*/
'use strict';
var React = require('react-native');
var {
Modal,
StyleSheet,
Text,
TouchableHighlight,
View,
} = React;
exports.displayName = (undefined: ?string);
exports.framework = 'React';
exports.title = '<Modal>';
exports.description = 'Component for presenting modal views.';
var ModalExample = React.createClass({
getInitialState: function() {
return {
openModal: null,
};
},
_closeModal: function() {
this.setState({openModal: null});
},
_openAnimatedModal: function() {
this.setState({openModal: 'animated'});
},
_openNotAnimatedModal: function() {
this.setState({openModal: 'not-animated'});
},
render: function() {
return (
<View>
<Modal animated={true} visible={this.state.openModal === 'animated'}>
<View style={styles.container}>
<Text>This modal was presented with animation.</Text>
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._closeModal}>
<Text>Close</Text>
</TouchableHighlight>
</View>
</Modal>
<Modal visible={this.state.openModal === 'not-animated'}>
<View style={styles.container}>
<Text>This modal was presented immediately, without animation.</Text>
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._closeModal}>
<Text>Close</Text>
</TouchableHighlight>
</View>
</Modal>
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._openAnimatedModal}>
<Text>Present Animated</Text>
</TouchableHighlight>
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._openNotAnimatedModal}>
<Text>Present Without Animation</Text>
</TouchableHighlight>
</View>
);
},
});
exports.examples = [
{
title: 'Modal Presentation',
description: 'Modals can be presented with or without animation',
render: () => <ModalExample />,
},
];
var styles = StyleSheet.create({
container: {
alignItems: 'center',
backgroundColor: '#f5fcff',
flex: 1,
justifyContent: 'center',
},
});
@@ -55,26 +55,24 @@ var BreadcrumbNavSample = React.createClass({
return (
<TouchableOpacity
onPress={() => navigator.push(_getRandomRoute())}>
<View>
<Text style={styles.titleText}>{route.title}</Text>
</View>
<Text style={styles.titleText}>{route.title}</Text>
</TouchableOpacity>
);
},
iconForRoute: function(route, navigator) {
return (
<TouchableOpacity onPress={() => {
navigator.popToRoute(route);
}}>
<View style={styles.crumbIconPlaceholder} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => { navigator.popToRoute(route); }}
style={styles.crumbIconPlaceholder}
/>
);
},
separatorForRoute: function(route, navigator) {
return (
<TouchableOpacity onPress={navigator.pop}>
<View style={styles.crumbSeparatorPlaceholder} />
</TouchableOpacity>
<TouchableOpacity
onPress={navigator.pop}
style={styles.crumbSeparatorPlaceholder}
/>
);
}
};
@@ -51,12 +51,11 @@ var NavigationBarRouteMapper = {
var previousRoute = navState.routeStack[index - 1];
return (
<TouchableOpacity
onPress={() => navigator.pop()}>
<View style={styles.navBarLeftButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
{previousRoute.title}
</Text>
</View>
onPress={() => navigator.pop()}
style={styles.navBarLeftButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
{previousRoute.title}
</Text>
</TouchableOpacity>
);
},
@@ -64,12 +63,11 @@ var NavigationBarRouteMapper = {
RightButton: function(route, navigator, index, navState) {
return (
<TouchableOpacity
onPress={() => navigator.push(newRandomRoute())}>
<View style={styles.navBarRightButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
Next
</Text>
</View>
onPress={() => navigator.push(newRandomRoute())}
style={styles.navBarRightButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
Next
</Text>
</TouchableOpacity>
);
},
@@ -83,6 +83,26 @@ exports.examples = [{
</View>
);
},
}, {
title: 'Status Bar Network Activity Indicator',
render() {
return (
<View>
<TouchableHighlight style={styles.wrapper}
onPress={() => StatusBarIOS.setNetworkActivityIndicatorVisible(true)}>
<View style={styles.button}>
<Text>setNetworkActivityIndicatorVisible(true)</Text>
</View>
</TouchableHighlight>
<TouchableHighlight style={styles.wrapper}
onPress={() => StatusBarIOS.setNetworkActivityIndicatorVisible(false)}>
<View style={styles.button}>
<Text>setNetworkActivityIndicatorVisible(false)</Text>
</View>
</TouchableHighlight>
</View>
);
},
}];
var styles = StyleSheet.create({
+6 -6
View File
@@ -39,11 +39,11 @@ var TabBarExample = React.createClass({
};
},
_renderContent: function(color: string, pageText: string) {
_renderContent: function(color: string, pageText: string, num?: number) {
return (
<View style={[styles.tabContent, {backgroundColor: color}]}>
<Text style={styles.tabText}>{pageText}</Text>
<Text style={styles.tabText}>{this.state.presses} re-renders of the More tab</Text>
<Text style={styles.tabText}>{num} re-renders of the {pageText}</Text>
</View>
);
},
@@ -51,8 +51,8 @@ var TabBarExample = React.createClass({
render: function() {
return (
<TabBarIOS
tintColor="black"
barTintColor="#3abeff">
tintColor="white"
barTintColor="darkslateblue">
<TabBarIOS.Item
title="Blue Tab"
selected={this.state.selectedTab === 'blueTab'}
@@ -73,7 +73,7 @@ var TabBarExample = React.createClass({
notifCount: this.state.notifCount + 1,
});
}}>
{this._renderContent('#783E33', 'Red Tab')}
{this._renderContent('#783E33', 'Red Tab', this.state.notifCount)}
</TabBarIOS.Item>
<TabBarIOS.Item
systemIcon="more"
@@ -84,7 +84,7 @@ var TabBarExample = React.createClass({
presses: this.state.presses + 1
});
}}>
{this._renderContent('#21551C', 'Green Tab')}
{this._renderContent('#21551C', 'Green Tab', this.state.presses)}
</TabBarIOS.Item>
</TabBarIOS>
);
+29 -1
View File
@@ -269,7 +269,7 @@ exports.examples = [
right right right right right right right right right right right right right
</Text>
<Text style={{textAlign: 'justify'}}>
justify: this text component's contents are laid out with "textAlign: justify"
justify: this text component{"'"}s contents are laid out with "textAlign: justify"
and as you can see all of the lines except the last one span the
available width of the parent container.
</Text>
@@ -369,6 +369,34 @@ exports.examples = [
</View>
);
},
}, {
title: 'Text highlighting (tap the link to see highlight)',
render: function() {
return (
<View>
<Text>Lorem ipsum dolor sit amet, <Text suppressHighlighting={false} style={{backgroundColor: 'white', textDecorationLine: 'underline', color: 'blue'}} onPress={() => null}>consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud</Text> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</Text>
</View>
);
},
}, {
title: 'allowFontScaling attribute',
render: function() {
return (
<View>
<Text>
By default, text will respect Text Size accessibility setting on iOS.
It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
{" "}<Text style={{fontWeight: 'bold'}}>Settings.app - Display & Brightness - Text Size</Text>
</Text>
<Text style={{marginTop: 10}}>
You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop.
</Text>
<Text allowFontScaling={false} style={{marginTop: 20}}>
This text will not scale.
</Text>
</View>
);
},
}];
var styles = StyleSheet.create({
+63 -16
View File
@@ -33,7 +33,7 @@ var WithLabel = React.createClass({
{this.props.children}
</View>
);
}
},
});
var TextEventsExample = React.createClass({
@@ -41,13 +41,17 @@ var TextEventsExample = React.createClass({
return {
curText: '<No Event>',
prevText: '<No Event>',
prev2Text: '<No Event>',
};
},
updateText: function(text) {
this.setState({
curText: text,
prevText: this.state.curText,
this.setState((state) => {
return {
curText: text,
prevText: state.curText,
prev2Text: state.prevText,
};
});
},
@@ -73,13 +77,43 @@ var TextEventsExample = React.createClass({
/>
<Text style={styles.eventLabel}>
{this.state.curText}{'\n'}
(prev: {this.state.prevText})
(prev: {this.state.prevText}){'\n'}
(prev2: {this.state.prev2Text})
</Text>
</View>
);
}
});
class RewriteExample extends React.Component {
constructor(props) {
super(props);
this.state = {text: ''};
}
render() {
var limit = 20;
var remainder = limit - this.state.text.length;
var remainderColor = remainder > 5 ? 'blue' : 'red';
return (
<View style={styles.rewriteContainer}>
<TextInput
multiline={false}
maxLength={limit}
onChangeText={(text) => {
text = text.replace(/ /g, '_');
this.setState({text});
}}
style={styles.default}
value={this.state.text}
/>
<Text style={[styles.remainder, {color: remainderColor}]}>
{remainder}
</Text>
</View>
);
}
}
var styles = StyleSheet.create({
page: {
paddingBottom: 300,
@@ -125,12 +159,19 @@ var styles = StyleSheet.create({
flex: 1,
},
label: {
width: 120,
justifyContent: 'flex-end',
flexDirection: 'row',
width: 115,
alignItems: 'flex-end',
marginRight: 10,
paddingTop: 2,
},
rewriteContainer: {
flexDirection: 'row',
alignItems: 'center',
},
remainder: {
textAlign: 'right',
width: 24,
},
});
exports.displayName = (undefined: ?string);
@@ -143,6 +184,12 @@ exports.examples = [
return <TextInput autoFocus={true} style={styles.default} />;
}
},
{
title: "Live Re-Write (<sp> -> '_') + maxLength",
render: function() {
return <RewriteExample />;
}
},
{
title: 'Auto-capitalize',
render: function() {
@@ -268,7 +315,7 @@ exports.examples = [
return (
<View>
<WithLabel label="true">
<TextInput password={true} style={styles.default} value="abc" />
<TextInput password={true} style={styles.default} defaultValue="abc" />
</WithLabel>
</View>
);
@@ -276,7 +323,7 @@ exports.examples = [
},
{
title: 'Event handling',
render: function(): ReactElement { return <TextEventsExample /> },
render: function(): ReactElement { return <TextEventsExample />; },
},
{
title: 'Colored input text',
@@ -285,11 +332,11 @@ exports.examples = [
<View>
<TextInput
style={[styles.default, {color: 'blue'}]}
value="Blue"
defaultValue="Blue"
/>
<TextInput
style={[styles.default, {color: 'green'}]}
value="Green"
defaultValue="Green"
/>
</View>
);
@@ -336,7 +383,7 @@ exports.examples = [
<WithLabel label="clearTextOnFocus">
<TextInput
placeholder="text is cleared on focus"
value="text is cleared on focus"
defaultValue="text is cleared on focus"
style={styles.default}
clearTextOnFocus={true}
/>
@@ -344,7 +391,7 @@ exports.examples = [
<WithLabel label="selectTextOnFocus">
<TextInput
placeholder="text is selected on focus"
value="text is selected on focus"
defaultValue="text is selected on focus"
style={styles.default}
selectTextOnFocus={true}
/>
@@ -364,7 +411,7 @@ exports.examples = [
style={styles.multiline}
/>
<TextInput
placeholder="mutliline text input with font styles and placeholder"
placeholder="multiline text input with font styles and placeholder"
multiline={true}
clearTextOnFocus={true}
autoCorrect={true}
@@ -374,7 +421,7 @@ exports.examples = [
style={[styles.multiline, styles.multilineWithFontStyles]}
/>
<TextInput
placeholder="uneditable mutliline text input"
placeholder="uneditable multiline text input"
editable={false}
multiline={true}
style={styles.multiline}
+171 -44
View File
@@ -1,61 +1,85 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* @providesModule TransformExample
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
var React = require('React');
var React = require('react-native');
var {
Animated,
StyleSheet,
Text,
View,
} = React;
var TimerMixin = require('react-timer-mixin');
var UIExplorerBlock = require('./UIExplorerBlock');
var UIExplorerPage = require('./UIExplorerPage');
var TransformExample = React.createClass({
mixins: [TimerMixin],
var Flip = React.createClass({
getInitialState() {
return {
interval: this.setInterval(this._update, 800),
pulse: false,
theta: new Animated.Value(45),
};
},
componentDidMount() {
this._animate();
},
_animate() {
this.state.theta.setValue(0);
Animated.timing(this.state.theta, {
toValue: 360,
duration: 5000,
}).start(this._animate);
},
render() {
return (
<UIExplorerPage title="Transforms">
<UIExplorerBlock title="foo bar">
<View style={{height: 500}}>
<View style={styles.box1} />
<View style={styles.box2} />
<View style={styles.box3step1} />
<View style={styles.box3step2} />
<View style={styles.box3step3} />
<View style={styles.box4} />
<View style={[
styles.box5,
this.state.pulse ? styles.box5Transform : null
]} />
</View>
</UIExplorerBlock>
</UIExplorerPage>
<View style={styles.flipCardContainer}>
<Animated.View style={[
styles.flipCard,
{transform: [
{perspective: 850},
{rotateX: this.state.theta.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg']
})},
]}]}>
<Text style={styles.flipText}>
This text is flipping great.
</Text>
</Animated.View>
<Animated.View style={[styles.flipCard, {
position: 'absolute',
top: 0,
backgroundColor: 'red',
transform: [
{perspective: 850},
{rotateX: this.state.theta.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg']
})},
]}]}>
<Text style={styles.flipText}>
On the flip side...
</Text>
</Animated.View>
</View>
);
},
_update() {
this.setState({
pulse: !this.state.pulse,
});
},
}
});
var styles = StyleSheet.create({
container: {
height: 500,
},
box1: {
left: 0,
backgroundColor: 'green',
@@ -88,7 +112,7 @@ var styles = StyleSheet.create({
},
box3step1: {
left: 0,
backgroundColor: '#ffb6c1', // lightpink
backgroundColor: 'lightpink',
height: 50,
position: 'absolute',
top: 0,
@@ -99,7 +123,7 @@ var styles = StyleSheet.create({
},
box3step2: {
left: 0,
backgroundColor: '#ff69b4', //hotpink
backgroundColor: 'hotpink',
height: 50,
opacity: 0.5,
position: 'absolute',
@@ -113,7 +137,7 @@ var styles = StyleSheet.create({
},
box3step3: {
left: 0,
backgroundColor: '#ff1493', // deeppink
backgroundColor: 'deeppink',
height: 50,
opacity: 0.5,
position: 'absolute',
@@ -129,7 +153,7 @@ var styles = StyleSheet.create({
},
box4: {
left: 0,
backgroundColor: '#ff8c00', // darkorange
backgroundColor: 'darkorange',
height: 50,
position: 'absolute',
top: 0,
@@ -141,7 +165,7 @@ var styles = StyleSheet.create({
width: 100,
},
box5: {
backgroundColor: '#800000', // maroon
backgroundColor: 'maroon',
height: 50,
position: 'absolute',
right: 0,
@@ -155,7 +179,110 @@ var styles = StyleSheet.create({
{scale: 2},
],
},
flipCardContainer: {
marginVertical: 40,
flex: 1,
alignSelf: 'center',
},
flipCard: {
width: 200,
height: 200,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'blue',
backfaceVisibility: 'hidden',
},
flipText: {
width: 90,
fontSize: 20,
color: 'white',
fontWeight: 'bold',
}
});
module.exports = TransformExample;
exports.title = 'Transforms';
exports.description = 'View transforms';
exports.examples = [
{
title: 'Perspective',
description: 'perspective: 850, rotateX: Animated.timing(0 -> 360)',
render(): ReactElement { return <Flip />; }
},
{
title: 'Translate, Rotate, Scale',
description: "translateX: 100, translateY: 50, rotate: '30deg', scaleX: 2, scaleY: 2",
render() {
return (
<View style={styles.container}>
<View style={styles.box1} />
</View>
);
}
},
{
title: 'Scale, Translate, Rotate, ',
description: "scaleX: 2, scaleY: 2, translateX: 100, translateY: 50, rotate: '30deg'",
render() {
return (
<View style={styles.container}>
<View style={styles.box2} />
</View>
);
}
},
{
title: 'Rotate',
description: "rotate: '30deg'",
render() {
return (
<View style={styles.container}>
<View style={styles.box3step1} />
</View>
);
}
},
{
title: 'Rotate, Scale',
description: "rotate: '30deg', scaleX: 2, scaleY: 2",
render() {
return (
<View style={styles.container}>
<View style={styles.box3step2} />
</View>
);
}
},
{
title: 'Rotate, Scale, Translate ',
description: "rotate: '30deg', scaleX: 2, scaleY: 2, translateX: 100, translateY: 50",
render() {
return (
<View style={styles.container}>
<View style={styles.box3step3} />
</View>
);
}
},
{
title: 'Translate, Scale, Rotate',
description: "translate: [200, 350], scale: 2.5, rotate: '-0.2rad'",
render() {
return (
<View style={styles.container}>
<View style={styles.box4} />
</View>
);
}
},
{
title: 'Translate, Rotate, Scale',
description: "translate: [-50, 35], rotate: '50deg', scale: 2",
render() {
return (
<View style={styles.container}>
<View style={[styles.box5, styles.box5Transform]} />
</View>
);
}
}
];
@@ -7,19 +7,25 @@
objects = {
/* Begin PBXBuildFile section */
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1300627E1B59179B0043FE5A /* RCTGzipTests.m */; };
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 */; };
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; };
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A151B53CD440074A87E /* RCTCacheTests.m */; };
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; };
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; };
139FDEDB1B0651FB00C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.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 */; };
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */; };
13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; };
141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 141FC1201B222EBB004D5FFB /* IntegrationTests.m */; };
143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */; };
144D21241B2204C5006DB32B /* RCTClippingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTClippingTests.m */; };
144D21241B2204C5006DB32B /* RCTClipRectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTClipRectTests.m */; };
147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; };
1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */; };
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */; };
@@ -47,6 +53,7 @@
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; };
3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; };
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; };
83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; };
D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; };
/* End PBXBuildFile section */
@@ -153,11 +160,15 @@
/* Begin PBXFileReference section */
004D289E1AAF61C70097A701 /* UIExplorerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
1300627E1B59179B0043FE5A /* RCTGzipTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGzipTests.m; sourceTree = "<group>"; };
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>"; };
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
138D6A151B53CD440074A87E /* RCTCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCacheTests.m; sourceTree = "<group>"; };
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = "<group>"; };
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = "<group>"; };
139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* UIExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = UIExplorer/AppDelegate.h; sourceTree = "<group>"; };
@@ -167,6 +178,8 @@
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = "<group>"; };
13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSONTests.m; sourceTree = "<group>"; };
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMethodArgumentTests.m; sourceTree = "<group>"; };
141FC1201B222EBB004D5FFB /* IntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntegrationTests.m; sourceTree = "<group>"; };
143BC57E1B21E18100462512 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
143BC5811B21E18100462512 /* testLayoutExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testLayoutExampleSnapshot_1@2x.png"; sourceTree = "<group>"; };
@@ -178,7 +191,7 @@
143BC5951B21E3E100462512 /* UIExplorerIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
143BC5981B21E3E100462512 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerSnapshotTests.m; sourceTree = "<group>"; };
144D21231B2204C5006DB32B /* RCTClippingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTClippingTests.m; sourceTree = "<group>"; };
144D21231B2204C5006DB32B /* RCTClipRectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTClipRectTests.m; sourceTree = "<group>"; };
1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAllocationTests.m; sourceTree = "<group>"; };
1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeTests.m; sourceTree = "<group>"; };
1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutorTests.m; sourceTree = "<group>"; };
@@ -202,6 +215,7 @@
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = "<group>"; };
83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = "<group>"; };
D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -342,11 +356,17 @@
children = (
1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */,
1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */,
144D21231B2204C5006DB32B /* RCTClippingTests.m */,
138D6A151B53CD440074A87E /* RCTCacheTests.m */,
144D21231B2204C5006DB32B /* RCTClipRectTests.m */,
1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */,
1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */,
1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */,
1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */,
1300627E1B59179B0043FE5A /* RCTGzipTests.m */,
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */,
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */,
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */,
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */,
1497CFAA1B21F5E400C1F8F2 /* RCTSparseArrayTests.m */,
1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */,
143BC57E1B21E18100462512 /* Info.plist */,
@@ -383,6 +403,7 @@
children = (
141FC1201B222EBB004D5FFB /* IntegrationTests.m */,
143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */,
83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */,
143BC5971B21E3E100462512 /* Supporting Files */,
);
path = UIExplorerIntegrationTests;
@@ -772,14 +793,20 @@
buildActionMask = 2147483647;
files = (
1497CFB01B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m in Sources */,
144D21241B2204C5006DB32B /* RCTClippingTests.m in Sources */,
144D21241B2204C5006DB32B /* RCTClipRectTests.m in Sources */,
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */,
1497CFB21B21F5E400C1F8F2 /* RCTSparseArrayTests.m in Sources */,
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */,
1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */,
1497CFAE1B21F5E400C1F8F2 /* RCTContextExecutorTests.m in Sources */,
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */,
1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */,
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */,
138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */,
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */,
1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */,
13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */,
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -797,6 +824,7 @@
buildActionMask = 2147483647;
files = (
141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */,
83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */,
143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1014,9 +1042,11 @@
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
@@ -1028,6 +1058,7 @@
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/../../Libraries/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
@@ -1068,9 +1099,11 @@
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
@@ -1082,6 +1115,7 @@
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
"$(SRCROOT)/../../Libraries/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
+56 -36
View File
@@ -14,49 +14,23 @@
#import "AppDelegate.h"
#import "RCTBridge.h"
#import "RCTJavaScriptLoader.h"
#import "RCTRootView.h"
@interface AppDelegate() <RCTBridgeDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self
launchOptions:launchOptions];
/**
* Loading JavaScript code - uncomment the one you want.
*
* OPTION 1
* Load from development server. Start the server from the repository root:
*
* $ npm start
*
* To run on device, change `localhost` to the IP address of your computer
* (you can get this by typing `ifconfig` into the terminal and selecting the
* `inet` value under `en0:`) and make sure your computer and iOS device are
* on the same Wi-Fi network.
*/
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle?dev=true"];
/**
* OPTION 2
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
* to your Xcode project folder and run
*
* $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.includeRequire.runModule.bundle' -o main.jsbundle
*
* then add the `main.jsbundle` file to your project and uncomment this line:
*/
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#if RUNNING_ON_CI
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"UIExplorerApp"
launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"UIExplorerApp"];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[UIViewController alloc] init];
@@ -66,4 +40,50 @@
return YES;
}
- (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge
{
NSURL *sourceURL;
/**
* Loading JavaScript code - uncomment the one you want.
*
* OPTION 1
* Load from development server. Start the server from the repository root:
*
* $ npm start
*
* To run on device, change `localhost` to the IP address of your computer
* (you can get this by typing `ifconfig` into the terminal and selecting the
* `inet` value under `en0:`) and make sure your computer and iOS device are
* on the same Wi-Fi network.
*/
sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle?dev=true"];
/**
* OPTION 2
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
* to your Xcode project folder and run
*
* $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle' -o main.jsbundle
*
* then add the `main.jsbundle` file to your project and uncomment this line:
*/
// sourceURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#if RUNNING_ON_CI
sourceURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
return sourceURL;
}
- (void)loadSourceForBridge:(RCTBridge *)bridge
withBlock:(RCTSourceLoadBlock)loadCallback
{
[RCTJavaScriptLoader loadBundleAtURL:[self sourceURLForBridge:bridge]
onComplete:loadCallback];
}
@end
+1 -1
View File
@@ -17,7 +17,7 @@
'use strict';
var React = require('react-native');
var UIExplorerList = require('./UIExplorerList');
var UIExplorerList = require('./UIExplorerList.ios');
var {
AppRegistry,
NavigatorIOS,
@@ -26,11 +26,11 @@
- (void)setUp
{
#if __LP64__
RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
RCTAssert(version.majorVersion == 8 || version.minorVersion >= 3, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerIntegrationTests/js/IntegrationTestsApp", nil);
}
@@ -0,0 +1,192 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
#import "UIView+React.h"
@interface RCTUIManager (Testing)
- (void)_manageChildren:(NSNumber *)containerReactTag
moveFromIndices:(NSArray *)moveFromIndices
moveToIndices:(NSArray *)moveToIndices
addChildReactTags:(NSArray *)addChildReactTags
addAtIndices:(NSArray *)addAtIndices
removeAtIndices:(NSArray *)removeAtIndices
registry:(RCTSparseArray *)registry;
@property (nonatomic, readonly) RCTSparseArray *viewRegistry;
@end
@interface RCTUIManagerScenarioTests : XCTestCase
@property (nonatomic, readwrite, strong) RCTUIManager *uiManager;
@end
@implementation RCTUIManagerScenarioTests
- (void)setUp
{
[super setUp];
_uiManager = [[RCTUIManager alloc] init];
// Register 20 views to use in the tests
for (NSInteger i = 1; i <= 20; i++) {
UIView *registeredView = [[UIView alloc] init];
[registeredView setReactTag:@(i)];
_uiManager.viewRegistry[i] = registeredView;
}
}
- (void)testManagingChildrenToAddViews
{
UIView *containerView = _uiManager.viewRegistry[20];
NSMutableArray *addedViews = [NSMutableArray array];
NSArray *tagsToAdd = @[@1, @2, @3, @4, @5];
NSArray *addAtIndices = @[@0, @1, @2, @3, @4];
for (NSNumber *tag in tagsToAdd) {
[addedViews addObject:_uiManager.viewRegistry[tag]];
}
// Add views 1-5 to view 20
[_uiManager _manageChildren:@20
moveFromIndices:nil
moveToIndices:nil
addChildReactTags:tagsToAdd
addAtIndices:addAtIndices
removeAtIndices:nil
registry:_uiManager.viewRegistry];
XCTAssertTrue([[containerView reactSubviews] count] == 5,
@"Expect to have 5 react subviews after calling manage children \
with 5 tags to add, instead have %lu", (unsigned long)[[containerView reactSubviews] count]);
for (UIView *view in addedViews) {
XCTAssertTrue([view superview] == containerView,
@"Expected to have manage children successfully add children");
[view removeFromSuperview];
}
}
- (void)testManagingChildrenToRemoveViews
{
UIView *containerView = _uiManager.viewRegistry[20];
NSMutableArray *removedViews = [NSMutableArray array];
NSArray *removeAtIndices = @[@0, @4, @8, @12, @16];
for (NSNumber *index in removeAtIndices) {
NSNumber *reactTag = @([index integerValue] + 2);
[removedViews addObject:_uiManager.viewRegistry[reactTag]];
}
for (NSInteger i = 2; i < 20; i++) {
UIView *view = _uiManager.viewRegistry[i];
[containerView addSubview:view];
}
// Remove views 1-5 from view 20
[_uiManager _manageChildren:@20
moveFromIndices:nil
moveToIndices:nil
addChildReactTags:nil
addAtIndices:nil
removeAtIndices:removeAtIndices
registry:_uiManager.viewRegistry];
XCTAssertEqual(containerView.reactSubviews.count, (NSUInteger)13,
@"Expect to have 13 react subviews after calling manage children\
with 5 tags to remove and 18 prior children, instead have %zd",
containerView.reactSubviews.count);
for (UIView *view in removedViews) {
XCTAssertTrue([view superview] == nil,
@"Expected to have manage children successfully remove children");
// After removing views are unregistered - we need to reregister
_uiManager.viewRegistry[view.reactTag] = view;
}
for (NSInteger i = 2; i < 20; i++) {
UIView *view = _uiManager.viewRegistry[i];
if (![removedViews containsObject:view]) {
XCTAssertTrue([view superview] == containerView,
@"Should not have removed view with react tag %ld during delete but did", (long)i);
[view removeFromSuperview];
}
}
}
// We want to start with views 1-10 added at indices 0-9
// Then we'll remove indices 2, 3, 5 and 8
// Add views 11 and 12 to indices 0 and 6
// And move indices 4 and 9 to 1 and 7
// So in total it goes from:
// [1,2,3,4,5,6,7,8,9,10]
// to
// [11,5,1,2,7,8,12,10]
- (void)testManagingChildrenToAddRemoveAndMove
{
UIView *containerView = _uiManager.viewRegistry[20];
NSArray *removeAtIndices = @[@2, @3, @5, @8];
NSArray *addAtIndices = @[@0, @6];
NSArray *tagsToAdd = @[@11, @12];
NSArray *moveFromIndices = @[@4, @9];
NSArray *moveToIndices = @[@1, @7];
// We need to keep these in array to keep them around
NSMutableArray *viewsToRemove = [NSMutableArray array];
for (NSUInteger i = 0; i < removeAtIndices.count; i++) {
NSNumber *reactTagToRemove = @([removeAtIndices[i] integerValue] + 1);
UIView *viewToRemove = _uiManager.viewRegistry[reactTagToRemove];
[viewsToRemove addObject:viewToRemove];
}
for (NSInteger i = 1; i < 11; i++) {
UIView *view = _uiManager.viewRegistry[i];
[containerView addSubview:view];
}
[_uiManager _manageChildren:@20
moveFromIndices:moveFromIndices
moveToIndices:moveToIndices
addChildReactTags:tagsToAdd
addAtIndices:addAtIndices
removeAtIndices:removeAtIndices
registry:_uiManager.viewRegistry];
XCTAssertTrue([[containerView reactSubviews] count] == 8,
@"Expect to have 8 react subviews after calling manage children,\
instead have the following subviews %@", [containerView reactSubviews]);
NSArray *expectedReactTags = @[@11, @5, @1, @2, @7, @8, @12, @10];
for (NSUInteger i = 0; i < containerView.subviews.count; i++) {
XCTAssertEqualObjects([[containerView reactSubviews][i] reactTag], expectedReactTags[i],
@"Expected subview at index %ld to have react tag #%@ but has tag #%@",
(long)i, expectedReactTags[i], [[containerView reactSubviews][i] reactTag]);
}
// Clean up after ourselves
for (NSInteger i = 1; i < 13; i++) {
UIView *view = _uiManager.viewRegistry[i];
[view removeFromSuperview];
}
for (UIView *view in viewsToRemove) {
_uiManager.viewRegistry[view.reactTag] = view;
}
}
@end
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

@@ -32,11 +32,12 @@
- (void)setUp
{
#ifdef __LP64__
RCTAssert(!__LP64__, @"Snapshot tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#if __LP64__
RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSString *version = [[UIDevice currentDevice] systemVersion];
RCTAssert([version isEqualToString:@"8.3"], @"Snapshot tests should be run on iOS 8.3, found %@", version);
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
RCTAssert(version.majorVersion == 8 || version.minorVersion >= 3, @"Snapshot tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerApp.ios", nil);
_runner.recordMode = NO;
}
@@ -10,6 +10,8 @@
*/
'use strict';
require('regenerator/runtime');
var React = require('react-native');
var {
@@ -59,12 +61,12 @@ var IntegrationTestsApp = React.createClass({
<View style={styles.separator} />
<ScrollView>
{TESTS.map((test) => [
<TouchableOpacity onPress={() => this.setState({test})}>
<View style={styles.row}>
<Text style={styles.testName}>
{test.displayName}
</Text>
</View>
<TouchableOpacity
onPress={() => this.setState({test})}
style={styles.row}>
<Text style={styles.testName}>
{test.displayName}
</Text>
</TouchableOpacity>,
<View style={styles.separator} />
])}
@@ -51,8 +51,7 @@ var LayoutEventsTest = React.createClass({
() => {
debug('layout animation done.');
this.checkLayout(this.addWrapText);
},
(error) => { throw new Error(JSON.stringify(error)); }
}
);
this.setState({viewStyle: {margin: 60}});
},
@@ -17,13 +17,18 @@ var PromiseTest = React.createClass({
shouldResolve: false,
shouldReject: false,
shouldSucceedAsync: false,
shouldThrowAsync: false,
componentDidMount() {
Promise.all([
this.testShouldResolve(),
this.testShouldReject(),
this.testShouldSucceedAsync(),
this.testShouldThrowAsync(),
]).then(() => RCTTestModule.markTestPassed(
this.shouldResolve && this.shouldReject
this.shouldResolve && this.shouldReject &&
this.shouldSucceedAsync && this.shouldThrowAsync
));
},
@@ -41,6 +46,24 @@ var PromiseTest = React.createClass({
.catch(() => this.shouldReject = true);
},
async testShouldSucceedAsync() {
try {
await RCTTestModule.shouldResolve();
this.shouldSucceedAsync = true;
} catch (e) {
this.shouldSucceedAsync = false;
}
},
async testShouldThrowAsync() {
try {
await RCTTestModule.shouldReject();
this.shouldThrowAsync = false;
} catch (e) {
this.shouldThrowAsync = true;
}
},
render() {
return <React.View />;
}
+185
View File
@@ -0,0 +1,185 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @flow
*/
'use strict';
var React = require('react-native');
var {
AppRegistry,
Settings,
StyleSheet,
} = React;
var { TestModule } = React.addons;
import type { NavigationContext } from 'NavigationContext';
var UIExplorerListBase = require('./UIExplorerListBase');
var COMPONENTS = [
require('./ActivityIndicatorIOSExample'),
require('./DatePickerIOSExample'),
require('./ImageExample'),
require('./LayoutEventsExample'),
require('./ListViewExample'),
require('./ListViewGridLayoutExample'),
require('./ListViewPagingExample'),
require('./MapViewExample'),
require('./ModalExample'),
require('./Navigator/NavigatorExample'),
require('./NavigatorIOSColorsExample'),
require('./NavigatorIOSExample'),
require('./PickerIOSExample'),
require('./ProgressViewIOSExample'),
require('./ScrollViewExample'),
require('./SegmentedControlIOSExample'),
require('./SliderIOSExample'),
require('./SwitchIOSExample'),
require('./TabBarIOSExample'),
require('./TextExample.ios'),
require('./TextInputExample'),
require('./TouchableExample'),
require('./ViewExample'),
require('./WebViewExample'),
];
var APIS = [
require('./AccessibilityIOSExample'),
require('./ActionSheetIOSExample'),
require('./AdSupportIOSExample'),
require('./AlertIOSExample'),
require('./AnimationExample/AnExApp'),
require('./AppStateIOSExample'),
require('./AsyncStorageExample'),
require('./BorderExample'),
require('./CameraRollExample.ios'),
require('./GeolocationExample'),
require('./LayoutExample'),
require('./NetInfoExample'),
require('./PanResponderExample'),
require('./PointerEventsExample'),
require('./PushNotificationIOSExample'),
require('./StatusBarIOSExample'),
require('./TimerExample'),
require('./VibrationIOSExample'),
require('./XHRExample'),
require('./ImageEditingExample'),
];
// Register suitable examples for snapshot tests
COMPONENTS.concat(APIS).forEach((Example) => {
if (Example.displayName) {
var Snapshotter = React.createClass({
componentDidMount: function() {
// View is still blank after first RAF :\
global.requestAnimationFrame(() =>
global.requestAnimationFrame(() => TestModule.verifySnapshot(
TestModule.markTestPassed
)
));
},
render: function() {
var Renderable = UIExplorerListBase.makeRenderable(Example);
return <Renderable />;
},
});
AppRegistry.registerComponent(Example.displayName, () => Snapshotter);
}
});
type Props = {
navigator: {
navigationContext: NavigationContext,
push: (route: {title: string, component: ReactClass<any,any,any>}) => void,
},
onExternalExampleRequested: Function,
};
class UIExplorerList extends React.Component {
props: Props;
render() {
return (
<UIExplorerListBase
components={COMPONENTS}
apis={APIS}
searchText={Settings.get('searchText')}
renderAdditionalView={this.renderAdditionalView.bind(this)}
search={this.search.bind(this)}
onPressRow={this.onPressRow.bind(this)}
/>
);
}
componentDidMount() {
var wasUIExplorer = false;
var didOpenExample = false;
this.props.navigator.navigationContext.addListener('didfocus', (event) => {
var isUIExplorer = event.data.route.title === 'UIExplorer';
if (!didOpenExample && isUIExplorer) {
didOpenExample = true;
var visibleExampleTitle = Settings.get('visibleExample');
if (visibleExampleTitle) {
var predicate = (example) => example.title === visibleExampleTitle;
var foundExample = APIS.find(predicate) || COMPONENTS.find(predicate);
if (foundExample) {
setTimeout(() => this._openExample(foundExample), 100);
}
} else if (!wasUIExplorer && isUIExplorer) {
Settings.set({visibleExample: null});
}
}
wasUIExplorer = isUIExplorer;
});
}
renderAdditionalView(renderRow: Function, renderTextInput: Function): React.Component {
return renderTextInput(styles.searchTextInput);
}
search(text: mixed) {
Settings.set({searchText: text});
}
_openExample(example: any) {
if (example.external) {
this.props.onExternalExampleRequested(example);
return;
}
var Component = UIExplorerListBase.makeRenderable(example);
this.props.navigator.push({
title: Component.title,
component: Component,
});
}
onPressRow(example: any) {
Settings.set({visibleExample: example.title});
this._openExample(example);
}
}
var styles = StyleSheet.create({
searchTextInput: {
height: 30,
},
});
module.exports = UIExplorerList;
-351
View File
@@ -1,351 +0,0 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @flow
*/
'use strict';
var React = require('react-native');
var {
AppRegistry,
ListView,
PixelRatio,
Platform,
Settings,
StyleSheet,
Text,
TextInput,
TouchableHighlight,
View,
} = React;
var { TestModule } = React.addons;
import type { ExampleModule } from 'ExampleTypes';
import type { NavigationContext } from 'NavigationContext';
var createExamplePage = require('./createExamplePage');
var COMMON_COMPONENTS = [
require('./ImageExample'),
require('./LayoutEventsExample'),
require('./ListViewExample'),
require('./ListViewPagingExample'),
require('./MapViewExample'),
require('./Navigator/NavigatorExample'),
require('./ScrollViewExample'),
require('./TextInputExample'),
require('./TouchableExample'),
require('./ViewExample'),
require('./WebViewExample'),
];
var COMMON_APIS = [
require('./AnimationExample/AnExApp'),
require('./GeolocationExample'),
require('./LayoutExample'),
require('./PanResponderExample'),
require('./PointerEventsExample'),
];
if (Platform.OS === 'ios') {
var COMPONENTS = COMMON_COMPONENTS.concat([
require('./ActivityIndicatorIOSExample'),
require('./DatePickerIOSExample'),
require('./NavigatorIOSColorsExample'),
require('./NavigatorIOSExample'),
require('./PickerIOSExample'),
require('./ProgressViewIOSExample'),
require('./SegmentedControlIOSExample'),
require('./SliderIOSExample'),
require('./SwitchIOSExample'),
require('./TabBarIOSExample'),
require('./TextExample.ios'),
]);
var APIS = COMMON_APIS.concat([
require('./AccessibilityIOSExample'),
require('./ActionSheetIOSExample'),
require('./AdSupportIOSExample'),
require('./AlertIOSExample'),
require('./AppStateIOSExample'),
require('./AsyncStorageExample'),
require('./BorderExample'),
require('./CameraRollExample.ios'),
require('./NetInfoExample'),
require('./PushNotificationIOSExample'),
require('./StatusBarIOSExample'),
require('./TimerExample'),
require('./VibrationIOSExample'),
require('./XHRExample'),
]);
} else if (Platform.OS === 'android') {
var COMPONENTS = COMMON_COMPONENTS.concat([
]);
var APIS = COMMON_APIS.concat([
]);
}
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (h1, h2) => h1 !== h2,
});
function makeRenderable(example: any): ReactClass<any, any, any> {
return example.examples ?
createExamplePage(null, example) :
example;
}
// Register suitable examples for snapshot tests
COMPONENTS.concat(APIS).forEach((Example) => {
if (Example.displayName) {
var Snapshotter = React.createClass({
componentDidMount: function() {
// View is still blank after first RAF :\
global.requestAnimationFrame(() =>
global.requestAnimationFrame(() => TestModule.verifySnapshot(
TestModule.markTestPassed
)
));
},
render: function() {
var Renderable = makeRenderable(Example);
return <Renderable />;
},
});
AppRegistry.registerComponent(Example.displayName, () => Snapshotter);
}
});
type Props = {
navigator: {
navigationContext: NavigationContext,
push: (route: {title: string, component: ReactClass<any,any,any>}) => void,
},
onExternalExampleRequested: Function,
onSelectExample: Function,
isInDrawer: bool,
};
class UIExplorerList extends React.Component {
props: Props;
constructor(props: Props) {
super(props);
this.state = {
dataSource: ds.cloneWithRowsAndSections({
components: COMPONENTS,
apis: APIS,
}),
searchText: Platform.OS === 'ios' ? Settings.get('searchText') : '',
};
}
componentWillMount() {
this.props.navigator.navigationContext.addListener('didfocus', function(event) {
if (event.data.route.title === 'UIExplorer') {
Settings.set({visibleExample: null});
}
});
}
componentDidMount() {
this._search(this.state.searchText);
var visibleExampleTitle = Settings.get('visibleExample');
if (visibleExampleTitle) {
var predicate = (example) => example.title === visibleExampleTitle;
var foundExample = APIS.find(predicate) || COMPONENTS.find(predicate);
if (foundExample) {
setTimeout(() => this._openExample(foundExample), 100);
}
}
}
render() {
if (Platform.OS === 'ios' ||
(Platform.OS === 'android' && !this.props.isInDrawer)) {
var platformTextInputStyle =
Platform.OS === 'ios' ? styles.searchTextInputIOS :
Platform.OS === 'android' ? styles.searchTextInputAndroid : {};
var textInput = (
<View style={styles.searchRow}>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
onChangeText={this._search.bind(this)}
placeholder="Search..."
style={[styles.searchTextInput, platformTextInputStyle]}
testID="explorer_search"
value={this.state.searchText}
/>
</View>);
}
var homePage;
if (Platform.OS === 'android' && this.props.isInDrawer) {
homePage = this._renderRow({
title: 'UIExplorer',
description: 'List of examples',
}, -1);
}
return (
<View style={styles.listContainer}>
{textInput}
{homePage}
<ListView
style={styles.list}
dataSource={this.state.dataSource}
renderRow={this._renderRow.bind(this)}
renderSectionHeader={this._renderSectionHeader}
keyboardShouldPersistTaps={true}
automaticallyAdjustContentInsets={false}
keyboardDismissMode="on-drag"
/>
</View>
);
}
_renderSectionHeader(data: any, section: string) {
return (
<View style={styles.sectionHeader}>
<Text style={styles.sectionHeaderTitle}>
{section.toUpperCase()}
</Text>
</View>
);
}
_renderRow(example: any, i: number) {
return (
<View key={i}>
<TouchableHighlight onPress={() => this._onPressRow(example)}>
<View style={styles.row}>
<Text style={styles.rowTitleText}>
{example.title}
</Text>
<Text style={styles.rowDetailText}>
{example.description}
</Text>
</View>
</TouchableHighlight>
<View style={styles.separator} />
</View>
);
}
_search(text: mixed) {
var regex = new RegExp(text, 'i');
var filter = (component) => regex.test(component.title);
this.setState({
dataSource: ds.cloneWithRowsAndSections({
components: COMPONENTS.filter(filter),
apis: APIS.filter(filter),
}),
searchText: text,
});
Settings.set({searchText: text});
}
_openExample(example: any) {
if (example.external) {
this.props.onExternalExampleRequested(example);
return;
}
var Component = makeRenderable(example);
if (Platform.OS === 'ios') {
this.props.navigator.push({
title: Component.title,
component: Component,
});
} else if (Platform.OS === 'android') {
this.props.onSelectExample({
title: Component.title,
component: Component,
});
}
}
_onPressRow(example: any) {
Settings.set({visibleExample: example.title});
this._openExample(example);
}
}
var styles = StyleSheet.create({
listContainer: {
flex: 1,
},
list: {
backgroundColor: '#eeeeee',
},
sectionHeader: {
padding: 5,
},
group: {
backgroundColor: 'white',
},
sectionHeaderTitle: {
fontWeight: '500',
fontSize: 11,
},
row: {
backgroundColor: 'white',
justifyContent: 'center',
paddingHorizontal: 15,
paddingVertical: 8,
},
separator: {
height: 1 / PixelRatio.get(),
backgroundColor: '#bbbbbb',
marginLeft: 15,
},
rowTitleText: {
fontSize: 17,
fontWeight: '500',
},
rowDetailText: {
fontSize: 15,
color: '#888888',
lineHeight: 20,
},
searchRow: {
backgroundColor: '#eeeeee',
paddingTop: 75,
paddingLeft: 10,
paddingRight: 10,
paddingBottom: 10,
},
searchTextInput: {
backgroundColor: 'white',
borderColor: '#cccccc',
borderRadius: 3,
borderWidth: 1,
paddingLeft: 8,
},
searchTextInputIOS: {
height: 30,
},
searchTextInputAndroid: {
padding: 2,
},
});
module.exports = UIExplorerList;
+195
View File
@@ -0,0 +1,195 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @flow
*/
'use strict';
var React = require('react-native');
var {
ListView,
PixelRatio,
StyleSheet,
Text,
TextInput,
TouchableHighlight,
View,
} = React;
var createExamplePage = require('./createExamplePage');
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (h1, h2) => h1 !== h2,
});
class UIExplorerListBase extends React.Component {
constructor(props: any) {
super(props);
this.state = {
dataSource: ds.cloneWithRowsAndSections({
components: [],
apis: [],
}),
searchText: this.props.searchText,
};
}
componentDidMount(): void {
this.search(this.state.searchText);
}
render() {
var topView = this.props.renderAdditionalView &&
this.props.renderAdditionalView(this.renderRow.bind(this), this.renderTextInput.bind(this));
return (
<View style={styles.listContainer}>
{topView}
<ListView
style={styles.list}
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
renderSectionHeader={this._renderSectionHeader}
keyboardShouldPersistTaps={true}
automaticallyAdjustContentInsets={false}
keyboardDismissMode="on-drag"
/>
</View>
);
}
renderTextInput(searchTextInputStyle: any) {
return (
<View style={styles.searchRow}>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
onChangeText={this.search.bind(this)}
placeholder="Search..."
style={[styles.searchTextInput, searchTextInputStyle]}
testID="explorer_search"
value={this.state.searchText}
/>
</View>
);
}
_renderSectionHeader(data: any, section: string) {
return (
<View style={styles.sectionHeader}>
<Text style={styles.sectionHeaderTitle}>
{section.toUpperCase()}
</Text>
</View>
);
}
renderRow(example: any, i: number) {
return (
<View key={i}>
<TouchableHighlight onPress={() => this.onPressRow(example)}>
<View style={styles.row}>
<Text style={styles.rowTitleText}>
{example.title}
</Text>
<Text style={styles.rowDetailText}>
{example.description}
</Text>
</View>
</TouchableHighlight>
<View style={styles.separator} />
</View>
);
}
search(text: mixed): void {
this.props.search && this.props.search(text);
var regex = new RegExp(text, 'i');
var filter = (component) => regex.test(component.title);
this.setState({
dataSource: ds.cloneWithRowsAndSections({
components: this.props.components.filter(filter),
apis: this.props.apis.filter(filter),
}),
searchText: text,
});
}
onPressRow(example: any): void {
this.props.onPressRow && this.props.onPressRow(example);
}
static makeRenderable(example: any): ReactClass<any, any, any> {
return example.examples ?
createExamplePage(null, example) :
example;
}
}
var styles = StyleSheet.create({
listContainer: {
flex: 1,
},
list: {
backgroundColor: '#eeeeee',
},
sectionHeader: {
padding: 5,
},
group: {
backgroundColor: 'white',
},
sectionHeaderTitle: {
fontWeight: '500',
fontSize: 11,
},
row: {
backgroundColor: 'white',
justifyContent: 'center',
paddingHorizontal: 15,
paddingVertical: 8,
},
separator: {
height: 1 / PixelRatio.get(),
backgroundColor: '#bbbbbb',
marginLeft: 15,
},
rowTitleText: {
fontSize: 17,
fontWeight: '500',
},
rowDetailText: {
fontSize: 15,
color: '#888888',
lineHeight: 20,
},
searchRow: {
backgroundColor: '#eeeeee',
paddingTop: 75,
paddingLeft: 10,
paddingRight: 10,
paddingBottom: 10,
},
searchTextInput: {
backgroundColor: 'white',
borderColor: '#cccccc',
borderRadius: 3,
borderWidth: 1,
paddingLeft: 8,
},
});
module.exports = UIExplorerListBase;
@@ -1,23 +1,34 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import "RCTBridge.h"
#import "RCTContextExecutor.h"
#import "RCTModuleMethod.h"
#import "RCTRootView.h"
#define RUN_RUNLOOP_WHILE(CONDITION, TIMEOUT) \
#define RUN_RUNLOOP_WHILE(CONDITION) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:TIMEOUT]; \
NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \
while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \
} \
_Pragma("clang diagnostic pop")
#define DEFAULT_TIMEOUT 2
@interface RCTBridge (RCTAllocationTests)
@property (nonatomic, weak) RCTBridge *batchedBridge;
@@ -52,6 +63,11 @@ RCT_EXPORT_MODULE();
_valid = NO;
}
RCT_EXPORT_METHOD(test:(__unused NSString *)a
:(__unused NSNumber *)b
:(__unused RCTResponseSenderBlock)c
:(__unused RCTResponseErrorBlock)d) {}
@end
@interface RCTAllocationTests : XCTestCase
@@ -71,7 +87,6 @@ RCT_EXPORT_MODULE();
(void)view;
}
sleep(DEFAULT_TIMEOUT);
XCTAssertNil(weakBridge, @"RCTBridge should have been deallocated");
}
@@ -88,12 +103,7 @@ RCT_EXPORT_MODULE();
(void)bridge;
}
/**
* Sleep on the main thread to allow js thread deallocations then run the runloop
* to allow the module to be deallocated on the main thread
*/
sleep(1);
RUN_RUNLOOP_WHILE(module.isValid, 1)
RUN_RUNLOOP_WHILE(module.isValid)
XCTAssertFalse(module.isValid, @"AllocationTestModule should have been invalidated by the bridge");
}
@@ -112,15 +122,23 @@ RCT_EXPORT_MODULE();
(void)bridge;
}
/**
* Sleep on the main thread to allow js thread deallocations then run the runloop
* to allow the module to be deallocated on the main thread
*/
sleep(1);
RUN_RUNLOOP_WHILE(weakModule, 1)
RUN_RUNLOOP_WHILE(weakModule)
XCTAssertNil(weakModule, @"AllocationTestModule should have been deallocated");
}
- (void)testModuleMethodsAreDeallocated
{
__weak RCTModuleMethod *weakMethod;
@autoreleasepool {
__autoreleasing RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:@"test:(NSString *)a :(nonnull NSNumber *)b :(RCTResponseSenderBlock)c :(RCTResponseErrorBlock)d" JSMethodName:@"" moduleClass:[AllocationTestModule class]];
weakMethod = method;
XCTAssertNotNil(method, @"RCTModuleMethod should have been created");
}
RUN_RUNLOOP_WHILE(weakMethod)
XCTAssertNil(weakMethod, @"RCTModuleMethod should have been deallocated");
}
- (void)testJavaScriptExecutorIsDeallocated
{
__weak id<RCTJavaScriptExecutor> weakExecutor;
@@ -133,8 +151,7 @@ RCT_EXPORT_MODULE();
(void)bridge;
}
RUN_RUNLOOP_WHILE(weakExecutor, 1);
sleep(1);
RUN_RUNLOOP_WHILE(weakExecutor);
XCTAssertNil(weakExecutor, @"JavaScriptExecutor should have been released");
}
@@ -146,13 +163,12 @@ RCT_EXPORT_MODULE();
moduleProvider:nil
launchOptions:nil];
id executor = [bridge.batchedBridge valueForKey:@"javaScriptExecutor"];
RUN_RUNLOOP_WHILE(!(weakContext = [executor valueForKey:@"context"]), DEFAULT_TIMEOUT);
RUN_RUNLOOP_WHILE(!(weakContext = [executor valueForKey:@"context"]));
XCTAssertNotNil(weakContext, @"RCTJavaScriptContext should have been created");
(void)bridge;
}
RUN_RUNLOOP_WHILE(weakContext, 1);
sleep(1);
RUN_RUNLOOP_WHILE(weakContext);
XCTAssertNil(weakContext, @"RCTJavaScriptContext should have been deallocated");
}
@@ -164,12 +180,11 @@ RCT_EXPORT_MODULE();
__weak id rootContentView;
@autoreleasepool {
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@""];
RUN_RUNLOOP_WHILE(!(rootContentView = [rootView valueForKey:@"contentView"]), DEFAULT_TIMEOUT)
RUN_RUNLOOP_WHILE(!(rootContentView = [rootView valueForKey:@"contentView"]))
XCTAssertTrue([rootContentView isValid], @"RCTContentView should be valid");
(void)rootView;
}
sleep(DEFAULT_TIMEOUT);
XCTAssertFalse([rootContentView isValid], @"RCTContentView should have been invalidated");
}
@@ -184,8 +199,7 @@ RCT_EXPORT_MODULE();
[bridge reload];
}
// Use RUN_RUNLOOP_WHILE because `batchedBridge` deallocates on the main thread.
RUN_RUNLOOP_WHILE(batchedBridge != nil, DEFAULT_TIMEOUT)
RUN_RUNLOOP_WHILE(batchedBridge != nil)
XCTAssertNotNil(bridge, @"RCTBridge should not have been deallocated");
XCTAssertNil(batchedBridge, @"RCTBatchedBridge should have been deallocated");
@@ -1,4 +1,16 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
@@ -12,7 +24,7 @@
@property (nonatomic, strong, readonly) RCTBridge *batchedBridge;
- (void)_handleBuffer:(id)buffer context:(NSNumber *)context;
- (void)_handleBuffer:(id)buffer;
- (void)setUp;
@end
@@ -45,7 +57,6 @@ RCT_EXPORT_MODULE()
- (void)executeJSCall:(__unused NSString *)name
method:(__unused NSString *)method
arguments:(__unused NSArray *)arguments
context:(__unused NSNumber *)executorID
callback:(RCTJavaScriptCallback)onComplete
{
onComplete(nil, nil);
@@ -109,10 +120,22 @@ RCT_EXPORT_MODULE(TestModule)
[_bridge invalidate];
}
#define RUN_RUNLOOP_WHILE(CONDITION) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \
while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \
} \
_Pragma("clang diagnostic pop")
- (void)testHookRegistration
{
TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"];
NSString *injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"];
NSString *injectedStuff;
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
@@ -131,7 +154,10 @@ RCT_EXPORT_MODULE(TestModule)
- (void)testCallNativeMethod
{
TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"];
NSString *injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"];
NSString *injectedStuff;
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
@@ -143,7 +169,7 @@ RCT_EXPORT_MODULE(TestModule)
NSArray *args = @[@1234, @5678, @"stringy", @{@"a": @1}, @42];
NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args], @[], @1234567];
[_bridge.batchedBridge _handleBuffer:buffer context:RCTGetExecutorID(executor)];
[_bridge.batchedBridge _handleBuffer:buffer];
dispatch_sync(_methodQueue, ^{
// clear the queue
@@ -159,7 +185,7 @@ RCT_EXPORT_MODULE(TestModule)
}
RCT_EXPORT_METHOD(testMethod:(NSInteger)integer
number:(NSNumber *)number
number:(nonnull NSNumber *)number
string:(NSString *)string
dictionary:(NSDictionary *)dict
callback:(RCTResponseSenderBlock)callback)
@@ -0,0 +1,168 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
#import "RCTCache.h"
// Silence silly sign warnings when using int literals
#pragma clang diagnostic ignored "-Wsign-compare"
@interface RCTCache (Private)
- (void)cleanUpAllObjects;
- (void)resequence;
- (NSDictionary *)cache;
- (void)setSequenceNumber:(NSInteger)number;
@end
@interface RCTCacheTests : XCTestCase
@property (nonatomic, strong) RCTCache *cache;
@end
@implementation RCTCacheTests
- (void)setUp
{
self.cache = [[RCTCache alloc] init];
self.cache.countLimit = 3;
self.cache.totalCostLimit = 100;
}
- (void)tearDown
{
self.cache = nil;
}
- (void)testInsertion
{
[self.cache setObject:@1 forKey:@"foo" cost:1];
[self.cache setObject:@2 forKey:@"bar" cost:2];
[self.cache setObject:@3 forKey:@"baz" cost:3];
XCTAssertEqual([self.cache count], 3);
XCTAssertEqual([self.cache totalCost], 6);
}
- (void)testRemoval
{
[self.cache setObject:@1 forKey:@"foo" cost:1];
[self.cache setObject:@2 forKey:@"bar" cost:2];
[self.cache setObject:@3 forKey:@"baz" cost:3];
[self.cache removeObjectForKey:@"bar"];
XCTAssertEqual([self.cache count], 2);
XCTAssertNil([self.cache objectForKey:@"bar"]);
}
- (void)testCountEviction
{
[self.cache setObject:@1 forKey:@"foo"];
[self.cache setObject:@2 forKey:@"bar"];
[self.cache setObject:@3 forKey:@"baz"];
[self.cache setObject:@4 forKey:@"bam"];
XCTAssertEqual([self.cache count], 3);
XCTAssertNil([self.cache objectForKey:@"foo"]);
[self.cache setObject:@5 forKey:@"boo"];
XCTAssertEqual([self.cache count], 3);
XCTAssertNil([self.cache objectForKey:@"bar"]);
}
- (void)testCostEviction
{
[self.cache setObject:@1 forKey:@"foo" cost:99];
[self.cache setObject:@2 forKey:@"bar" cost:2];
XCTAssertEqual([self.cache count], 1);
XCTAssertEqual([self.cache totalCost], 2);
XCTAssertNil([self.cache objectForKey:@"foo"]);
[self.cache setObject:@3 forKey:@"baz" cost:999];
XCTAssertEqual([self.cache count], 0);
XCTAssertEqual([self.cache totalCost], 0);
}
- (void)testCleanup
{
[self.cache setObject:@1 forKey:@"foo"];
[self.cache setObject:@2 forKey:@"bar"];
[self.cache setObject:@3 forKey:@"baz"];
//simulate memory warning
[self.cache cleanUpAllObjects];
XCTAssertEqual([self.cache count], 0);
XCTAssertEqual([self.cache totalCost], 0);
}
- (void)testResequence
{
[self.cache setObject:@1 forKey:@"foo"];
[self.cache setObject:@2 forKey:@"bar"];
[self.cache setObject:@3 forKey:@"baz"];
[self.cache resequence];
NSDictionary *innerCache = [self.cache cache];
XCTAssertEqualObjects([innerCache[@"foo"] valueForKey:@"sequenceNumber"], @0);
XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @1);
XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @2);
[self.cache removeObjectForKey:@"foo"];
[self.cache resequence];
XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @0);
XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @1);
}
- (void)testResequenceTrigger
{
[self.cache setObject:@1 forKey:@"foo"];
[self.cache setObject:@2 forKey:@"bar"];
//first object should now be bar with sequence number of 1
[self.cache removeObjectForKey:@"foo"];
//should trigger resequence
[self.cache setSequenceNumber:NSIntegerMax];
[self.cache setObject:@3 forKey:@"baz"];
NSDictionary *innerCache = [self.cache cache];
XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @0);
XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @1);
//first object should now be baz with sequence number of 1
[self.cache removeObjectForKey:@"bar"];
//should also trigger resequence
[self.cache setSequenceNumber:NSIntegerMax];
[self.cache objectForKey:@"baz"];
XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @0);
}
- (void)testName
{
self.cache.name = @"Hello";
XCTAssertEqualObjects(self.cache.name, @"Hello");
}
@end
@@ -16,10 +16,7 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIView.h>
#import <XCTest/XCTest.h>
extern CGRect RCTClipRect(CGSize contentSize, CGFloat contentScale,
CGSize targetSize, CGFloat targetScale,
UIViewContentMode resizeMode);
#import "RCTImageUtils.h"
#define RCTAssertEqualPoints(a, b) { \
XCTAssertEqual(a.x, b.x); \
@@ -36,11 +33,11 @@ RCTAssertEqualPoints(a.origin, b.origin); \
RCTAssertEqualSizes(a.size, b.size); \
}
@interface ClippingTests : XCTestCase
@interface RCTClipRectTests : XCTestCase
@end
@implementation ClippingTests
@implementation RCTClipRectTests
- (void)testLandscapeSourceLandscapeTarget
{
@@ -109,6 +106,18 @@ RCTAssertEqualSizes(a.size, b.size); \
{
CGRect expected = {{0, -37.5}, {10, 100}};
CGRect result = RCTClipRect(content, 2, target, 2, UIViewContentModeScaleAspectFill);
RCTAssertEqualRects(expected, result);
}
}
- (void)testRounding
{
CGSize content = {10, 100};
CGSize target = {20, 50};
{
CGRect expected = {{0, -38}, {10, 100}};
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFill);
RCTAssertEqualRects(expected, result);
}
@@ -1,4 +1,16 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <mach/mach_time.h>
@@ -7,6 +19,7 @@
#import "RCTContextExecutor.h"
#import "RCTUtils.h"
#define RUN_PERF_TESTS 0
@interface RCTContextExecutorTests : XCTestCase
@@ -21,7 +34,6 @@
{
[super setUp];
_executor = [[RCTContextExecutor alloc] init];
RCTSetExecutorID(_executor);
[_executor setUp];
}
@@ -37,6 +49,8 @@
[_executor invalidate];
}
#if RUN_PERF_TESTS
static uint64_t _get_time_nanoseconds(void)
{
static struct mach_timebase_info tb_info = {0, 0};
@@ -80,7 +94,7 @@ static uint64_t _get_time_nanoseconds(void)
JSContextGroupRelease(group);
}
- (void)MANUALLY_testJavaScriptCallSpeed
- (void)testJavaScriptCallSpeed
{
/**
* Since we almost don't change the RCTContextExecutor logic, and this test is
@@ -127,7 +141,6 @@ static uint64_t _get_time_nanoseconds(void)
[_executor executeJSCall:@"module"
method:@"method"
arguments:params
context:RCTGetExecutorID(_executor)
callback:^(id json, __unused NSError *unused) {
XCTAssert([json isEqual:@YES], @"Invalid return");
}];
@@ -190,4 +203,6 @@ static uint64_t _get_time_nanoseconds(void)
}
}
#endif
@end
@@ -1,8 +1,21 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
#import "RCTConvert.h"
#import "RCTUtils.h"
@interface RCTConvert_NSURLTests : XCTestCase
@@ -13,8 +26,7 @@
#define TEST_URL(name, _input, _expectedURL) \
- (void)test_##name { \
NSURL *result = [RCTConvert NSURL:_input]; \
NSURL *expected = (_expectedURL) ? [NSURL URLWithString:_expectedURL ?: @""] : nil; \
XCTAssertEqualObjects(result.absoluteURL, expected); \
XCTAssertEqualObjects(result.absoluteString, _expectedURL); \
} \
#define TEST_PATH(name, _input, _expectedPath) \
@@ -30,7 +42,7 @@ TEST_PATH(name, _input, [[[NSBundle mainBundle] bundlePath] stringByAppendingPat
TEST_URL(basic, @"http://example.com", @"http://example.com")
TEST_URL(null, (id)kCFNull, nil)
// Local files
// Resource files
TEST_PATH(fileURL, @"file:///blah/hello.jsbundle", @"/blah/hello.jsbundle")
TEST_BUNDLE_PATH(filePath, @"blah/hello.jsbundle", @"blah/hello.jsbundle")
TEST_BUNDLE_PATH(filePathWithSpaces, @"blah blah/hello.jsbundle", @"blah blah/hello.jsbundle")
@@ -38,6 +50,11 @@ TEST_BUNDLE_PATH(filePathWithEncodedSpaces, @"blah%20blah/hello.jsbundle", @"bla
TEST_BUNDLE_PATH(imageAt2XPath, @"images/foo@2x.jpg", @"images/foo@2x.jpg")
TEST_BUNDLE_PATH(imageFile, @"foo.jpg", @"foo.jpg")
// User documents
TEST_PATH(documentsFolder, @"~/Documents",
[NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES) firstObject])
// Remote files
TEST_URL(fullURL, @"http://example.com/blah/hello.jsbundle", @"http://example.com/blah/hello.jsbundle")
TEST_URL(urlWithSpaces, @"http://example.com/blah blah/foo", @"http://example.com/blah%20blah/foo")
@@ -45,4 +62,19 @@ TEST_URL(urlWithEncodedSpaces, @"http://example.com/blah%20blah/foo", @"http://e
TEST_URL(imageURL, @"http://example.com/foo@2x.jpg", @"http://example.com/foo@2x.jpg")
TEST_URL(imageURLWithSpaces, @"http://example.com/blah foo@2x.jpg", @"http://example.com/blah%20foo@2x.jpg")
// Unicode
TEST_URL(unicodeURL,
@"https://ru.wikipedia.org/wiki/\u0417\u0430\u0433\u043B\u0430\u0432"
"\u043D\u0430\u044F_\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430",
@"https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2"
"%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0")
// Data URLs
- (void)testDataURL
{
NSURL *expectedURL = RCTDataURL(@"text/plain", [@"abcde" dataUsingEncoding:NSUTF8StringEncoding]);
NSURL *testURL = [NSURL URLWithString:@"data:text/plain;base64,YWJjZGU="];
XCTAssertEqualObjects([testURL absoluteString], [expectedURL absoluteString]);
}
@end
@@ -1,4 +1,16 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
@@ -174,7 +186,7 @@
RCTAssertEqualFonts(expected, result);
}
{
UIFont *expected = [UIFont boldSystemFontOfSize:14];
UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"foobar", @"fontWeight": @"bold"}];
RCTAssertEqualFonts(expected, result);
}
@@ -1,12 +1,15 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <UIKit/UIKit.h>
@@ -61,7 +64,7 @@
_eventDispatcher = [[RCTEventDispatcher alloc] init];
((id<RCTBridgeModule>)_eventDispatcher).bridge = _bridge;
_eventName = @"sampleEvent";
_eventName = RCTNormalizeInputEventName(@"sampleEvent");
_body = @{ @"foo": @"bar" };
_testEvent = [[RCTTestEvent alloc] initWithViewTag:nil
eventName:_eventName
@@ -122,7 +125,7 @@
- (void)testDifferentEventTypesDontCoalesce
{
NSString *firstEventName = @"firstEvent";
NSString *firstEventName = RCTNormalizeInputEventName(@"firstEvent");
RCTTestEvent *firstEvent = [[RCTTestEvent alloc] initWithViewTag:nil
eventName:firstEventName
body:_body];
@@ -0,0 +1,81 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
#import "RCTUtils.h"
#import "RCTNetworking.h"
extern BOOL RCTIsGzippedData(NSData *data);
@interface RCTNetworking (Private)
- (void)buildRequest:(NSDictionary *)query
completionBlock:(void (^)(NSURLRequest *request))block;
@end
@interface RCTGzipTests : XCTestCase
@end
@implementation RCTGzipTests
- (void)testGzip
{
//set up data
NSString *inputString = @"Hello World!";
NSData *inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
//compress
NSData *outputData = RCTGzipData(inputData, -1);
XCTAssertTrue(RCTIsGzippedData(outputData));
}
- (void)testDontRezipZippedData
{
//set up data
NSString *inputString = @"Hello World!";
NSData *inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
//compress
NSData *compressedData = RCTGzipData(inputData, -1);
inputString = [[NSString alloc] initWithData:compressedData encoding:NSUTF8StringEncoding];
//compress again
NSData *outputData = RCTGzipData(inputData, -1);
NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];
XCTAssertEqualObjects(outputString, inputString);
}
- (void)testRequestBodyEncoding
{
NSDictionary *query = @{
@"url": @"http://example.com",
@"method": @"POST",
@"data": @{@"string": @"Hello World"},
@"headers": @{@"Content-Encoding": @"gzip"},
};
RCTNetworking *networker = [[RCTNetworking alloc] init];
__block NSURLRequest *request = nil;
[networker buildRequest:query completionBlock:^(NSURLRequest *_request) {
request = _request;
}];
XCTAssertNotNil(request);
XCTAssertNotNil(request.HTTPBody);
XCTAssertTrue(RCTIsGzippedData(request.HTTPBody));
}
@end
@@ -0,0 +1,81 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
#import "RCTUtils.h"
@interface RCTJSONTests : XCTestCase
@end
@implementation RCTJSONTests
- (void)testEncodingObject
{
NSDictionary *obj = @{@"foo": @"bar"};
NSString *json = @"{\"foo\":\"bar\"}";
XCTAssertEqualObjects(json, RCTJSONStringify(obj, NULL));
}
- (void)testEncodingArray
{
NSArray *array = @[@"foo", @"bar"];
NSString *json = @"[\"foo\",\"bar\"]";
XCTAssertEqualObjects(json, RCTJSONStringify(array, NULL));
}
- (void)testEncodingString
{
NSString *text = @"Hello\nWorld";
NSString *json = @"\"Hello\\nWorld\"";
XCTAssertEqualObjects(json, RCTJSONStringify(text, NULL));
}
- (void)testDecodingObject
{
NSDictionary *obj = @{@"foo": @"bar"};
NSString *json = @"{\"foo\":\"bar\"}";
XCTAssertEqualObjects(obj, RCTJSONParse(json, NULL));
}
- (void)testDecodingArray
{
NSArray *array = @[@"foo", @"bar"];
NSString *json = @"[\"foo\",\"bar\"]";
XCTAssertEqualObjects(array, RCTJSONParse(json, NULL));
}
- (void)testDecodingString
{
NSString *text = @"Hello\nWorld";
NSString *json = @"\"Hello\\nWorld\"";
XCTAssertEqualObjects(text, RCTJSONParse(json, NULL));
}
- (void)testDecodingMutableArray
{
NSString *json = @"[1,2,3]";
NSMutableArray *array = RCTJSONParseMutable(json, NULL);
XCTAssertNoThrow([array addObject:@4]);
XCTAssertEqualObjects(array, (@[@1, @2, @3, @4]));
}
- (void)testLeadingWhitespace
{
NSDictionary *obj = @{@"foo": @"bar"};
NSString *json = @" \r\n\t{\"foo\":\"bar\"}";
XCTAssertEqualObjects(obj, RCTJSONParse(json, NULL));
}
@end
@@ -0,0 +1,144 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "RCTModuleMethod.h"
@interface RCTMethodArgumentTests : XCTestCase
@end
@implementation RCTMethodArgumentTests
extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes);
- (void)testOneArgument
{
NSArray *arguments;
NSString *methodName = @"foo:(NSInteger)foo";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:");
XCTAssertEqual(arguments.count, (NSUInteger)1);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
}
- (void)testTwoArguments
{
NSArray *arguments;
NSString *methodName = @"foo:(NSInteger)foo bar:(BOOL)bar";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:bar:");
XCTAssertEqual(arguments.count, (NSUInteger)2);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
}
- (void)testSpaces
{
NSArray *arguments;
NSString *methodName = @"foo : (NSInteger)foo bar : (BOOL) bar";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:bar:");
XCTAssertEqual(arguments.count, (NSUInteger)2);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
}
- (void)testNewlines
{
NSArray *arguments;
NSString *methodName = @"foo : (NSInteger)foo\nbar : (BOOL) bar";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:bar:");
XCTAssertEqual(arguments.count, (NSUInteger)2);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
}
- (void)testUnnamedArgs
{
NSArray *arguments;
NSString *methodName = @"foo:(NSInteger)foo:(BOOL)bar";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo::");
XCTAssertEqual(arguments.count, (NSUInteger)2);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
}
- (void)testUntypedUnnamedArgs
{
NSArray *arguments;
NSString *methodName = @"foo:foo:bar:bar";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:::");
XCTAssertEqual(arguments.count, (NSUInteger)3);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"id");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"id");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[2]).type, @"id");
}
- (void)testAttributes
{
NSArray *arguments;
NSString *methodName = @"foo:(__attribute__((nonnull)) NSString *)foo bar:(__unused BOOL)bar";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:bar:");
XCTAssertEqual(arguments.count, (NSUInteger)2);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
}
- (void)testNullability
{
NSArray *arguments;
NSString *methodName = @"foo:(nullable NSString *)foo bar:(nonnull NSNumber *)bar baz:(id)baz";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:bar:baz:");
XCTAssertEqual(arguments.count, (NSUInteger)3);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"NSNumber");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[2]).type, @"id");
XCTAssertEqual(((RCTMethodArgument *)arguments[0]).nullability, RCTNullable);
XCTAssertEqual(((RCTMethodArgument *)arguments[1]).nullability, RCTNonnullable);
XCTAssertEqual(((RCTMethodArgument *)arguments[2]).nullability, RCTNullabilityUnspecified);
}
- (void)testSemicolonStripping
{
NSArray *arguments;
NSString *methodName = @"foo:(NSString *)foo bar:(BOOL)bar;";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:bar:");
XCTAssertEqual(arguments.count, (NSUInteger)2);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
}
- (void)testUnused
{
NSArray *arguments;
NSString *methodName = @"foo:(__unused NSString *)foo bar:(NSNumber *)bar";
RCTParseObjCMethodName(&methodName, &arguments);
XCTAssertEqualObjects(methodName, @"foo:bar:");
XCTAssertEqual(arguments.count, (NSUInteger)2);
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"NSNumber");
XCTAssertTrue(((RCTMethodArgument *)arguments[0]).unused);
XCTAssertFalse(((RCTMethodArgument *)arguments[1]).unused);
}
@end
@@ -0,0 +1,103 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "RCTModuleMethod.h"
#import "RCTLog.h"
static BOOL RCTLogsError(void (^block)(void))
{
__block BOOL loggedError = NO;
RCTPerformBlockWithLogFunction(block, ^(RCTLogLevel level,
__unused NSString *fileName,
__unused NSNumber *lineNumber,
__unused NSString *message) {
loggedError = (level == RCTLogLevelError);
});
return loggedError;
}
@interface RCTModuleMethodTests : XCTestCase
@end
@implementation RCTModuleMethodTests
- (void)doFooWithBar:(__unused NSString *)bar { }
- (void)testNonnull
{
NSString *methodName = @"doFooWithBar:(nonnull NSString *)bar";
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
JSMethodName:nil
moduleClass:[self class]];
XCTAssertFalse(RCTLogsError(^{
[method invokeWithBridge:nil module:self arguments:@[@"Hello World"]];
}));
XCTAssertTrue(RCTLogsError(^{
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
}));
}
- (void)doFooWithNumber:(__unused NSNumber *)n { }
- (void)doFooWithDouble:(__unused double)n { }
- (void)doFooWithInteger:(__unused NSInteger)n { }
- (void)testNumbersNonnull
{
{
// Specifying an NSNumber param without nonnull isn't allowed
XCTAssertTrue(RCTLogsError(^{
NSString *methodName = @"doFooWithNumber:(NSNumber *)n";
(void)[[RCTModuleMethod alloc] initWithObjCMethodName:methodName
JSMethodName:nil
moduleClass:[self class]];
}));
}
{
NSString *methodName = @"doFooWithNumber:(nonnull NSNumber *)n";
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
JSMethodName:nil
moduleClass:[self class]];
XCTAssertTrue(RCTLogsError(^{
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
}));
}
{
NSString *methodName = @"doFooWithDouble:(double)n";
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
JSMethodName:nil
moduleClass:[self class]];
XCTAssertTrue(RCTLogsError(^{
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
}));
}
{
NSString *methodName = @"doFooWithInteger:(NSInteger)n";
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
JSMethodName:nil
moduleClass:[self class]];
XCTAssertTrue(RCTLogsError(^{
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
}));
}
}
@end
@@ -1,4 +1,16 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
@@ -1,4 +1,16 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
@@ -13,10 +25,10 @@
- (void)testDictionary
{
NSObject<RCTViewNodeProtocol> *myView = [[UIView alloc] init];
id<RCTComponent> myView = [[UIView alloc] init];
myView.reactTag = @4;
NSObject<RCTViewNodeProtocol> *myOtherView = [[UIView alloc] init];
id<RCTComponent> myOtherView = [[UIView alloc] init];
myOtherView.reactTag = @5;
RCTSparseArray *registry = [[RCTSparseArray alloc] init];
@@ -1,9 +1,19 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <XCTest/XCTest.h>
#import "RCTRootView.h"
#import "RCTShadowView.h"
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
#import "UIView+React.h"
@@ -11,36 +21,14 @@
@interface RCTUIManager (Testing)
- (void)_manageChildren:(NSNumber *)containerReactTag
moveFromIndices:(NSArray *)moveFromIndices
moveToIndices:(NSArray *)moveToIndices
addChildReactTags:(NSArray *)addChildReactTags
addAtIndices:(NSArray *)addAtIndices
removeAtIndices:(NSArray *)removeAtIndices
registry:(RCTSparseArray *)registry;
- (void)modifyManageChildren:(NSNumber *)containerReactTag
addChildReactTags:(NSMutableArray *)mutableAddChildReactTags
addAtIndices:(NSMutableArray *)mutableAddAtIndices
removeAtIndices:(NSMutableArray *)mutableRemoveAtIndices;
- (void)createView:(NSNumber *)reactTag
viewName:(NSString *)viewName
rootTag:(NSNumber *)rootTag
props:(NSDictionary *)props;
- (void)updateView:(NSNumber *)reactTag
viewName:(NSString *)viewName
props:(NSDictionary *)props;
- (void)manageChildren:(NSNumber *)containerReactTag
moveFromIndices:(NSArray *)moveFromIndices
moveToIndices:(NSArray *)moveToIndices
addChildReactTags:(NSArray *)addChildReactTags
addAtIndices:(NSArray *)addAtIndices
removeAtIndices:(NSArray *)removeAtIndices;
- (void)flushUIBlocks;
@property (nonatomic, readonly) RCTSparseArray *viewRegistry;
@property (nonatomic, readonly) RCTSparseArray *shadowViewRegistry; // RCT thread only
@end
@@ -63,11 +51,6 @@
UIView *registeredView = [[UIView alloc] init];
[registeredView setReactTag:@(i)];
_uiManager.viewRegistry[i] = registeredView;
RCTShadowView *registeredShadowView = [[RCTShadowView alloc] init];
registeredShadowView.viewName = @"RCTView";
[registeredShadowView setReactTag:@(i)];
_uiManager.shadowViewRegistry[i] = registeredShadowView;
}
}
@@ -84,6 +67,8 @@
// Add views 1-5 to view 20
[_uiManager _manageChildren:@20
moveFromIndices:nil
moveToIndices:nil
addChildReactTags:tagsToAdd
addAtIndices:addAtIndices
removeAtIndices:nil
@@ -116,6 +101,8 @@
// Remove views 1-5 from view 20
[_uiManager _manageChildren:@20
moveFromIndices:nil
moveToIndices:nil
addChildReactTags:nil
addAtIndices:nil
removeAtIndices:removeAtIndices
@@ -153,9 +140,11 @@
{
UIView *containerView = _uiManager.viewRegistry[20];
NSArray *removeAtIndices = @[@2, @3, @5, @8, @4, @9];
NSArray *addAtIndices = @[@0, @6, @1, @7];
NSArray *tagsToAdd = @[@11, @12, @5, @10];
NSArray *removeAtIndices = @[@2, @3, @5, @8];
NSArray *addAtIndices = @[@0, @6];
NSArray *tagsToAdd = @[@11, @12];
NSArray *moveFromIndices = @[@4, @9];
NSArray *moveToIndices = @[@1, @7];
// We need to keep these in array to keep them around
NSMutableArray *viewsToRemove = [NSMutableArray array];
@@ -171,6 +160,8 @@
}
[_uiManager _manageChildren:@20
moveFromIndices:moveFromIndices
moveToIndices:moveToIndices
addChildReactTags:tagsToAdd
addAtIndices:addAtIndices
removeAtIndices:removeAtIndices
@@ -197,329 +188,4 @@
}
}
/* +-----------------------------------------------------------+ +----------------------+
* | Shadow Hierarchy | | Legend |
* +-----------------------------------------------------------+ +----------------------+
* | | | |
* | +---+ ****** | | ******************** |
* | | 1 | * 11 * | | * Layout-only View * |
* | +---+ ****** | | ******************** |
* | | | | | |
* | +-------+---+---+----------+ +---+---+ | | +----+ |
* | | | | | | | | | |View| Subview |
* | v v v v v v | | +----+ -----------> |
* | ***** +---+ ***** +---+ +----+ +----+ | | |
* | * 2 * | 3 | * 4 * | 5 | | 12 | | 13 | | +----------------------+
* | ***** +---+ ***** +---+ +----+ +----+ |
* | | | | |
* | +---+--+ | +---+---+ |
* | | | | | | |
* | v v v v v |
* | +---+ +---+ +---+ +---+ ****** |
* | | 6 | | 7 | | 8 | | 9 | * 10 * |
* | +---+ +---+ +---+ +---+ ****** |
* | |
* +-----------------------------------------------------------+
*
* +-----------------------------------------------------------+
* | View Hierarchy |
* +-----------------------------------------------------------+
* | |
* | +---+ ****** |
* | | 1 | * 11 * |
* | +---+ ****** |
* | | | |
* | +------+------+------+------+ +---+---+ |
* | | | | | | | | |
* | v v v v v v v |
* | +---+ +---+ +---+ +---+ +---+ +----+ +----+ |
* | | 6 | | 7 | | 3 | | 8 | | 5 | | 12 | | 13 | |
* | +---+ +---+ +---+ +---+ +---+ +----+ +----+ |
* | | |
* | v |
* | +---+ |
* | | 9 | |
* | +---+ |
* | |
* +-----------------------------------------------------------+
*/
- (void)updateShadowViewWithReactTag:(NSNumber *)reactTag layoutOnly:(BOOL)isLayoutOnly childTags:(NSArray *)childTags
{
RCTShadowView *shadowView = _uiManager.shadowViewRegistry[reactTag];
shadowView.allProps = isLayoutOnly ? @{} : @{@"collapsible": @NO};
[childTags enumerateObjectsUsingBlock:^(NSNumber *childTag, NSUInteger idx, __unused BOOL *stop) {
[shadowView insertReactSubview:_uiManager.shadowViewRegistry[childTag] atIndex:idx];
}];
}
- (void)setUpShadowViewHierarchy
{
[self updateShadowViewWithReactTag:@1 layoutOnly:NO childTags:@[@2, @3, @4, @5]];
[self updateShadowViewWithReactTag:@2 layoutOnly:YES childTags:@[@6, @7]];
[self updateShadowViewWithReactTag:@3 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@4 layoutOnly:YES childTags:@[@8]];
[self updateShadowViewWithReactTag:@5 layoutOnly:NO childTags:@[@9, @10]];
[self updateShadowViewWithReactTag:@6 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@7 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@8 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@9 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@10 layoutOnly:YES childTags:nil];
[self updateShadowViewWithReactTag:@11 layoutOnly:YES childTags:@[@12, @13]];
[self updateShadowViewWithReactTag:@12 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@13 layoutOnly:NO childTags:nil];
}
- (void)testModifyIndices1
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@2] mutableCopy];
NSMutableArray *addIndices = [@[@3] mutableCopy];
NSMutableArray *removeIndices = [@[@0] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@6, @7]));
XCTAssertEqualObjects(addIndices, (@[@3, @4]));
XCTAssertEqualObjects(removeIndices, (@[@0, @1]));
}
- (void)testModifyIndices2
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@11] mutableCopy];
NSMutableArray *addIndices = [@[@4] mutableCopy];
NSMutableArray *removeIndices = [@[] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@12, @13]));
XCTAssertEqualObjects(addIndices, (@[@5, @6]));
XCTAssertEqualObjects(removeIndices, (@[]));
}
- (void)testModifyIndices3
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[] mutableCopy];
NSMutableArray *addIndices = [@[] mutableCopy];
NSMutableArray *removeIndices = [@[@2] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[]));
XCTAssertEqualObjects(addIndices, (@[]));
XCTAssertEqualObjects(removeIndices, (@[@3]));
}
- (void)testModifyIndices4
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@11] mutableCopy];
NSMutableArray *addIndices = [@[@3] mutableCopy];
NSMutableArray *removeIndices = [@[@2] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@12, @13]));
XCTAssertEqualObjects(addIndices, (@[@4, @5]));
XCTAssertEqualObjects(removeIndices, (@[@3]));
}
- (void)testModifyIndices5
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[] mutableCopy];
NSMutableArray *addIndices = [@[] mutableCopy];
NSMutableArray *removeIndices = [@[@0, @1, @2, @3] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[]));
XCTAssertEqualObjects(addIndices, (@[]));
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @2, @3, @4]));
}
- (void)testModifyIndices6
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@11] mutableCopy];
NSMutableArray *addIndices = [@[@0] mutableCopy];
NSMutableArray *removeIndices = [@[@0, @1, @2, @3] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@12, @13]));
XCTAssertEqualObjects(addIndices, (@[@0, @1]));
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @2, @3, @4]));
}
- (void)testModifyIndices7
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@11] mutableCopy];
NSMutableArray *addIndices = [@[@1] mutableCopy];
NSMutableArray *removeIndices = [@[@0, @2, @3] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@12, @13]));
XCTAssertEqualObjects(addIndices, (@[@1, @2]));
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @3, @4]));
}
- (void)DISABLED_testScenario1 // t7660646
{
RCTUIManager *uiManager = [[RCTUIManager alloc] init];
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
NS_VALID_UNTIL_END_OF_SCOPE RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"Test"];
XCTestExpectation *expectation = [self expectationWithDescription:@""];
dispatch_queue_t shadowQueue = [uiManager valueForKey:@"shadowQueue"];
dispatch_async(shadowQueue, ^{
// Make sure root view finishes loading.
dispatch_sync(dispatch_get_main_queue(), ^{});
/* */[uiManager createView:@2 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
/* */[uiManager createView:@3 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
/* V */[uiManager createView:@4 viewName:@"RCTView" rootTag:@1 props:@{@"alignItems":@"center",@"backgroundColor":@"#F5FCFF",@"flex":@1,@"justifyContent":@"center"}];
/* V */[uiManager createView:@5 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"blue",@"height":@50,@"width":@50}];
/* */[uiManager createView:@6 viewName:@"RCTView" rootTag:@1 props:@{@"width":@250}];
/* V */[uiManager createView:@7 viewName:@"RCTView" rootTag:@1 props:@{@"borderWidth":@10,@"margin":@50}];
/* V */[uiManager createView:@8 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"yellow",@"height":@50}];
/* V */[uiManager createView:@9 viewName:@"RCTText" rootTag:@1 props:@{@"accessible":@1,@"fontSize":@20,@"isHighlighted":@0,@"margin":@10,@"textAlign":@"center"}];
/* */[uiManager createView:@10 viewName:@"RCTRawText" rootTag:@1 props:@{@"text":@"This tests removal of layout-only views."}];
/* */[uiManager manageChildren:@9 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@10] addAtIndices:@[@0] removeAtIndices:nil];
/* V */[uiManager createView:@12 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"green",@"height":@50}];
/* */[uiManager manageChildren:@7 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@8,@9,@12] addAtIndices:@[@0,@1,@2] removeAtIndices:nil];
/* */[uiManager manageChildren:@6 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@7] addAtIndices:@[@0] removeAtIndices:nil];
/* V */[uiManager createView:@13 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"red",@"height":@50,@"width":@50}];
/* */[uiManager manageChildren:@4 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@5,@6,@13] addAtIndices:@[@0,@1,@2] removeAtIndices:nil];
/* */[uiManager manageChildren:@3 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@4] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@2 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@3] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@1 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@2] addAtIndices:@[@0] removeAtIndices:nil];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)8);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
expectation = [self expectationWithDescription:@""];
dispatch_async(shadowQueue, ^{
[uiManager updateView:@7 viewName:@"RCTView" props:@{@"borderWidth":[NSNull null]}];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)7);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
expectation = [self expectationWithDescription:@""];
dispatch_async(shadowQueue, ^{
[uiManager updateView:@7 viewName:@"RCTView" props:@{@"borderWidth":@10}];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)8);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
}
- (void)DISABLED_testScenario2 // t7660646
{
RCTUIManager *uiManager = [[RCTUIManager alloc] init];
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
NS_VALID_UNTIL_END_OF_SCOPE RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"Test"];
XCTestExpectation *expectation = [self expectationWithDescription:@""];
dispatch_queue_t shadowQueue = [uiManager valueForKey:@"shadowQueue"];
dispatch_async(shadowQueue, ^{
// Make sure root view finishes loading.
dispatch_sync(dispatch_get_main_queue(), ^{});
/* */[uiManager createView:@2 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
/* */[uiManager createView:@3 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
/* V */[uiManager createView:@4 viewName:@"RCTView" rootTag:@1 props:@{@"alignItems":@"center",@"backgroundColor":@"#F5FCFF",@"flex":@1,@"justifyContent":@"center"}];
/* */[uiManager createView:@5 viewName:@"RCTView" rootTag:@1 props:@{@"width":@250}];
/* V */[uiManager createView:@6 viewName:@"RCTView" rootTag:@1 props:@{@"borderWidth":@1}];
/* V */[uiManager createView:@7 viewName:@"RCTText" rootTag:@1 props:@{@"accessible":@1,@"fontSize":@20,@"isHighlighted":@0,@"margin":@10,@"textAlign":@"center"}];
/* */[uiManager createView:@8 viewName:@"RCTRawText" rootTag:@1 props:@{@"text":@"This tests removal of layout-only views."}];
/* */[uiManager manageChildren:@7 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@8] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@6 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@7] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@5 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@6] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@4 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@5] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@3 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@4] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@2 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@3] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@1 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@2] addAtIndices:@[@0] removeAtIndices:nil];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)4);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
expectation = [self expectationWithDescription:@""];
dispatch_async(shadowQueue, ^{
[uiManager updateView:@6 viewName:@"RCTView" props:@{@"borderWidth":[NSNull null]}];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)3);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
expectation = [self expectationWithDescription:@""];
dispatch_async(shadowQueue, ^{
[uiManager updateView:@6 viewName:@"RCTView" props:@{@"borderWidth":@1}];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)4);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
}
@end
+12 -12
View File
@@ -58,19 +58,19 @@ var WebViewExample = React.createClass({
return (
<View style={[styles.container]}>
<View style={[styles.addressBarRow]}>
<TouchableOpacity onPress={this.goBack}>
<View style={this.state.backButtonEnabled ? styles.navButton : styles.disabledButton}>
<Text>
{'<'}
</Text>
</View>
<TouchableOpacity
onPress={this.goBack}
style={this.state.backButtonEnabled ? styles.navButton : styles.disabledButton}>
<Text>
{'<'}
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.goForward}>
<View style={this.state.forwardButtonEnabled ? styles.navButton : styles.disabledButton}>
<Text>
{'>'}
</Text>
</View>
<TouchableOpacity
onPress={this.goForward}
style={this.state.forwardButtonEnabled ? styles.navButton : styles.disabledButton}>
<Text>
{'>'}
</Text>
</TouchableOpacity>
<TextInput
ref={TEXT_INPUT_REF}
+13 -1
View File
@@ -128,6 +128,7 @@ class FormUploader extends React.Component {
super(props);
this.state = {
isUploading: false,
uploadProgress: null,
randomPhoto: null,
textParams: [],
};
@@ -143,7 +144,6 @@ class FormUploader extends React.Component {
CameraRoll.getPhotos(
{first: PAGE_SIZE},
(data) => {
console.log('isMounted', this._isMounted);
if (!this._isMounted) {
return;
}
@@ -217,6 +217,14 @@ class FormUploader extends React.Component {
this.state.textParams.forEach(
(param) => formdata.append(param.name, param.value)
);
if (xhr.upload) {
xhr.upload.onprogress = (event) => {
console.log('upload onprogress', event);
if (event.lengthComputable) {
this.setState({uploadProgress: event.loaded / event.total});
}
};
}
xhr.send(formdata);
this.setState({isUploading: true});
}
@@ -251,6 +259,10 @@ class FormUploader extends React.Component {
</View>
));
var uploadButtonLabel = this.state.isUploading ? 'Uploading...' : 'Upload';
var uploadProgress = this.state.uploadProgress;
if (uploadProgress !== null) {
uploadButtonLabel += ' ' + Math.round(uploadProgress * 100) + '%';
}
var uploadButton = (
<View style={styles.uploadButtonBox}>
<Text style={styles.uploadButtonLabel}>{uploadButtonLabel}</Text>
+3 -1
View File
@@ -54,7 +54,9 @@ var createExamplePage = function(title: ?string, exampleModule: ExampleModule)
};
var result = example.render(null);
if (result) {
renderedComponent = result;
renderedComponent = React.cloneElement(result, {
navigator: this.props.navigator,
});
}
(React: Object).render = originalRender;
(React: Object).renderComponent = originalRenderComponent;
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#import "JSContextRef.h"
extern "C" {
JSValueRef nativeProfilerStart(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
JSValueRef nativeProfilerEnd(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
}
+161
View File
@@ -0,0 +1,161 @@
//#include "config.h"
#include "JSCLegacyProfiler.h"
#include "APICast.h"
#include "LegacyProfiler.h"
#include "OpaqueJSString.h"
#include "JSProfilerPrivate.h"
#include "JSStringRef.h"
#include <yajl/yajl_gen.h>
#define GEN_AND_CHECK(expr) \
do { \
yajl_gen_status GEN_AND_CHECK_status = (expr); \
if (GEN_AND_CHECK_status != yajl_gen_status_ok) { \
return GEN_AND_CHECK_status; \
} \
} while (false)
static inline yajl_gen_status yajl_gen_cstring(yajl_gen gen, const char *str) {
return yajl_gen_string(gen, (const unsigned char*)str, strlen(str));
}
static yajl_gen_status append_children_array_json(yajl_gen gen, const JSC::ProfileNode *node);
static yajl_gen_status append_node_json(yajl_gen gen, const JSC::ProfileNode *node);
static yajl_gen_status append_root_json(yajl_gen gen, const JSC::Profile *profile) {
GEN_AND_CHECK(yajl_gen_map_open(gen));
GEN_AND_CHECK(yajl_gen_cstring(gen, "rootNodes"));
GEN_AND_CHECK(append_children_array_json(gen, profile->head()));
GEN_AND_CHECK(yajl_gen_map_close(gen));
return yajl_gen_status_ok;
}
static yajl_gen_status append_children_array_json(yajl_gen gen, const JSC::ProfileNode *node) {
GEN_AND_CHECK(yajl_gen_array_open(gen));
for (RefPtr<JSC::ProfileNode> child : node->children()) {
GEN_AND_CHECK(append_node_json(gen, child.get()));
}
GEN_AND_CHECK(yajl_gen_array_close(gen));
return yajl_gen_status_ok;
}
static yajl_gen_status append_node_json(yajl_gen gen, const JSC::ProfileNode *node) {
GEN_AND_CHECK(yajl_gen_map_open(gen));
GEN_AND_CHECK(yajl_gen_cstring(gen, "id"));
GEN_AND_CHECK(yajl_gen_integer(gen, node->id()));
if (!node->functionName().isEmpty()) {
GEN_AND_CHECK(yajl_gen_cstring(gen, "functionName"));
GEN_AND_CHECK(yajl_gen_cstring(gen, node->functionName().utf8().data()));
}
if (!node->url().isEmpty()) {
GEN_AND_CHECK(yajl_gen_cstring(gen, "url"));
GEN_AND_CHECK(yajl_gen_cstring(gen, node->url().utf8().data()));
GEN_AND_CHECK(yajl_gen_cstring(gen, "lineNumber"));
GEN_AND_CHECK(yajl_gen_integer(gen, node->lineNumber()));
GEN_AND_CHECK(yajl_gen_cstring(gen, "columnNumber"));
GEN_AND_CHECK(yajl_gen_integer(gen, node->columnNumber()));
}
GEN_AND_CHECK(yajl_gen_cstring(gen, "calls"));
GEN_AND_CHECK(yajl_gen_array_open(gen));
for (const JSC::ProfileNode::Call &call : node->calls()) {
GEN_AND_CHECK(yajl_gen_map_open(gen));
GEN_AND_CHECK(yajl_gen_cstring(gen, "startTime"));
GEN_AND_CHECK(yajl_gen_double(gen, call.startTime()));
GEN_AND_CHECK(yajl_gen_cstring(gen, "totalTime"));
GEN_AND_CHECK(yajl_gen_double(gen, call.totalTime()));
GEN_AND_CHECK(yajl_gen_map_close(gen));
}
GEN_AND_CHECK(yajl_gen_array_close(gen));
if (!node->children().isEmpty()) {
GEN_AND_CHECK(yajl_gen_cstring(gen, "children"));
GEN_AND_CHECK(append_children_array_json(gen, node));
}
GEN_AND_CHECK(yajl_gen_map_close(gen));
return yajl_gen_status_ok;
}
static char *render_error_code(yajl_gen_status status) {
char err[1024];
snprintf(err, sizeof(err), "{\"error\": %d}", (int)status);
return strdup(err);
}
static char *convert_to_json(const JSC::Profile *profile) {
yajl_gen_status status;
yajl_gen gen = yajl_gen_alloc(NULL);
status = append_root_json(gen, profile);
if (status != yajl_gen_status_ok) {
yajl_gen_free(gen);
return render_error_code(status);
}
const unsigned char *buf;
size_t buf_size;
status = yajl_gen_get_buf(gen, &buf, &buf_size);
if (status != yajl_gen_status_ok) {
yajl_gen_free(gen);
return render_error_code(status);
}
char *json_copy = strdup((const char*)buf);
yajl_gen_free(gen);
return json_copy;
}
static char *JSEndProfilingAndRender(JSContextRef ctx, JSStringRef title)
{
JSC::ExecState *exec = toJS(ctx);
JSC::LegacyProfiler *profiler = JSC::LegacyProfiler::profiler();
RefPtr<JSC::Profile> rawProfile = profiler->stopProfiling(exec, title->string());
return convert_to_json(rawProfile.get());
}
JSValueRef nativeProfilerStart(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount < 1) {
// Could raise an exception here.
return JSValueMakeUndefined(ctx);
}
JSStringRef title = JSValueToStringCopy(ctx, arguments[0], NULL);
JSStartProfiling(ctx, title);
JSStringRelease(title);
return JSValueMakeUndefined(ctx);
}
JSValueRef nativeProfilerEnd(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount < 1) {
// Could raise an exception here.
return JSValueMakeUndefined(ctx);
}
JSStringRef title = JSValueToStringCopy(ctx, arguments[0], NULL);
char *rendered = JSEndProfilingAndRender(ctx, title);
JSStringRelease(title);
JSStringRef profile = JSStringCreateWithUTF8CString(rendered);
free(rendered);
return JSValueMakeString(ctx, profile);
}
+108
View File
@@ -0,0 +1,108 @@
HEADER_PATHS := `find ./tmp/JavaScriptCore -name '*.h' | xargs -I{} dirname {} | uniq | xargs -I{} echo "-I {}"`
CERT ?= "iPhone Developer"
ios8: prepare build generate
prepare: clean create download
build: x86_64 arm64 armv7
generate: lipo codesign
clean:
@rm -rf tmp/ /tmp/RCTJSCProfiler
lipo:
lipo -create -output /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib ./tmp/RCTJSCProfiler_x86_64 ./tmp/RCTJSCProfiler_arm64 ./tmp/RCTJSCProfiler_armv7
codesign:
codesign -f -s ${CERT} /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib
create:
mkdir -p ./tmp /tmp/RCTJSCProfiler/ ./tmp/CoreFoundation ./tmp/Foundation
for file in ./tmp/CoreFoundation/CFUserNotification.h ./tmp/CoreFoundation/CFXMLNode.h ./tmp/CoreFoundation/CFXMLParser.h ./tmp/Foundation/Foundation.h; do echo '' > "$$file"; done
download: wtf jsc webcore yajl
wtf:
curl -o tmp/WTF.tar.gz http://www.opensource.apple.com/tarballs/WTF/WTF-7600.1.24.tar.gz
tar -zxvf tmp/WTF.tar.gz -C tmp
jsc:
curl -o tmp/JSC.tar.gz http://www.opensource.apple.com/tarballs/JavaScriptCore/JavaScriptCore-7600.1.17.tar.gz
tar -zxvf tmp/JSC.tar.gz -C tmp
mv ./tmp/JavaScriptCore-7600.1.17 ./tmp/JavaScriptCore
python ./tmp/JavaScriptCore/generate-bytecode-files --bytecodes_h ./tmp/JavaScriptCore/Bytecodes.h ./tmp/JavaScriptCore/bytecode/BytecodeList.json
webcore:
curl -o tmp/WebCore.tar.gz http://www.opensource.apple.com/tarballs/WebCore/WebCore-7600.1.25.tar.gz
tar -zxvf tmp/WebCore.tar.gz -C tmp
yajl:
curl -o tmp/yajl.tar.gz https://codeload.github.com/lloyd/yajl/tar.gz/2.1.0
tar -zxvf tmp/yajl.tar.gz -C tmp
mkdir -p ./tmp/yajl-2.1.0/build && cd ./tmp/yajl-2.1.0/build && cmake .. && make
echo `find . -name '*.c'`
cd ./tmp/yajl-2.1.0/src && \
clang -arch arm64 -arch armv7 -std=c99 \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/ \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/machine \
-I ../build/yajl-2.1.0/include \
-c `find . -name '*.c'`
libtool -static -o ./tmp/yajl.a `find ./tmp/yajl-2.1.0/src/ -name '*.o'`
x86_64:
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_x86_64 -std=c++11 \
-install_name RCTJSCProfiler.ios8.dylib \
-include ./tmp/JavaScriptCore/config.h \
-I ./tmp \
-I ./tmp/WebCore-7600.1.25/icu \
-I ./tmp/WTF-7600.1.24 \
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
-DNDEBUG=1\
-miphoneos-version-min=8.0 \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/system \
${HEADER_PATHS} \
-undefined dynamic_lookup \
./JSCLegacyProfiler.mm ./tmp/yajl-2.1.0/build/yajl-2.1.0/lib/libyajl_s.a
arm64:
echo $(HEADER_PATHS)
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_arm64 -std=c++11 \
-install_name RCTJSCProfiler.ios8.dylib \
-arch arm64 \
-include ./tmp/JavaScriptCore/config.h \
-I ./tmp \
-I ./tmp/WebCore-7600.1.25/icu \
-I ./tmp/WTF-7600.1.24 \
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/machine \
-DNDEBUG=1\
-miphoneos-version-min=8.0 \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib/system \
${HEADER_PATHS} \
-undefined dynamic_lookup \
./JSCLegacyProfiler.mm ./tmp/yajl.a
armv7:
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_armv7 -std=c++11 \
-install_name RCTJSCProfiler.ios8.dylib \
-arch armv7 \
-include ./tmp/JavaScriptCore/config.h \
-I ./tmp \
-I ./tmp/WebCore-7600.1.25/icu \
-I ./tmp/WTF-7600.1.24 \
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include \
-DNDEBUG=1\
-miphoneos-version-min=8.0 \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib/system \
${HEADER_PATHS} \
-undefined dynamic_lookup \
./JSCLegacyProfiler.mm ./tmp/yajl.a
.PHONY: ios8
+2 -2
View File
@@ -87,7 +87,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
}
NSDictionary *fontDict = dict[@"font"];
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"]];
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
if (!font) {
return frame;
}
@@ -144,7 +144,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
+ (ARTBrush *)ARTBrush:(id)json
{
NSArray *arr = [self NSArray:json];
NSUInteger type = [self NSUInteger:arr[0]];
NSUInteger type = [self NSUInteger:arr.firstObject];
switch (type) {
case 0: // solid color
// These are probably expensive allocations since it's often the same value.
@@ -151,6 +151,7 @@
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
@@ -190,6 +191,7 @@
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
@@ -9,6 +9,7 @@
#import "RCTActionSheetManager.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#import "RCTUtils.h"
@@ -72,13 +73,13 @@ RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
successCallback:(RCTResponseSenderBlock)successCallback)
{
NSMutableArray *items = [NSMutableArray array];
id message = options[@"message"];
id url = options[@"url"];
if ([message isKindOfClass:[NSString class]]) {
NSString *message = [RCTConvert NSString:options[@"message"]];
if (message) {
[items addObject:message];
}
if ([url isKindOfClass:[NSString class]]) {
[items addObject:[NSURL URLWithString:url]];
NSURL *URL = [RCTConvert NSURL:options[@"url"]];
if (URL) {
[items addObject:URL];
}
if ([items count] == 0) {
failureCallback(@[@"No `url` or `message` to share"]);
@@ -151,6 +151,7 @@
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
@@ -195,6 +196,7 @@
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
+17 -5
View File
@@ -17,12 +17,12 @@ var InteractionManager = require('InteractionManager');
var Interpolation = require('Interpolation');
var React = require('React');
var Set = require('Set');
var SpringConfig = require('SpringConfig');
var Text = require('Text');
var View = require('View');
var invariant = require('invariant');
var flattenStyle = require('flattenStyle');
var rebound = require('rebound');
var requestAnimationFrame = require('requestAnimationFrame');
import type InterpolationConfigType from 'Interpolation';
@@ -351,12 +351,12 @@ class SpringAnimation extends Animation {
config.tension === undefined && config.friction === undefined,
'You can only define bounciness/speed or tension/friction but not both',
);
springConfig = rebound.SpringConfig.fromBouncinessAndSpeed(
springConfig = SpringConfig.fromBouncinessAndSpeed(
withDefault(config.bounciness, 8),
withDefault(config.speed, 12),
);
} else {
springConfig = rebound.SpringConfig.fromOrigamiTensionAndFriction(
springConfig = SpringConfig.fromOrigamiTensionAndFriction(
withDefault(config.tension, 40),
withDefault(config.friction, 7),
);
@@ -479,7 +479,13 @@ class SpringAnimation extends Animation {
if (this._tension !== 0) {
isDisplacement = Math.abs(this._toValue - position) <= this._restDisplacementThreshold;
}
if (isOvershooting || (isVelocity && isDisplacement)) {
if (this._tension !== 0) {
// Ensure that we end up with a round value
this._onUpdate(this._toValue);
}
this.__debouncedOnEnd({finished: true});
return;
}
@@ -1177,7 +1183,7 @@ var parallel = function(
}
animations.forEach((animation, idx) => {
animation.start(endResult => {
var cb = function(endResult) {
hasEnded[idx] = true;
doneCount++;
if (doneCount === animations.length) {
@@ -1189,7 +1195,13 @@ var parallel = function(
if (!endResult.finished && stopTogether) {
result.stop();
}
});
};
if (!animation) {
cb({finished: true});
} else {
animation.start(cb);
}
});
},
@@ -0,0 +1,102 @@
/**
* 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 SpringConfig
* @flow
*/
'use strict';
type SpringConfigType = {
tension: number,
friction: number,
};
function tensionFromOrigamiValue(oValue) {
return (oValue - 30) * 3.62 + 194;
}
function frictionFromOrigamiValue(oValue) {
return (oValue - 8) * 3 + 25;
}
function fromOrigamiTensionAndFriction(
tension: number,
friction: number,
): SpringConfigType {
return {
tension: tensionFromOrigamiValue(tension),
friction: frictionFromOrigamiValue(friction)
};
}
function fromBouncinessAndSpeed(
bounciness: number,
speed: number,
): SpringConfigType {
function normalize(value, startValue, endValue) {
return (value - startValue) / (endValue - startValue);
}
function projectNormal(n, start, end) {
return start + (n * (end - start));
}
function linearInterpolation(t, start, end) {
return t * end + (1 - t) * start;
}
function quadraticOutInterpolation(t, start, end) {
return linearInterpolation(2 * t - t * t, start, end);
}
function b3Friction1(x) {
return (0.0007 * Math.pow(x, 3)) -
(0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28;
}
function b3Friction2(x) {
return (0.000044 * Math.pow(x, 3)) -
(0.006 * Math.pow(x, 2)) + 0.36 * x + 2;
}
function b3Friction3(x) {
return (0.00000045 * Math.pow(x, 3)) -
(0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84;
}
function b3Nobounce(tension) {
if (tension <= 18) {
return b3Friction1(tension);
} else if (tension > 18 && tension <= 44) {
return b3Friction2(tension);
} else {
return b3Friction3(tension);
}
}
var b = normalize(bounciness / 1.7, 0, 20);
b = projectNormal(b, 0, 0.8);
var s = normalize(speed / 1.7, 0, 20);
var bouncyTension = projectNormal(s, 0.5, 200);
var bouncyFriction = quadraticOutInterpolation(
b,
b3Nobounce(bouncyTension),
0.01
);
return {
tension: tensionFromOrigamiValue(bouncyTension),
friction: frictionFromOrigamiValue(bouncyFriction)
};
}
module.exports = {
fromOrigamiTensionAndFriction,
fromBouncinessAndSpeed,
};
@@ -127,6 +127,19 @@ describe('Animated', () => {
Animated.spring(anim, {toValue: 0, velocity: 0}).start(callback);
expect(callback).toBeCalled();
});
it('send toValue when a spring stops', () => {
var anim = new Animated.Value(0);
var listener = jest.genMockFunction();
anim.addListener(listener);
Animated.spring(anim, {toValue: 15}).start();
jest.runAllTimers();
var lastValue = listener.mock.calls[listener.mock.calls.length - 2][0].value;
expect(lastValue).not.toBe(15);
expect(lastValue).toBeCloseTo(15);
expect(anim.__getValue()).toBe(15);
});
});
@@ -205,6 +218,16 @@ describe('Animated Parallel', () => {
expect(cb).toBeCalledWith({finished: true});
});
it('works with an empty element in array', () => {
var anim1 = {start: jest.genMockFunction()};
var cb = jest.genMockFunction();
Animated.parallel([null, anim1]).start(cb);
expect(anim1.start).toBeCalled();
anim1.start.mock.calls[0][0]({finished: true});
expect(cb).toBeCalledWith({finished: true});
});
it('parellelizes well', () => {
var anim1 = {start: jest.genMockFunction()};
@@ -1,99 +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 AnimationExperimental
*/
'use strict';
var RCTAnimationManager = require('NativeModules').AnimationExperimentalManager;
if (!RCTAnimationManager) {
// AnimationExperimental isn't available internally - this is a temporary
// workaround to enable its availability to be determined at runtime.
// For Flow let's pretend like we always export AnimationExperimental
// so all our users don't need to do null checks
module.exports = null;
} else {
var React = require('React');
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 = React.findNodeHandle(anim.node);
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;
}
-244
View File
@@ -1,244 +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.
*
* The easing functions come from the the jQuery UI project.
* See http://api.jqueryui.com/easings/
* Copyright jQuery Foundation and other contributors, https://jquery.org/
* Copyright (c) 2008 George McGinley Smith
* Copyright (c) 2001 Robert Penner
*
* @providesModule AnimationUtils
* @flow
*/
'use strict';
type EasingFunction = (t: number) => number;
var defaults = {
linear: function(t: number): number {
return t;
},
easeInQuad: function(t: number): number {
return t * t;
},
easeOutQuad: function(t: number): number {
return -t * (t - 2);
},
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: number): number {
return t * t * t;
},
easeOutCubic: function(t: number): number {
t -= 1;
return t * t * t + 1;
},
easeInOutCubic: function(t: number): number {
t *= 2;
if (t < 1) {
return 0.5 * t * t * t;
}
t -= 2;
return (t * t * t + 2) / 2;
},
easeInQuart: function(t: number): number {
return t * t * t * t;
},
easeOutQuart: function(t: number): number {
t -= 1;
return -(t * t * t * t - 1);
},
easeInOutQuart: function(t: number): number {
t *= 2;
if (t < 1) {
return 0.5 * t * t * t * t;
}
t -= 2;
return -(t * t * t * t - 2) / 2;
},
easeInQuint: function(t: number): number {
return t * t * t * t * t;
},
easeOutQuint: function(t: number): number {
t -= 1;
return t * t * t * t * t + 1;
},
easeInOutQuint: function(t: number): number {
t *= 2;
if (t < 1) {
return (t * t * t * t * t) / 2;
}
t -= 2;
return (t * t * t * t * t + 2) / 2;
},
easeInSine: function(t: number): number {
return -Math.cos(t * (Math.PI / 2)) + 1;
},
easeOutSine: function(t: number): number {
return Math.sin(t * (Math.PI / 2));
},
easeInOutSine: function(t: number): number {
return -(Math.cos(Math.PI * t) - 1) / 2;
},
easeInExpo: function(t: number): number {
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
},
easeOutExpo: function(t: number): number {
return (t === 1) ? 1 : (-Math.pow(2, -10 * t) + 1);
},
easeInOutExpo: function(t: number): number {
if (t === 0) {
return 0;
}
if (t === 1) {
return 1;
}
t *= 2;
if (t < 1) {
return 0.5 * Math.pow(2, 10 * (t - 1));
}
return (-Math.pow(2, -10 * (t - 1)) + 2) / 2;
},
easeInCirc: function(t: number): number {
return -(Math.sqrt(1 - t * t) - 1);
},
easeOutCirc: function(t: number): number {
t -= 1;
return Math.sqrt(1 - t * t);
},
easeInOutCirc: function(t: number): number {
t *= 2;
if (t < 1) {
return -(Math.sqrt(1 - t * t) - 1) / 2;
}
t -= 2;
return (Math.sqrt(1 - t * t) + 1) / 2;
},
easeInElastic: function(t: number): number {
var s = 1.70158;
var p = 0.3;
if (t === 0) {
return 0;
}
if (t === 1) {
return 1;
}
var s = p / (2 * Math.PI) * Math.asin(1);
t -= 1;
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
},
easeOutElastic: function(t: number): number {
var s = 1.70158;
var p = 0.3;
if (t === 0) {
return 0;
}
if (t === 1) {
return 1;
}
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: number): number {
var s = 1.70158;
var p = 0.3 * 1.5;
if (t === 0) {
return 0;
}
t *= 2;
if (t === 2) {
return 1;
}
var s = p / (2 * Math.PI) * Math.asin(1);
if (t < 1) {
t -= 1;
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)) / 2;
}
t -= 1;
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) / 2 + 1;
},
easeInBack: function(t: number): number {
var s = 1.70158;
return t * t * ((s + 1) * t - s);
},
easeOutBack: function(t: number): number {
var s = 1.70158;
t -= 1;
return (t * t * ((s + 1) * t + s) + 1);
},
easeInOutBack: function(t: number): number {
var s = 1.70158 * 1.525;
t *= 2;
if (t < 1) {
return (t * t * ((s + 1) * t - s)) / 2;
}
t -= 2;
return (t * t * ((s + 1) * t + s) + 2) / 2;
},
easeInBounce: function(t: number): number {
return 1 - this.easeOutBounce(1 - t);
},
easeOutBounce: function(t: number): number {
if (t < (1 / 2.75)) {
return 7.5625 * t * t;
} else if (t < (2 / 2.75)) {
t -= 1.5 / 2.75;
return 7.5625 * t * t + 0.75;
} else if (t < (2.5 / 2.75)) {
t -= 2.25 / 2.75;
return 7.5625 * t * t + 0.9375;
} else {
t -= 2.625 / 2.75;
return 7.5625 * t * t + 0.984375;
}
},
easeInOutBounce: function(t: number): number {
if (t < 0.5) {
return this.easeInBounce(t * 2) / 2;
}
return this.easeOutBounce(t * 2 - 1) / 2 + 0.5;
},
};
var ticksPerSecond = 60;
var lastUsedTag = 0;
module.exports = {
/**
* Returns a new unique animation tag.
*/
allocateTag: function(): number {
return ++lastUsedTag;
},
/**
* Returns the values of the easing function at discrete ticks (60 per second).
*/
evaluateEasingFunction: function(duration: number, easing: string | EasingFunction): Array<number> {
if (typeof easing === 'string') {
easing = defaults[easing] || defaults.easeOutQuad;
}
var tickCount = Math.round(duration * ticksPerSecond / 1000);
var samples = [];
if (tickCount > 0) {
for (var i = 0; i <= tickCount; i++) {
samples.push(easing.call(defaults, i / tickCount));
}
}
return samples;
},
Defaults: defaults,
};
+28 -2
View File
@@ -69,9 +69,11 @@ type Config = {
delete?: Anim;
}
function configureNext(config: Config, onAnimationDidEnd?: Function, onError?: Function) {
function configureNext(config: Config, onAnimationDidEnd?: Function) {
configChecker({config}, 'config', 'LayoutAnimation.configureNext');
RCTUIManager.configureNextLayoutAnimation(config, onAnimationDidEnd, onError);
RCTUIManager.configureNextLayoutAnimation(
config, onAnimationDidEnd || function() {}, function() { /* unused */ }
);
}
function create(duration: number, type, creationProp): Config {
@@ -107,8 +109,32 @@ var Presets = {
},
};
/**
* Automatically animates views to their new positions when the
* next layout happens.
*
* A common way to use this API is to call `LayoutAnimation.configureNext`
* before calling `setState`.
*/
var LayoutAnimation = {
/**
* Schedules an animation to happen on the next layout.
*
* @param config Specifies animation properties:
*
* - `duration` in milliseconds
* - `create`, config for animating in new views (see `Anim` type)
* - `update`, config for animating views that have been updated
* (see `Anim` type)
*
* @param onAnimationDidEnd Called when the animation finished.
* Only supported on iOS.
* @param onError Called on error. Only supported on iOS.
*/
configureNext,
/**
* Helper for creating a config for `configureNext`.
*/
create,
Types,
Properties,
@@ -1,250 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
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 */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */,
);
name = Products;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */,
13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RCTAnimationExperimental;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTAnimationExperimental;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTAnimationExperimental;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}
@@ -1,299 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "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
#else
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_F
#endif
@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;
}
- (dispatch_queue_t)methodQueue
{
return _bridge.uiManager.methodQueue;
}
- (id (^)(CGFloat))interpolateFrom:(CGFloat[])fromArray to:(CGFloat[])toArray count:(NSUInteger)count typeName:(const char *)typeName
{
if (count == 1) {
CGFloat from = *fromArray, to = *toArray, delta = to - from;
return ^(CGFloat t) {
return @(from + t * delta);
};
}
CG_APPEND(vDSP_vsub,,D)(fromArray, 1, toArray, 1, toArray, 1, count);
const size_t size = count * sizeof(CGFloat);
NSData *deltaData = [NSData dataWithBytes:toArray length:size];
NSData *fromData = [NSData dataWithBytes:fromArray length:size];
return ^(CGFloat t) {
const CGFloat *delta = deltaData.bytes;
const CGFloat *_fromArray = fromData.bytes;
CGFloat value[count];
CG_APPEND(vDSP_vma,,D)(delta, 1, &t, 0, _fromArray, 1, value, 1, count);
return [NSValue valueWithBytes:value objCType:typeName];
};
}
static void RCTInvalidAnimationProp(RCTSparseArray *callbacks, NSNumber *tag, NSString *key, id value)
{
RCTResponseSenderBlock callback = callbacks[tag];
RCTLogError(@"Invalid animation property `%@ = %@`", key, value);
if (callback) {
callback(@[@NO]);
callbacks[tag] = nil;
}
[CATransaction commit];
return;
}
RCT_EXPORT_METHOD(startAnimation:(NSNumber *)reactTag
animationTag:(NSNumber *)animationTag
duration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
easingSample:(NSNumberArray *)easingSample
properties:(NSDictionary *)properties
callback:(RCTResponseSenderBlock)callback)
{
__weak RCTAnimationExperimentalManager *weakSelf = self;
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTAnimationExperimentalManager *strongSelf = weakSelf;
UIView *view = viewRegistry[reactTag];
if (!view) {
RCTLogWarn(@"React tag #%@ is not registered with the view registry", reactTag);
return;
}
__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 ([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:
if (![obj respondsToSelector:@selector(objectForKeyedSubscript:)] || obj[@"x"]) {
toValue = [NSValue valueWithCGPoint:[RCTConvert CGPoint:obj]];
} else {
toValue = [NSValue valueWithCGSize:[RCTConvert CGSize:obj]];
}
break;
case 4:
toValue = [NSValue valueWithCGRect:[RCTConvert CGRect:obj]];
break;
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;
}
const char *typeName = toValue.objCType;
size_t count;
switch (typeName[0]) {
case 'i':
case 'I':
case 's':
case 'S':
case 'l':
case 'L':
case 'q':
case 'Q':
count = 1;
break;
default: {
NSUInteger size;
NSGetSizeAndAlignment(typeName, &size, NULL);
count = size / sizeof(CGFloat);
break;
}
}
CGFloat toFields[count];
switch (typeName[0]) {
#define CASE(encoding, type) \
case encoding: { \
type value; \
[toValue getValue:&value]; \
toFields[0] = value; \
break; \
}
CASE('i', int)
CASE('I', unsigned int)
CASE('s', short)
CASE('S', unsigned short)
CASE('l', long)
CASE('L', unsigned long)
CASE('q', long long)
CASE('Q', unsigned long long)
#undef CASE
default:
[toValue getValue:toFields];
break;
}
NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:keypath];
#if !CGFLOAT_IS_DOUBLE
if ([fromValue isKindOfClass:[NSNumber class]]) {
fromValue = [NSNumber numberWithFloat:[(NSNumber *)fromValue doubleValue]];
}
#endif
CGFloat fromFields[count];
[fromValue getValue:fromFields];
id (^interpolationBlock)(CGFloat t) = [strongSelf interpolateFrom:fromFields to:toFields count:count typeName:typeName];
NSMutableArray *sampledValues = [NSMutableArray arrayWithCapacity:easingSample.count];
for (NSNumber *sample in easingSample) {
CGFloat t = sample.CG_APPEND(, floatValue, doubleValue);
[sampledValues addObject:interpolationBlock(t)];
}
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
animation.beginTime = CACurrentMediaTime() + delay;
animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.values = sampledValues;
@try {
[view.layer setValue:toValue forKey:keypath];
NSString *animationKey = [@"RCT" stringByAppendingString:RCTJSONStringify(@{@"tag": animationTag, @"key": keypath}, nil)];
if (!completionBlockSet) {
strongSelf->_callbackRegistry[animationTag] = callback;
[CATransaction setCompletionBlock:^{
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
if (cb) {
cb(@[@YES]);
strongSelf->_callbackRegistry[animationTag] = nil;
}
}];
completionBlockSet = YES;
}
[view.layer addAnimation:animation forKey:animationKey];
}
@catch (NSException *exception) {
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, toValue);
}
}
[CATransaction commit];
strongSelf->_animationRegistry[animationTag] = reactTag;
}];
}
RCT_EXPORT_METHOD(stopAnimation:(NSNumber *)animationTag)
{
__weak RCTAnimationExperimentalManager *weakSelf = self;
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
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{"]) {
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
+9 -2
View File
@@ -22,9 +22,11 @@ if (__DEV__) {
var runnables = {};
type ComponentProvider = () => ReactClass<any, any, any>;
type AppConfig = {
appKey: string;
component: ReactClass<any, any, any>;
component?: ComponentProvider;
run?: Function;
};
@@ -46,12 +48,13 @@ var AppRegistry = {
if (appConfig.run) {
AppRegistry.registerRunnable(appConfig.appKey, appConfig.run);
} else {
invariant(appConfig.component, 'No component provider passed in');
AppRegistry.registerComponent(appConfig.appKey, appConfig.component);
}
}
},
registerComponent: function(appKey: string, getComponentFunc: Function): string {
registerComponent: function(appKey: string, getComponentFunc: ComponentProvider): string {
runnables[appKey] = {
run: (appParameters) =>
renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)
@@ -64,6 +67,10 @@ var AppRegistry = {
return appKey;
},
getAppKeys: function(): Array<string> {
return Object.keys(runnables);
},
runApplication: function(appKey: string, appParameters: any): void {
console.log(
'Running application "' + appKey + '" with appParams: ' +
@@ -40,6 +40,10 @@ var NativeModules = {
SourceCode: {
scriptURL: null,
},
BuildInfo: {
appVersion: '0',
buildVersion: '0',
},
};
module.exports = NativeModules;
+20 -9
View File
@@ -109,17 +109,28 @@ var getPhotosReturnChecker = createStrictShapeTypeChecker({
}).isRequired,
});
/**
* `CameraRoll` provides access to the local camera roll / gallery.
*/
class CameraRoll {
static GroupTypesOptions: Array<string>;
static AssetTypeOptions: Array<string>;
/**
* Saves the image with tag `tag` to the camera roll.
* Saves the image to the camera roll / gallery.
*
* @param {string} tag - Can be any of the three kinds of tags we accept:
* 1. URL
* 2. assets-library tag
* 3. tag returned from storing an image in memory
* @param {string} tag On Android, this is a local URI, such
* as `"file:///sdcard/img.png"`.
*
* On iOS, the tag can be one of the following:
*
* - local URI
* - assets-library tag
* - a tag not maching any of the above, which means the image data will
* be stored in memory (and consume memory as long as the process is alive)
*
* @param successCallback Invoked with the value of `tag` on success.
* @param errorCallback Invoked with error message on error.
*/
static saveImageWithTag(tag, successCallback, errorCallback) {
invariant(
@@ -140,10 +151,10 @@ class CameraRoll {
* Invokes `callback` with photo identifier objects from the local camera
* roll of the device matching shape defined by `getPhotosReturnChecker`.
*
* @param {object} params - See `getPhotosParamChecker`.
* @param {function} callback - Invoked with arg of shape defined by
* `getPhotosReturnChecker` on success.
* @param {function} errorCallback - Invoked with error message on error.
* @param {object} params See `getPhotosParamChecker`.
* @param {function} callback Invoked with arg of shape defined by
* `getPhotosReturnChecker` on success.
* @param {function} errorCallback Invoked with error message on error.
*/
static getPhotos(params, callback, errorCallback) {
var metaCallback = callback;
@@ -18,7 +18,6 @@ var StyleSheet = require('StyleSheet');
var View = require('View');
var requireNativeComponent = require('requireNativeComponent');
var verifyPropTypes = require('verifyPropTypes');
var GRAY = '#999999';
@@ -99,15 +98,8 @@ var styles = StyleSheet.create({
var RCTActivityIndicatorView = requireNativeComponent(
'RCTActivityIndicatorView',
null
ActivityIndicatorIOS,
{nativeOnly: {activityIndicatorViewStyle: true}},
);
if (__DEV__) {
var nativeOnlyProps = {activityIndicatorViewStyle: true};
verifyPropTypes(
ActivityIndicatorIOS,
RCTActivityIndicatorView.viewConfig,
nativeOnlyProps
);
}
module.exports = ActivityIndicatorIOS;
@@ -15,17 +15,14 @@ var EventEmitter = require('EventEmitter');
var Image = require('Image');
var NavigationContext = require('NavigationContext');
var React = require('React');
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var RCTNavigatorManager = require('NativeModules').NavigatorManager;
var StyleSheet = require('StyleSheet');
var StaticContainer = require('StaticContainer.react');
var View = require('View');
var createReactNativeComponentClass =
require('createReactNativeComponentClass');
var requireNativeComponent = require('requireNativeComponent');
var invariant = require('invariant');
var logError = require('logError');
var merge = require('merge');
var TRANSITIONER_REF = 'transitionerRef';
@@ -36,36 +33,6 @@ function getuid() {
return __uid++;
}
var RCTNavigator = createReactNativeComponentClass({
validAttributes: merge(ReactNativeViewAttributes.UIView, {
requestedTopOfStack: true
}),
uiViewClassName: 'RCTNavigator',
});
var RCTNavigatorItem = createReactNativeComponentClass({
validAttributes: {
// TODO: Remove or fix the attributes that are not fully functional.
// NavigatorIOS does not use them all, because some are problematic
title: true,
barTintColor: true,
leftButtonIcon: true,
leftButtonTitle: true,
onNavLeftButtonTap: true,
rightButtonIcon: true,
rightButtonTitle: true,
onNavRightButtonTap: true,
backButtonIcon: true,
backButtonTitle: true,
tintColor: true,
translucent: true,
navigationBarHidden: true,
titleTextColor: true,
style: true,
},
uiViewClassName: 'RCTNavItem',
});
var NavigatorTransitionerIOS = React.createClass({
requestSchedulingNavigation: function(cb) {
RCTNavigatorManager.requestSchedulingJavaScriptNavigation(
@@ -281,6 +248,11 @@ var NavigatorIOS = React.createClass({
*/
navigationBarHidden: PropTypes.bool,
/**
* A Boolean value that indicates whether to hide the 1px hairline shadow
*/
shadowHidden: PropTypes.bool,
/**
* The default wrapper style for components in the navigator.
* A common use case is to set the backgroundColor for every page
@@ -640,6 +612,7 @@ var NavigatorIOS = React.createClass({
rightButtonTitle={route.rightButtonTitle}
onNavRightButtonTap={route.onRightButtonPress}
navigationBarHidden={this.props.navigationBarHidden}
shadowHidden={this.props.shadowHidden}
tintColor={this.props.tintColor}
barTintColor={this.props.barTintColor}
translucent={this.props.translucent !== false}
@@ -704,4 +677,7 @@ var styles = StyleSheet.create({
},
});
var RCTNavigator = requireNativeComponent('RCTNavigator');
var RCTNavigatorItem = requireNativeComponent('RCTNavItem');
module.exports = NavigatorIOS;
@@ -19,7 +19,6 @@ var React = require('React');
var StyleSheet = require('StyleSheet');
var requireNativeComponent = require('requireNativeComponent');
var verifyPropTypes = require('verifyPropTypes');
/**
* Use `ProgressViewIOS` to render a UIProgressView on iOS.
+27 -7
View File
@@ -12,13 +12,13 @@
'use strict';
var NativeModules = require('NativeModules');
var Platform = require('Platform');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var React = require('React');
var Subscribable = require('Subscribable');
var TextInputState = require('TextInputState');
var RCTUIManager = NativeModules.UIManager;
var RCTUIManagerDeprecated = NativeModules.UIManager;
var RCTScrollViewConsts = RCTUIManager.RCTScrollView.Constants;
var warning = require('warning');
@@ -351,7 +351,19 @@ var ScrollResponderMixin = {
* can also be used to quickly scroll to any element we want to focus
*/
scrollResponderScrollTo: function(offsetX: number, offsetY: number) {
RCTUIManagerDeprecated.scrollTo(React.findNodeHandle(this), offsetX, offsetY);
if (Platform.OS === 'android') {
RCTUIManager.dispatchViewManagerCommand(
React.findNodeHandle(this),
RCTUIManager.RCTScrollView.Commands.scrollTo,
[offsetX, offsetY],
);
} else {
RCTUIManager.scrollTo(
React.findNodeHandle(this),
offsetX,
offsetY
);
}
},
/**
@@ -359,7 +371,7 @@ var ScrollResponderMixin = {
* @param {object} rect Should have shape {x, y, width, height}
*/
scrollResponderZoomTo: function(rect: { x: number; y: number; width: number; height: number; }) {
RCTUIManagerDeprecated.zoomToRect(React.findNodeHandle(this), rect);
RCTUIManager.zoomToRect(React.findNodeHandle(this), rect);
},
/**
@@ -377,7 +389,7 @@ var ScrollResponderMixin = {
this.preventNegativeScrollOffset = !!preventNegativeScrollOffset;
RCTUIManager.measureLayout(
nodeHandle,
React.findNodeHandle(this),
React.findNodeHandle(this.getInnerViewNode()),
this.scrollResponderTextInputFocusError,
this.scrollResponderInputMeasureAndScrollToKeyboard
);
@@ -429,6 +441,10 @@ var ScrollResponderMixin = {
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillHide', this.scrollResponderKeyboardWillHide);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardDidShow', this.scrollResponderKeyboardDidShow);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardDidHide', this.scrollResponderKeyboardDidHide);
warning(this.getInnerViewNode, 'You need to implement getInnerViewNode in '
+ this.constructor.displayName + ' to get full'
+ 'functionality from ScrollResponder mixin. See example of ListView and'
+ ' ScrollView.');
},
/**
@@ -469,9 +485,13 @@ var ScrollResponderMixin = {
this.props.onKeyboardWillHide && this.props.onKeyboardWillHide(e);
},
scrollResponderKeyboardDidShow: function() {
this.keyboardWillOpenTo = null;
this.props.onKeyboardDidShow && this.props.onKeyboardDidShow();
scrollResponderKeyboardDidShow: function(e: Event) {
// TODO(7693961): The event for DidShow is not available on iOS yet.
// Use the one from WillShow and do not assign.
if (e) {
this.keyboardWillOpenTo = e;
}
this.props.onKeyboardDidShow && this.props.onKeyboardDidShow(e);
},
scrollResponderKeyboardDidHide: function() {
+98 -58
View File
@@ -17,7 +17,6 @@ var PointPropType = require('PointPropType');
var RCTScrollView = require('NativeModules').UIManager.RCTScrollView;
var RCTScrollViewConsts = RCTScrollView.Constants;
var React = require('React');
var ReactChildren = require('ReactChildren');
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var RCTUIManager = require('NativeModules').UIManager;
var ScrollResponder = require('ScrollResponder');
@@ -54,44 +53,54 @@ var INNERVIEW = 'InnerScrollView';
* Doesn't yet support other contained responders from blocking this scroll
* view from becoming the responder.
*/
var ScrollView = React.createClass({
propTypes: {
automaticallyAdjustContentInsets: PropTypes.bool, // true
contentInset: EdgeInsetsPropType, // zeros
contentOffset: PointPropType, // zeros
onScroll: PropTypes.func,
onScrollAnimationEnd: PropTypes.func,
scrollEnabled: PropTypes.bool, // true
scrollIndicatorInsets: EdgeInsetsPropType, // zeros
showsHorizontalScrollIndicator: PropTypes.bool,
showsVerticalScrollIndicator: PropTypes.bool,
style: StyleSheetPropType(ViewStylePropTypes),
scrollEventThrottle: PropTypes.number, // null
/**
* Controls whether iOS should automatically adjust the content inset
* for scroll views that are placed behind a navigation bar or
* tab bar/ toolbar. The default value is true.
* @platform ios
*/
automaticallyAdjustContentInsets: PropTypes.bool,
/**
* The amount by which the scroll view content is inset from the edges
* of the scroll view. Defaults to `{0, 0, 0, 0}`.
* @platform ios
*/
contentInset: EdgeInsetsPropType,
/**
* Used to manually set the starting scroll offset.
* The default value is `{x: 0, y: 0}`.
* @platform ios
*/
contentOffset: PointPropType,
/**
* When true, the scroll view bounces when it reaches the end of the
* content if the content is larger then the scroll view along the axis of
* the scroll direction. When false, it disables all bouncing even if
* the `alwaysBounce*` props are true. The default value is true.
* @platform ios
*/
bounces: PropTypes.bool,
/**
* When true, gestures can drive zoom past min/max and the zoom will animate
* to the min/max value at gesture end, otherwise the zoom will not exceed
* the limits.
* @platform ios
*/
bouncesZoom: PropTypes.bool,
/**
* When true, the scroll view bounces horizontally when it reaches the end
* even if the content is smaller than the scroll view itself. The default
* value is true when `horizontal={true}` and false otherwise.
* @platform ios
*/
alwaysBounceHorizontal: PropTypes.bool,
/**
* When true, the scroll view bounces vertically when it reaches the end
* even if the content is smaller than the scroll view itself. The default
* value is false when `horizontal={true}` and true otherwise.
* @platform ios
*/
alwaysBounceVertical: PropTypes.bool,
/**
@@ -99,6 +108,7 @@ var ScrollView = React.createClass({
* content is smaller than the scroll view bounds; when the content is
* larger than the scroll view, this property has no effect. The default
* value is false.
* @platform ios
*/
centerContent: PropTypes.bool,
/**
@@ -122,6 +132,7 @@ var ScrollView = React.createClass({
* decelerates after the user lifts their finger. Reasonable choices include
* - Normal: 0.998 (the default)
* - Fast: 0.9
* @platform ios
*/
decelerationRate: PropTypes.number,
/**
@@ -132,17 +143,19 @@ var ScrollView = React.createClass({
/**
* When true, the ScrollView will try to lock to only vertical or horizontal
* scrolling while dragging. The default value is false.
* @platform ios
*/
directionalLockEnabled: PropTypes.bool,
/**
* When false, once tracking starts, won't try to drag if the touch moves.
* The default value is true.
* @platform ios
*/
canCancelContentTouches: PropTypes.bool,
/**
* Determines whether the keyboard gets dismissed in response to a drag.
* - 'none' (the default), drags do not dismiss the keyboard.
* - 'onDrag', the keyboard is dismissed when a drag begins.
* - 'on-drag', the keyboard is dismissed when a drag begins.
* - 'interactive', the keyboard is dismissed interactively with the drag
* and moves in synchrony with the touch; dragging upwards cancels the
* dismissal.
@@ -157,35 +170,83 @@ var ScrollView = React.createClass({
* is up dismisses the keyboard. When true, the scroll view will not catch
* taps, and the keyboard will not dismiss automatically. The default value
* is false.
* @platform ios
*/
keyboardShouldPersistTaps: PropTypes.bool,
/**
* The maximum allowed zoom scale. The default value is 1.0.
* @platform ios
*/
maximumZoomScale: PropTypes.number,
/**
* The minimum allowed zoom scale. The default value is 1.0.
* @platform ios
*/
minimumZoomScale: PropTypes.number,
/**
* Fires at most once per frame during scrolling. The frequency of the
* events can be contolled using the `scrollEventThrottle` prop.
*/
onScroll: PropTypes.func,
/**
* Called when a scrolling animation ends.
* @platform ios
*/
onScrollAnimationEnd: PropTypes.func,
/**
* When true, the scroll view stops on multiples of the scroll view's size
* when scrolling. This can be used for horizontal pagination. The default
* value is false.
* @platform ios
*/
pagingEnabled: PropTypes.bool,
/**
* When false, the content does not scroll.
* The default value is true.
* @platform ios
*/
scrollEnabled: PropTypes.bool,
/**
* This controls how often the scroll event will be fired while scrolling
* (in events per seconds). A higher number yields better accuracy for code
* that is tracking the scroll position, but can lead to scroll performance
* problems due to the volume of information being send over the bridge.
* The default value is zero, which means the scroll event will be sent
* only once each time the view is scrolled.
* @platform ios
*/
scrollEventThrottle: PropTypes.number,
/**
* The amount by which the scroll view indicators are inset from the edges
* of the scroll view. This should normally be set to the same value as
* the `contentInset`. Defaults to `{0, 0, 0, 0}`.
* @platform ios
*/
scrollIndicatorInsets: EdgeInsetsPropType,
/**
* When true, the scroll view scrolls to top when the status bar is tapped.
* The default value is true.
* @platform ios
*/
scrollsToTop: PropTypes.bool,
/**
* When true, shows a horizontal scroll indicator.
*/
showsHorizontalScrollIndicator: PropTypes.bool,
/**
* When true, shows a vertical scroll indicator.
*/
showsVerticalScrollIndicator: PropTypes.bool,
/**
* An array of child indices determining which children get docked to the
* top of the screen when scrolling. For example, passing
* `stickyHeaderIndices={[0]}` will cause the first child to be fixed to the
* top of the scroll view. This property is not supported in conjunction
* with `horizontal={true}`.
* @platform ios
*/
stickyHeaderIndices: PropTypes.arrayOf(PropTypes.number),
style: StyleSheetPropType(ViewStylePropTypes),
/**
* Experimental: When true, offscreen child views (whose `overflow` value is
* `hidden`) are removed from their native backing superview when offscreen.
@@ -195,6 +256,7 @@ var ScrollView = React.createClass({
removeClippedSubviews: PropTypes.bool,
/**
* The current scale of the scroll view content. The default value is 1.0.
* @platform ios
*/
zoomScale: PropTypes.number,
},
@@ -224,19 +286,8 @@ var ScrollView = React.createClass({
},
scrollTo: function(destY?: number, destX?: number) {
if (Platform.OS === 'android') {
RCTUIManager.dispatchViewManagerCommand(
React.findNodeHandle(this),
RCTUIManager.RCTScrollView.Commands.scrollTo,
[destX || 0, destY || 0]
);
} else {
RCTUIManager.scrollTo(
React.findNodeHandle(this),
destX || 0,
destY || 0
);
}
// $FlowFixMe - Don't know how to pass Mixin correctly. Postpone for now
this.getScrollResponder().scrollResponderScrollTo(destX || 0, destY || 0);
},
scrollWithoutAnimationTo: function(destY?: number, destX?: number) {
@@ -247,6 +298,21 @@ var ScrollView = React.createClass({
);
},
handleScroll: function(e: Event) {
if (__DEV__) {
if (this.props.onScroll && !this.props.scrollEventThrottle) {
console.log(
'You specified `onScroll` on a <ScrollView> but not ' +
'`scrollEventThrottle`. You will only receive one event. ' +
'Using `16` you get all the events but be aware that it may ' +
'cause frame drops, use a bigger number if you don\'t need as ' +
'much precision.'
);
}
}
this.scrollResponderHandleScroll(e);
},
render: function() {
var contentContainerStyle = [
this.props.horizontal && styles.contentContainerHorizontal,
@@ -262,40 +328,14 @@ var ScrollView = React.createClass({
') must by applied through the contentContainerStyle prop.'
);
}
if (__DEV__) {
if (this.props.onScroll && !this.props.scrollEventThrottle) {
var onScroll = this.props.onScroll;
this.props.onScroll = function() {
console.log(
'You specified `onScroll` on a <ScrollView> but not ' +
'`scrollEventThrottle`. You will only receive one event. ' +
'Using `16` you get all the events but be aware that it may ' +
'cause frame drops, use a bigger number if you don\'t need as ' +
'much precision.'
);
onScroll.apply(this, arguments);
};
}
}
var children = this.props.children;
if (this.props.stickyHeaderIndices) {
children = ReactChildren.map(children, (child) => {
if (child) {
return <View collapsible={false}>{child}</View>;
} else {
return child;
}
});
}
var contentContainer =
<View
collapsible={false}
ref={INNERVIEW}
style={contentContainerStyle}
removeClippedSubviews={this.props.removeClippedSubviews}>
{children}
removeClippedSubviews={this.props.removeClippedSubviews}
collapsable={false}>
{this.props.children}
</View>;
var alwaysBounceHorizontal =
@@ -323,7 +363,7 @@ var ScrollView = React.createClass({
onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
onScroll: this.scrollResponderHandleScroll,
onScroll: this.handleScroll,
onResponderGrant: this.scrollResponderHandleResponderGrant,
onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest,
onResponderTerminate: this.scrollResponderHandleTerminate,
@@ -18,7 +18,6 @@ var React = require('React');
var StyleSheet = require('StyleSheet');
var requireNativeComponent = require('requireNativeComponent');
var verifyPropTypes = require('verifyPropTypes');
type DefaultProps = {
values: Array<string>;
@@ -35,6 +35,10 @@ var StatusBarIOS = {
animation = animation || 'none';
RCTStatusBarManager.setHidden(hidden, animation);
},
setNetworkActivityIndicatorVisible(visible: boolean) {
RCTStatusBarManager.setNetworkActivityIndicatorVisible(visible);
},
};
module.exports = StatusBarIOS;
@@ -32,7 +32,11 @@ var TabBarIOS = React.createClass({
/**
* Background color of the tab bar
*/
barTintColor: React.PropTypes.string
barTintColor: React.PropTypes.string,
/**
* A Boolean value that indicates whether the tab bar is translucent
*/
translucent: React.PropTypes.bool,
},
render: function() {
@@ -40,7 +44,8 @@ var TabBarIOS = React.createClass({
<RCTTabBar
style={[styles.tabGroup, this.props.style]}
tintColor={this.props.tintColor}
barTintColor={this.props.barTintColor}>
barTintColor={this.props.barTintColor}
translucent={this.props.translucent !== false}>
{this.props.children}
</RCTTabBar>
);
+108 -116
View File
@@ -31,8 +31,8 @@ var invariant = require('invariant');
var requireNativeComponent = require('requireNativeComponent');
var onlyMultiline = {
onSelectionChange: true,
onTextInput: true,
onSelectionChange: true, // not supported in Open Source yet
onTextInput: true, // not supported in Open Source yet
children: true,
};
@@ -47,6 +47,7 @@ var AndroidTextInputAttributes = {
textAlign: true,
textAlignVertical: true,
keyboardType: true,
mostRecentEventCount: true,
multiline: true,
password: true,
placeholder: true,
@@ -54,6 +55,7 @@ var AndroidTextInputAttributes = {
text: true,
testID: true,
underlineColorAndroid: true,
editable : true,
};
var viewConfigAndroid = {
@@ -64,10 +66,6 @@ var viewConfigAndroid = {
var RCTTextView = requireNativeComponent('RCTTextView', null);
var RCTTextField = requireNativeComponent('RCTTextField', null);
type DefaultProps = {
bufferDelay: number;
};
type Event = Object;
/**
@@ -77,32 +75,30 @@ type Event = Object;
* types, such as a numeric keypad.
*
* The simplest use case is to plop down a `TextInput` and subscribe to the
* `onChangeText` events to read the user input. There are also other events, such
* as `onSubmitEditing` and `onFocus` that can be subscribed to. A simple
* `onChangeText` events to read the user input. There are also other events,
* such as `onSubmitEditing` and `onFocus` that can be subscribed to. A simple
* example:
*
* ```
* <View>
* <TextInput
* style={{height: 40, borderColor: 'gray', borderWidth: 1}}
* onChangeText={(text) => this.setState({input: text})}
* onChangeText={(text) => this.setState({text})}
* value={this.state.text}
* />
* <Text>{'user input: ' + this.state.input}</Text>
* </View>
* ```
*
* The `value` prop can be used to set the value of the input in order to make
* the state of the component clear, but <TextInput> does not behave as a true
* controlled component by default because all operations are asynchronous.
* Setting `value` once is like setting the default value, but you can change it
* continuously based on `onChangeText` events as well. If you really want to
* force the component to always revert to the value you are setting, you can
* set `controlled={true}`.
* Note that some props are only available with multiline={true/false}:
*
* The `multiline` prop is not supported in all releases, and some props are
* multiline only.
* var onlyMultiline = {
* onSelectionChange: true, // not supported in Open Source yet
* onTextInput: true, // not supported in Open Source yet
* children: true,
* };
*
* var notMultiline = {
* onSubmitEditing: true,
* };
*/
var TextInput = React.createClass({
propTypes: {
/**
@@ -120,32 +116,43 @@ var TextInput = React.createClass({
'characters',
]),
/**
* If false, disables auto-correct. Default value is true.
* If false, disables auto-correct. The default value is true.
*/
autoCorrect: PropTypes.bool,
/**
* If true, focuses the input on componentDidMount. Default value is false.
* If true, focuses the input on componentDidMount.
* The default value is false.
*/
autoFocus: PropTypes.bool,
/**
* Set the position of the cursor from where editing will begin.
* @platorm android
*/
textAlign: PropTypes.oneOf([
'start',
'center',
'end',
]),
/**
* Aligns text vertically within the TextInput.
* @platform android
*/
textAlignVertical: PropTypes.oneOf([
'top',
'center',
'bottom',
]),
/**
* If false, text is not editable. Default value is true.
* If false, text is not editable. The default value is true.
*/
editable: PropTypes.bool,
/**
* Determines which keyboard to open, e.g.`numeric`.
*
* The following values work across platforms:
* - default
* - numeric
* - email-address
*/
keyboardType: PropTypes.oneOf([
// Cross-platform
@@ -165,6 +172,7 @@ var TextInput = React.createClass({
]),
/**
* Determines how the return key should look.
* @platform ios
*/
returnKeyType: PropTypes.oneOf([
'default',
@@ -179,13 +187,21 @@ var TextInput = React.createClass({
'done',
'emergency-call',
]),
/**
* Limits the maximum number of characters that can be entered. Use this
* instead of implementing the logic in JS to avoid flicker.
* @platform ios
*/
maxLength: PropTypes.number,
/**
* If true, the keyboard disables the return key when there is no text and
* automatically enables it when there is text. Default value is false.
* automatically enables it when there is text. The default value is false.
* @platform ios
*/
enablesReturnKeyAutomatically: PropTypes.bool,
/**
* If true, the text input can be multiple lines. Default value is false.
* If true, the text input can be multiple lines.
* The default value is false.
*/
multiline: PropTypes.bool,
/**
@@ -214,14 +230,9 @@ var TextInput = React.createClass({
*/
onSubmitEditing: PropTypes.func,
/**
* Invoked on mount and layout changes with {x, y, width, height}.
* Invoked on mount and layout changes with `{x, y, width, height}`.
*/
onLayout: PropTypes.func,
/**
* If true, the text input obscures the text entered so that sensitive text
* like passwords stay secure. Default value is false.
*/
password: PropTypes.bool,
/**
* The string that will be rendered before text input has been entered
*/
@@ -230,30 +241,36 @@ var TextInput = React.createClass({
* The text color of the placeholder string
*/
placeholderTextColor: PropTypes.string,
/**
* If true, the text input obscures the text entered so that sensitive text
* like passwords stay secure. The default value is false.
*/
secureTextEntry: PropTypes.bool,
/**
* See DocumentSelectionState.js, some state that is responsible for
* maintaining selection information for a document
* @platform ios
*/
selectionState: PropTypes.instanceOf(DocumentSelectionState),
/**
* The default value for the text input
* The value to show for the text input. TextInput is a controlled
* component, which means the native value will be forced to match this
* value prop if provided. For most uses this works great, but in some
* cases this may cause flickering - one common cause is preventing edits
* by keeping value the same. In addition to simply setting the same value,
* either set `editable={false}`, or set/update `maxLength` to prevent
* unwanted edits without flicker.
*/
value: PropTypes.string,
/**
* This helps avoid drops characters due to race conditions between JS and
* the native text input. The default should be fine, but if you're
* potentially doing very slow operations on every keystroke then you may
* want to try increasing this.
* Provides an initial value that will change when the user starts typing.
* Useful for simple use-cases where you don't want to deal with listening
* to events and updating the value prop to keep the controlled state in sync.
*/
bufferDelay: PropTypes.number,
/**
* If you really want this to behave as a controlled component, you can set
* this true, but you will probably see flickering, dropped keystrokes,
* and/or laggy typing, depending on how you process onChange events.
*/
controlled: PropTypes.bool,
defaultValue: PropTypes.string,
/**
* When the clear button should appear on the right side of the text view
* @platform ios
*/
clearButtonMode: PropTypes.oneOf([
'never',
@@ -263,10 +280,12 @@ var TextInput = React.createClass({
]),
/**
* If true, clears the text field automatically when editing begins
* @platform ios
*/
clearTextOnFocus: PropTypes.bool,
/**
* If true, selected the text automatically when editing begins
* If true, all text will automatically be selected on focus
* @platform ios
*/
selectTextOnFocus: PropTypes.bool,
/**
@@ -274,11 +293,12 @@ var TextInput = React.createClass({
*/
style: Text.propTypes.style,
/**
* Used to locate this view in end-to-end tests.
* Used to locate this view in end-to-end tests
*/
testID: PropTypes.string,
/**
* The color of the textInput underline. Is only supported on Android.
* The color of the textInput underline.
* @platform android
*/
underlineColorAndroid: PropTypes.string,
},
@@ -297,16 +317,9 @@ var TextInput = React.createClass({
React.findNodeHandle(this.refs.input);
},
getDefaultProps: function(): DefaultProps {
return {
bufferDelay: 100,
};
},
getInitialState: function() {
return {
mostRecentEventCounter: 0,
bufferedValue: this.props.value,
mostRecentEventCount: 0,
};
},
@@ -346,52 +359,6 @@ var TextInput = React.createClass({
}
},
_bufferTimeout: (undefined: ?number),
componentWillReceiveProps: function(newProps: {value: any}) {
if (newProps.value !== this.props.value) {
if (!this.isFocused()) {
// Set the value immediately if the input is not focused since that
// means there is no risk of the user typing immediately.
this.setState({bufferedValue: newProps.value});
} else {
// The following clear and setTimeout buffers the value such that if more
// characters are typed in quick succession, generating new values, the
// out of date values will get cancelled before they are ever sent to
// native.
//
// If we don't do this, it's likely the out of date values will blow
// away recently typed characters in the native input that JS was not
// yet aware of (since it is informed asynchronously), then the next
// character will be appended to the older value, dropping the
// characters in between. Here is a potential sequence of events
// (recall we have multiple independently serial, interleaved queues):
//
// 1) User types 'R' => send 'R' to JS queue.
// 2) User types 'e' => send 'Re' to JS queue.
// 3) JS processes 'R' and sends 'R' back to native.
// 4) Native recieves 'R' and changes input from 'Re' back to 'R'.
// 5) User types 'a' => send 'Ra' to JS queue.
// 6) JS processes 'Re' and sends 'Re' back to native.
// 7) Native recieves 'Re' and changes input from 'R' back to 'Re'.
// 8) JS processes 'Ra' and sends 'Ra' back to native.
// 9) Native recieves final 'Ra' from JS - 'e' has been dropped!
//
// This isn't 100% foolproop (e.g. if it takes longer than
// `props.bufferDelay` ms to process one keystroke), and there are of
// course other potential algorithms to deal with this, but this is a
// simple solution that seems to reduce the chance of dropped characters
// drastically without compromising native input responsiveness (e.g. by
// introducing delay from a synchronization protocol).
this.clearTimeout(this._bufferTimeout);
this._bufferTimeout = this.setTimeout(
() => this.setState({bufferedValue: newProps.value}),
this.props.bufferDelay
);
}
}
},
getChildContext: function(): Object {
return {isInAParentText: true};
},
@@ -400,6 +367,10 @@ var TextInput = React.createClass({
isInAParentText: React.PropTypes.bool
},
clear: function() {
this.setNativeProps({text: ''});
},
render: function() {
if (Platform.OS === 'ios') {
return this._renderIOS();
@@ -408,12 +379,17 @@ var TextInput = React.createClass({
}
},
_getText: function(): ?string {
return typeof this.props.value === 'string' ?
this.props.value :
this.props.defaultValue;
},
_renderIOS: function() {
var textContainer;
var props = this.props;
var props = Object.assign({}, this.props);
props.style = [styles.input, this.props.style];
if (!props.multiline) {
for (var propKey in onlyMultiline) {
if (props[propKey]) {
@@ -430,7 +406,8 @@ var TextInput = React.createClass({
onBlur={this._onBlur}
onChange={this._onChange}
onSelectionChangeShouldSetResponder={() => true}
text={this.state.bufferedValue}
text={this._getText()}
mostRecentEventCount={this.state.mostRecentEventCount}
/>;
} else {
for (var propKey in notMultiline) {
@@ -459,14 +436,14 @@ var TextInput = React.createClass({
ref="input"
{...props}
children={children}
mostRecentEventCounter={this.state.mostRecentEventCounter}
mostRecentEventCount={this.state.mostRecentEventCount}
onFocus={this._onFocus}
onBlur={this._onBlur}
onChange={this._onChange}
onSelectionChange={this._onSelectionChange}
onTextInput={this._onTextInput}
onSelectionChangeShouldSetResponder={emptyFunction.thatReturnsTrue}
text={this.state.bufferedValue}
text={this._getText()}
/>;
}
@@ -505,6 +482,7 @@ var TextInput = React.createClass({
textAlign={textAlign}
textAlignVertical={textAlignVertical}
keyboardType={this.props.keyboardType}
mostRecentEventCount={this.state.mostRecentEventCount}
multiline={this.props.multiline}
onFocus={this._onFocus}
onBlur={this._onBlur}
@@ -516,9 +494,10 @@ var TextInput = React.createClass({
password={this.props.password || this.props.secureTextEntry}
placeholder={this.props.placeholder}
placeholderTextColor={this.props.placeholderTextColor}
text={this.state.bufferedValue}
text={this._getText()}
underlineColorAndroid={this.props.underlineColorAndroid}
children={children}
editable={this.props.editable}
/>;
return (
@@ -537,15 +516,32 @@ var TextInput = React.createClass({
},
_onPress: function(event: Event) {
this.focus();
if (this.props.editable || this.props.editable === undefined) {
this.focus();
}
},
_onChange: function(event: Event) {
if (this.props.controlled && event.nativeEvent.text !== this.props.value) {
this.refs.input.setNativeProps({text: this.props.value});
if (Platform.OS === 'android') {
// Android expects the event count to be updated as soon as possible.
this.refs.input.setNativeProps({
mostRecentEventCount: event.nativeEvent.eventCount,
});
}
var text = event.nativeEvent.text;
var eventCount = event.nativeEvent.eventCount;
this.props.onChange && this.props.onChange(event);
this.props.onChangeText && this.props.onChangeText(event.nativeEvent.text);
this.props.onChangeText && this.props.onChangeText(text);
this.setState({mostRecentEventCount: eventCount}, () => {
// This is a controlled component, so make sure to force the native value
// to match. Most usage shouldn't need this, but if it does this will be
// more correct but might flicker a bit and/or cause the cursor to jump.
if (text !== this.props.value && typeof this.props.value === 'string') {
this.refs.input.setNativeProps({
text: this.props.value,
});
}
});
},
_onBlur: function(event: Event) {
@@ -565,10 +561,6 @@ var TextInput = React.createClass({
_onTextInput: function(event: Event) {
this.props.onTextInput && this.props.onTextInput(event);
var counter = event.nativeEvent.eventCounter;
if (counter > this.state.mostRecentEventCounter) {
this.setState({mostRecentEventCounter: counter});
}
},
});
@@ -15,6 +15,7 @@
*/
'use strict';
var Platform = require('Platform');
var RCTUIManager = require('NativeModules').UIManager;
var TextInputState = {
@@ -39,7 +40,15 @@ var TextInputState = {
focusTextInput: function(textFieldID: ?number) {
if (this._currentlyFocusedID !== textFieldID && textFieldID !== null) {
this._currentlyFocusedID = textFieldID;
RCTUIManager.focus(textFieldID);
if (Platform.OS === 'ios') {
RCTUIManager.focus(textFieldID);
} else if (Platform.OS === 'android') {
RCTUIManager.dispatchViewManagerCommand(
textFieldID,
RCTUIManager.AndroidTextInput.Commands.focusTextInput,
null
);
}
}
},
@@ -51,7 +60,15 @@ var TextInputState = {
blurTextInput: function(textFieldID: ?number) {
if (this._currentlyFocusedID === textFieldID && textFieldID !== null) {
this._currentlyFocusedID = null;
RCTUIManager.blur(textFieldID);
if (Platform.OS === 'ios') {
RCTUIManager.blur(textFieldID);
} else if (Platform.OS === 'android') {
RCTUIManager.dispatchViewManagerCommand(
textFieldID,
RCTUIManager.AndroidTextInput.Commands.blurTextInput,
null
);
}
}
}
};
@@ -11,20 +11,12 @@
*/
'use strict';
var AnimationExperimental = require('AnimationExperimental');
var Animated = require('Animated');
var NativeMethodsMixin = require('NativeMethodsMixin');
var POPAnimation = require('POPAnimation');
var React = require('React');
var Touchable = require('Touchable');
var merge = require('merge');
var onlyChild = require('onlyChild');
var invariant = require('invariant');
invariant(
AnimationExperimental || POPAnimation,
'Please add the RCTAnimationExperimental framework to your project, or add //Libraries/FBReactKit:RCTPOPAnimation to your BUCK file if running internally within Facebook.'
);
type State = {
animationID: ?number;
@@ -58,40 +50,23 @@ var TouchableBounce = React.createClass({
},
getInitialState: function(): State {
return merge(this.touchableGetInitialState(), {animationID: null});
return {
...this.touchableGetInitialState(),
scale: new Animated.Value(1),
};
},
bounceTo: function(
value: number,
velocity: number,
bounciness: number,
fromValue?: ?number,
callback?: ?Function
) {
if (POPAnimation) {
this.state.animationID && this.removeAnimation(this.state.animationID);
var anim = {
property: POPAnimation.Properties.scaleXY,
dynamicsTension: 0,
toValue: [value, value],
velocity: [velocity, velocity],
springBounciness: bounciness,
fromValue: fromValue ? [fromValue, fromValue] : undefined,
};
this.state.animationID = POPAnimation.createSpringAnimation(anim);
this.addAnimation(this.state.animationID, callback);
} else {
AnimationExperimental.startAnimation(
{
node: this,
duration: 300,
easing: 'easeOutBack',
property: 'scaleXY',
toValue: { x: value, y: value},
},
callback
);
}
Animated.spring(this.state.scale, {
toValue: value,
velocity,
bounciness,
}).start(callback);
},
/**
@@ -109,13 +84,14 @@ var TouchableBounce = React.createClass({
touchableHandlePress: function() {
var onPressWithCompletion = this.props.onPressWithCompletion;
if (onPressWithCompletion) {
onPressWithCompletion(
this.bounceTo.bind(this, 1, 10, 10, 0.93, this.props.onPressAnimationComplete)
);
onPressWithCompletion(() => {
this.state.scale.setValue(0.93);
this.bounceTo(1, 10, 10, this.props.onPressAnimationComplete);
});
return;
}
this.bounceTo(1, 10, 10, undefined, this.props.onPressAnimationComplete);
this.bounceTo(1, 10, 10, this.props.onPressAnimationComplete);
this.props.onPress && this.props.onPress();
},
@@ -127,18 +103,21 @@ var TouchableBounce = React.createClass({
return 0;
},
render: function() {
var child = onlyChild(this.props.children);
return React.cloneElement(child, {
accessible: true,
testID: this.props.testID,
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
onResponderGrant: this.touchableHandleResponderGrant,
onResponderMove: this.touchableHandleResponderMove,
onResponderRelease: this.touchableHandleResponderRelease,
onResponderTerminate: this.touchableHandleResponderTerminate
});
render: function(): ReactElement {
return (
<Animated.View
style={[{transform: [{scale: this.state.scale}]}, this.props.style]}
accessible={true}
testID={this.props.testID}
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
onResponderGrant={this.touchableHandleResponderGrant}
onResponderMove={this.touchableHandleResponderMove}
onResponderRelease={this.touchableHandleResponderRelease}
onResponderTerminate={this.touchableHandleResponderTerminate}>
{this.props.children}
</Animated.View>
);
}
});
@@ -181,6 +181,10 @@ var TouchableHighlight = React.createClass({
},
_showUnderlay: function() {
if (!this.isMounted()) {
return;
}
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
this.props.onShowUnderlay && this.props.onShowUnderlay();
@@ -202,6 +206,7 @@ var TouchableHighlight = React.createClass({
render: function() {
return (
<View
accessible={true}
ref={UNDERLAY_REF}
style={this.state.underlayStyle}
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
@@ -214,7 +219,6 @@ var TouchableHighlight = React.createClass({
onlyChild(this.props.children),
{
ref: CHILD_REF,
accessible: true,
testID: this.props.testID,
}
)}
@@ -12,19 +12,16 @@
// Note (avik): add @flow when Flow supports spread properties in propTypes
var Animated = require('Animated');
var NativeMethodsMixin = require('NativeMethodsMixin');
var POPAnimationMixin = require('POPAnimationMixin');
var React = require('React');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('Touchable');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
var cloneWithProps = require('cloneWithProps');
var ensureComponentIsNative = require('ensureComponentIsNative');
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
var flattenStyle = require('flattenStyle');
var keyOf = require('keyOf');
var onlyChild = require('onlyChild');
/**
* A wrapper for making views respond properly to touches.
@@ -52,7 +49,7 @@ var onlyChild = require('onlyChild');
*/
var TouchableOpacity = React.createClass({
mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],
mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin],
propTypes: {
...TouchableWithoutFeedback.propTypes,
@@ -70,16 +67,17 @@ var TouchableOpacity = React.createClass({
},
getInitialState: function() {
return this.touchableGetInitialState();
return {
...this.touchableGetInitialState(),
anim: new Animated.Value(1),
};
},
componentDidMount: function() {
ensurePositiveDelayProps(this.props);
ensureComponentIsNative(this.refs[CHILD_REF]);
},
componentDidUpdate: function() {
ensureComponentIsNative(this.refs[CHILD_REF]);
},
componentWillReceiveProps: function(nextProps) {
@@ -87,22 +85,10 @@ var TouchableOpacity = React.createClass({
},
setOpacityTo: function(value) {
if (POPAnimationMixin) {
// Reset with animation if POP is available
this.stopAllAnimations();
var anim = {
type: this.AnimationTypes.linear,
property: this.AnimationProperties.opacity,
duration: 0.15,
toValue: value,
};
this.startAnimation(CHILD_REF, anim);
} else {
// Reset immediately if POP is unavailable
this.refs[CHILD_REF].setNativeProps({
opacity: value
});
}
Animated.timing(
this.state.anim,
{toValue: value, duration: 150}
).start();
},
/**
@@ -161,25 +147,27 @@ var TouchableOpacity = React.createClass({
_opacityInactive: function() {
this.clearTimeout(this._hideTimeout);
this._hideTimeout = null;
var child = onlyChild(this.props.children);
var childStyle = flattenStyle(child.props.style) || {};
var childStyle = flattenStyle(this.props.style) || {};
this.setOpacityTo(
childStyle.opacity === undefined ? 1 : childStyle.opacity
);
},
render: function() {
return cloneWithProps(onlyChild(this.props.children), {
ref: CHILD_REF,
accessible: true,
testID: this.props.testID,
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
onResponderGrant: this.touchableHandleResponderGrant,
onResponderMove: this.touchableHandleResponderMove,
onResponderRelease: this.touchableHandleResponderRelease,
onResponderTerminate: this.touchableHandleResponderTerminate,
});
return (
<Animated.View
accessible={true}
style={[this.props.style, {opacity: this.state.anim}]}
testID={this.props.testID}
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
onResponderGrant={this.touchableHandleResponderGrant}
onResponderMove={this.touchableHandleResponderMove}
onResponderRelease={this.touchableHandleResponderRelease}
onResponderTerminate={this.touchableHandleResponderTerminate}>
{this.props.children}
</Animated.View>
);
},
});
@@ -191,6 +179,5 @@ var TouchableOpacity = React.createClass({
*/
var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
var CHILD_REF = keyOf({childRef: null});
module.exports = TouchableOpacity;
+51 -7
View File
@@ -44,6 +44,13 @@ var AccessibilityTraits = [
'pageTurn',
];
// <<<<< WARNING >>>>>
// If adding any properties to View that could change the way layout-only status
// works on iOS, make sure to update ReactNativeViewAttributes.js and
// RCTShadowView.m (in the -[RCTShadowView isLayoutOnly] method).
// <<<<< WARNING >>>>>
/**
* The most fundamental component for building UI, `View` is a
* container that supports layout with flexbox, style, some touch handling, and
@@ -77,12 +84,6 @@ var View = React.createClass({
},
propTypes: {
/**
* When false, indicates that the view should not be collapsed, even if it is
* layout-only. Defaults to true.
*/
collapsible: PropTypes.bool,
/**
* When true, indicates that the view is an accessibility element. By default,
* all the touchable elements are accessible.
@@ -96,6 +97,27 @@ var View = React.createClass({
*/
accessibilityLabel: PropTypes.string,
/**
* Indicates to accessibility services to treat UI component like a
* native one. Works for Android only.
*/
accessibilityComponentType: PropTypes.oneOf([
'none',
'button',
]),
/**
* Indicates to accessibility services whether the user should be notified
* when this view changes. Works for Android API >= 19 only.
* See http://developer.android.com/reference/android/view/View.html#attr_android:accessibilityLiveRegion
* for references.
*/
accessibilityLiveRegion: PropTypes.oneOf([
'none',
'polite',
'assertive',
]),
/**
* Provides additional traits to screen reader. By default no traits are
* provided unless specified otherwise in element
@@ -118,7 +140,8 @@ var View = React.createClass({
onMagicTap: PropTypes.func,
/**
* Used to locate this view in end-to-end tests.
* Used to locate this view in end-to-end tests. NB: disables the 'layout-only
* view removal' optimization for this view!
*/
testID: PropTypes.string,
@@ -203,8 +226,29 @@ var View = React.createClass({
* different parameters. The downside is that this can use up limited video
* memory, so this prop should be set back to false at the end of the
* interaction/animation.
* @platform android
*/
renderToHardwareTextureAndroid: PropTypes.bool,
/**
* Whether this view should be rendered as a bitmap before compositing.
*
* On iOS, this is useful for animations and interactions that do not
* modify this component's dimensions nor its children; for example, when
* translating the position of a static view, rasterization allows the
* renderer to reuse a cached bitmap of a static view and quickly composite
* it during each frame.
*
* Rasterization incurs an off-screen drawing pass and the bitmap consumes
* memory. Test and measure when using this property.
* @platform ios
*/
shouldRasterizeIOS: PropTypes.bool,
/**
* @platform android
*/
collapsable: PropTypes.bool,
},
render: function() {
@@ -21,6 +21,7 @@ var TransformPropTypes = require('TransformPropTypes');
var ViewStylePropTypes = {
...LayoutPropTypes,
...TransformPropTypes,
backfaceVisibility: ReactPropTypes.oneOf(['visible', 'hidden']),
backgroundColor: ReactPropTypes.string,
borderColor: ReactPropTypes.string,
borderTopColor: ReactPropTypes.string,

Some files were not shown because too many files have changed in this diff Show More