Compare commits

...
227 Commits
Author SHA1 Message Date
Mike Grabowski a3268f7dd1 [0.27.2] Bump version numbers 2016-06-09 01:52:24 +02:00
Mike Grabowski 8abca24268 Add Keyboard to exported APIs 2016-06-09 01:43:54 +02:00
Martin Konicek 8074b09e7d [0.27.1] Bump version numbers 2016-06-07 00:58:08 +01:00
Andrej Badin a6b8110528 Fix scrolling issue on iOS devices.
Summary:
It was not possible to scroll due to overlaying (and visually not present) docs navigation layer.

Fixes issue introduced in PR #7669, see this [comment](https://github.com/facebook/react-native/pull/7669#issuecomment-220784882)
Additionally, script makes sure only single target is "in" and closes any other target with "in" state.
Closes https://github.com/facebook/react-native/pull/7731

Differential Revision: D3344927

Pulled By: vjeux

fbshipit-source-id: b2c64d90a2a6e531a9be79b936d6a5da61a69b22
2016-06-07 00:54:16 +01:00
Ben Alpert 877df8cb1a Change survey image
Reviewed By: vjeux

Differential Revision: D3362968

fbshipit-source-id: f6de18a5f27e32e6bb64a65bc233d741cb276f41
2016-06-07 00:54:11 +01:00
Andrej Badin fe189c18aa Improve Navigation panel column layout for landscape/portrait
Summary:
Improve column layout for _phone vs. tablet_ & _portrait vs landscape_ variations.
This change only affects handheld devices, or devices with really small (physical) screen dimensions.

See previews bellow:

Phone - landscape
![phone-landscape](https://cloud.githubusercontent.com/assets/829963/15448311/53863428-1f5e-11e6-9e05-d98587b9752f.png)

Phone - portrait
![phone-portrait](https://cloud.githubusercontent.com/assets/829963/15448309/5385eb9e-1f5e-11e6-8d12-6bda7e1d2e7f.png)

Tablet - landscape
![tablet-landscape](https://cloud.githubusercontent.com/assets/829963/15448312/538719a6-1f5e-11e6-9a2c-52d0c9109f09.png)

Tablet - portrait
![tablet-portrait](https://cloud.githubusercontent.com/assets/829963/15448310/53862636-1f5e-11e6-87ff-727568d7dd1f.png)
Closes https://github.com/facebook/react-native/pull/7669

Differential Revision: D3332676

Pulled By: vjeux

fbshipit-source-id: 8cfc1903e35fd62c82fcd8dd1e05a1e2062c555d
2016-06-07 00:54:02 +01:00
Mike Grabowski 37cc5cfb0c [0.27.0] Bump version numbers 2016-06-06 10:21:36 +02:00
Eloy Durán 41fa6e7f45 Add C++ and standard v14 settings.
Summary:
This PR adds the C++ stdlib to the linker flags and sets the C++ standard that’s used to v14.

I have tested this with my app, without it any CP build would fail unless users add those flags to the generated projects themselves.

/cc grabbou
Closes https://github.com/facebook/react-native/pull/7800

Differential Revision: D3360421

fbshipit-source-id: 0a80030dd255f073a201acc6e1c846be114c2c2a
2016-06-06 10:19:25 +02:00
Konstantin Raev 8b734173d3 [0.27.0-rc3] Bump version numbers 2016-06-05 19:47:08 +01:00
Konstantin Raev 252102588d [0.27.0-rc.3] Bump version numbers 2016-06-05 17:19:23 +01:00
Martin Konicek 76003f3720 [0.27.0-rc2] Bump version numbers 2016-05-27 16:51:49 +01:00
Eric Rozell 3cabf4f98f Update node-haste dependency to 2.12.0
Summary:
Update to node-haste 2.12.0 to support pass through configuration of supported platforms.
Closes https://github.com/facebook/react-native/pull/7660

Differential Revision: D3335034

Pulled By: mkonicek

fbshipit-source-id: d238b90a90d51654301d61251ceb26d183fef57a
2016-05-27 16:42:19 +01:00
Mike Grabowski bbccdd5b84 [0.27.0-rc1] Bump version numbers 2016-05-25 02:26:29 +02:00
David Aurelio cacf6b1267 Respect original enumerability/writability when polyfilling globals
Summary:
Reuses the original property descriptor when overwriting / polyfilling globals to ensure enumerability and writability are the same
Closes https://github.com/facebook/react-native/pull/7704

Differential Revision: D3338119

Pulled By: davidaurelio

fbshipit-source-id: ab456324a3346cd3ec8b2c3e3a2378408c92087c
2016-05-25 01:55:58 +02:00
Konstantin Raev 934acd809d Made npm deployment to be independent of git tags cache
Summary:
Fixes npm deployment script as in https://circleci.com/gh/facebook/react-native/6745.

Example situation:
- admin is ready to release 0.26.0
- admin adds tag v0.26.0 and pushes to origin
- Circle CI build fails because of code error
- admin cherry-picks a fix and tags v0.26.0 again and pushes to origin
- Circle does not update local tag v0.26.0 because it already has it cached, compiles the code but fails to deploy to npm because v0.26.0 is not on branch HEAD from Circle point of view

The previous version of the script was checking the tags on current commit as well but it used the local branch tags.
This change fetches tags from `origin` fresh and allows us to redeploy the same version multiple times.
Closes https://github.com/facebook/react-native/pull/7619

Differential Revision: D3334469

fbshipit-source-id: b423fc19516dc4f5f7f2605224e62b8a378c8a7d
2016-05-25 01:53:59 +02:00
Marc Horowitz 24a806a9f1 Fix chrome debugging on android
Summary:
Java loadApplicationScript changed, but the C++ code in the
debug ProxyExecutor which called it did not.  This fixes the fbjni
method lookup.

fixes #7659

Reviewed By: AaaChiuuu

Differential Revision: D3331472

fbshipit-source-id: 33312dccc3c7687f51742e42f9e0397f9c925e76
2016-05-25 01:53:29 +02:00
James Ide 7d1b4d681a [React] Use React 15.1.0 and stop using the alpha 2016-05-23 11:16:31 -07:00
Mike Grabowski 0f9e31cce8 [0.27.0-rc] Bump version numbers 2016-05-20 20:23:00 +02:00
Hugo Agbonon bc634ea350 More helpful error message when react-native start is forgotten
Summary:
Developing with react-native on Linux, I found myself facing this message:

![chpao3jwuaehr_p jpg large](https://cloud.githubusercontent.com/assets/1598317/15032665/ae90ee88-1263-11e6-9acd-3fe261c08c28.jpeg)

The problem is actually quite simple: I hadn't used `react-native start` before starting `react-native run-android`, which caused this error, both on an emulator and a real Android device. As the message is currently unhelpful, but can be shown because of a simple mistake, I updated it.

~~Additionally, I clarified the fact that `react-native start` is still necessary on Linux, updating a title on the Linux and Windows Support documentation page.~~
Closes https://github.com/facebook/react-native/pull/7396

Differential Revision: D3305078

fbshipit-source-id: 2d87e02ff2ad15d8239fbcc0ada4a4e67b982e94
2016-05-20 09:13:27 -07:00
dmueller39 0aea74ebea Fixed incorrect rows reported in onChangeVisibleRows
Summary:
There is a bug in onChangeVisibleRows when the renderSeparator is not null. The _updateVisibleRows function does not account for the presence of separator frames in the  _childFrames array.

When renderSeparator is not null, increment the totalIndex an additional
time for each row that isn't the last in its section, or the last in the entire
list.

This continues a slightly brittle precedent of having a set of parallel conditions in render and _updateVisibleRows. (i.e. renderSectionHeader is used in both functions, in render as a condition to create a sectionHeader, and in _updateVisibleRows as a condition to increment the totalIndex.

Before change:
![yeeqnmvmif](https://cloud.githubusercontent.com/assets/9422359/14515342/38543952-01c7-11e6-984c-7c1a3fc3d820.gif)

After change:
![gzbrljclzm](https://cloud.githubusercontent.com/assets/9422359/14515340/3296e294-01c7-11e6-8ae9-1ad313600956.gif)

Built using https://gist.github.com/dmueller39/f95028f6fe8bd251944bb604e51f18b2
Closes https://github.com/facebook/react-native/pull/6965

Differential Revision: D3328001

Pulled By: vjeux

fbshipit-source-id: 977e54382ee07be7a432a54febafcae6acaae905
2016-05-20 08:13:23 -07:00
Alexander Blom d6c2f5f3f1 Don't find module methods until needed
Reviewed By: astreet

Differential Revision: D3322974

fbshipit-source-id: 4cb47cc2ebdffd77b62c5d54e65574d44eba286e
2016-05-20 07:28:26 -07:00
Eric Rozell ef21d99ded Adds packager configuration to support windows platform
Summary:
This pull request is a prerequisite to enabling the react-native-windows platform extension.

In the Resolver component, we need to add 'windows' to the list of platforms that are allowed in the DependencyGraph.  We also need to add 'react-native-windows' (the name of the Windows platform extension NPM module) to the `providesModuleNodeModules` option. This allows the node_module folder check in the DependencyGraphHelper from node-haste to be bypassed for *.windows.js files in the Windows NPM package.

For good measure, I also included a change to blacklist.js to ensure .windows.js files are ignored when the packager is parameterized on a platform.
Closes https://github.com/facebook/react-native/pull/7639

Differential Revision: D3327771

Pulled By: mkonicek

fbshipit-source-id: d1080b045ff6aa0cbf05d8070ceb0eb4cdb6dceb
2016-05-20 05:28:38 -07:00
Mike Grabowski 149d0b91c2 Merge rnpm into react-native
Summary:
This is initial (first step) in the merging process. For now, we are just going to move our code as is into `local-cli` folder (first commit). There were other tweaks made in separate commits to make it easier to go through the code as the diff is expected to be rather large. The purpose of this is to make it easier to start working in small batches and improving the CLI incrementally on a daily basis.

Current codebase will still leave in `rnpm` organisation on Github where we keep working on new features, bugs and ship releases to `npm` until we finish our integration and provide a nice interface for users to migrate (in case it changes at all)

Flow, Jest and npm will ignore this folder for now until we integrate it properly.

Tests are to be rewritten from mocha to jest in `rnpm/link`. We will hook them all up as soon as we start using them in local-cli.

For now, there's no point in having them running and possibly breaking the builds.

We will announce next steps with Kureev later this week
Closes https://github.com/facebook/react-native/pull/7550

Differential Revision: D3327772

Pulled By: mkonicek

fbshipit-source-id: 90faa4bd78476d93ed21b1253e0d95c755d28a30
2016-05-20 04:58:26 -07:00
Andrej Badin 00c77800c9 Improve Docs navigation on handheld devices.
Summary:
Fixes #7519

JS detects handheld device by sniffing UA string (very primitive detection). If on handheld device, event listener is registered. Event handler toggles Docs Navigation overlay after clicking on "Docs" nav button.
Original Docs Navigation panel is taken out of the natural page flow using pure CSS and is styled to look "good" on device.  As a result of this, Navigation overlay is ONLY visible when you are at Docs page, otherwise "Docs" nav button takes you Docs page first.

iPhone/iPad previews
![iphone](https://cloud.githubusercontent.com/assets/829963/15409630/f1a64b1a-1e15-11e6-92eb-f85c5cd06754.gif)
![ipad](https://cloud.githubusercontent.com/assets/829963/15409631/f1a6f952-1e15-11e6-8f5c-6f89f54e6814.gif)
Closes https://github.com/facebook/react-native/pull/7640

Differential Revision: D3325440

Pulled By: vjeux

fbshipit-source-id: a06b21d743d56bfea5db5b750836856c3af9bbe2
2016-05-20 02:50:08 -07:00
Fred Liu 3ccfb58701 Simplify SwipeableRow styling
Summary: - Removed some styling from `SwipeableRow` that wasn't doing much and made slide out view full height

Reviewed By: fkgozali

Differential Revision: D3322849

fbshipit-source-id: 811eee9032c142c61d303ae7e966d8ef7903adaf
2016-05-19 21:43:42 -07:00
Fred Liu 62e588bece Fix Android flash and iOS juttering
Summary:
Before:
- Android had the slideout row flash upon render due to it being rendered first
- iOS had the left side of each row load first, then rerender to show entire row when `scrollViewWidth` is available

Reason:
- Android was loading the slideout view first without an opacity check
- iOS was loading the swipeable view with width 0 first then stretching to `scrollViewWidth` when it was available via `onLayout`

Fix:
Render swipeable view with `flex: 1` then render slideout view

Reviewed By: fkgozali

Differential Revision: D3321466

fbshipit-source-id: 92a3b5e22034e06d05986ddb8c348796bafbbf34
2016-05-19 21:43:42 -07:00
Chris Hopman 830197847a Move more new bridge code into OSS
Reviewed By: mhorowitz

Differential Revision: D3296231

fbshipit-source-id: 5a05b1ddacdfecda067a08398dd5652df76c1f14
2016-05-19 18:58:53 -07:00
Spencer Ahrens 5e91a2a312 Fix 95% of WindowedListView jumpiness
Summary:
- Replace some fixes that were accidentally lost in local rebase that prevent jumpiness when incremental is disabled.
- Require each row to have a key specified by the caller to prevent jumping because of accidental duplicates or unneeded/problematic row re-rendering because of legit re-ordering.

Reviewed By: steveluscher

Differential Revision: D3322006

fbshipit-source-id: 0019aab07cb1fe2b148a14b5818de53aa373eb50
2016-05-19 14:58:26 -07:00
Alexander Blom 942b9332b2 Make BugReporting not pull in native module on require
Reviewed By: astreet

Differential Revision: D3322838

fbshipit-source-id: 4f12fda213b6a4ab7b19d3dafad7f31eb627f85a
2016-05-19 12:58:21 -07:00
Konstantin Raev 6a34c9c3da Disabled flow in e2e test because it causes memory crashes
Summary: Closes https://github.com/facebook/react-native/pull/7620

Differential Revision: D3322029

fbshipit-source-id: e0406770d011956af6b47d15c5b49a68b1b611f5
2016-05-19 03:58:25 -07:00
Leonardo Tegon 8a201b842a Fixes syntax typo in DirectManipulation section of docs
Summary:
Fixing JSX tag in DirectManipulation section of the docs
Closes https://github.com/facebook/react-native/pull/7622

Differential Revision: D3321130

fbshipit-source-id: e315529dc94c88597e4855a63ba6931301d4dba7
2016-05-18 19:43:39 -07:00
Spencer Ahrens 838d8d4094 adaptive render window throughput
Summary:
Incremental rendering is a tradeoff between throughput and responsiveness because it yields. When we have plenty of
buffer (say 50% of the target), we render incrementally to keep the app responsive. If we are dangerously low on buffer
(say below 25%) we always disable incremental to try to catch up as fast as possible. In between, we only disable
incremental while actively scrolling since it's unlikely the user will try to press a button while scrolling.

This also optimizes some things then incremental is switching back and forth.

I played around with making the render window itself adaptive, but it seems pretty futile to predict - once the user
decides to scroll quickly in some direction, it's pretty much too late and increasing the render window size won't help
because we're already limited by the render throughput at that point.

Reviewed By: ericvicenti

Differential Revision: D3250916

fbshipit-source-id: 930d418522a3bf3e20083e60f6eb6f891497a2b8
2016-05-18 17:13:23 -07:00
Fred Liu 514677525a Prevent SwipeableRow bleed
Summary: Default image icon size for Quick Actions could cause slideout view to bleed over the slideable view. All styling has been removed and must now be passed in, thus allowing every caller to control size for best fit.

Reviewed By: furdei

Differential Revision: D3315696

fbshipit-source-id: 8f8b3d2cf7a005d42a18d434d9e0080c64597be0
2016-05-18 16:43:20 -07:00
Alexander Blom dc6a44713d Refactor BugReporing to avoid multiple registers
Reviewed By: sahrens

Differential Revision: D3316146

fbshipit-source-id: 80df4ea73150ba6920a03fe336b3ddb207ba535a
2016-05-18 14:28:26 -07:00
Hedger Wang 7db7f78dc7 Fork NavigationAnimatedView to NavigationTransitioner
Summary:
- Fork NavigationAnimatedView to NavigationTransitioner
- NavigationAnimatedView will soon be deprecated and we'd ask people to use NavigationTransitioner instead.

Difference between  NavigationTransitioner and NavigationAnimatedView

- prop `applyAnimation` is removed.
- new prop `configureTransition`, `onTransitionStart`, and `onTransitionEnd` are added.

tl;dr;

In NavigationAnimatedView, we `position` (an Animated.Value object) as a proxy of the
transtion which happens whenever the index of navigation state changes.

Because `position` does not change unless navigation index changes, it won't
be possible to build animations for actions that changes the  navigation state
without changing the index.

Also, we believe that the name `Transitioner` is a better name for this core component
that focuses on transitioning. Note that the actual animation work is done via
`<Animated.View />` returnd from the `renderScene` prop.

Reviewed By: ericvicenti

Differential Revision: D3302688

fbshipit-source-id: 720c3a4d3ccf97eb05b038baa44c9e780aad120b
2016-05-18 13:43:31 -07:00
Joe Noon 0e997c6eab fixes a bug where NavigationPropTypes.SceneRenderer was a plain object
Summary:
... used as both a shape and plain object.

this splits them out so both parts can be used as needed.

NavigationPropTypes.SceneRenderer is a PropTypes shape
NavigationPropTypes.SceneRendererProps is the plain object that makes up the shape.
Closes https://github.com/facebook/react-native/pull/7518

Differential Revision: D3317322

Pulled By: ericvicenti

fbshipit-source-id: e8a31e05130e6647b63f68dbef31bc874550948c
2016-05-18 13:13:29 -07:00
Marc Horowitz 6796f136f1 shut down JSC before quitting its thread
Differential Revision: D3280544

fbshipit-source-id: 2a3ef2c445aaa1b224ac391b020467e087c0b81d
2016-05-18 12:58:47 -07:00
Marc Horowitz 779314a413 Implement incremental module requires
Differential Revision: D3234844

fbshipit-source-id: 24615528ad6a049aad7c2dbb7ce55e8b034c79e7
2016-05-18 12:58:47 -07:00
Fred Liu 7f1346bfdd Top align quick action buttons
Summary: Top align the button icons

Reviewed By: fkgozali

Differential Revision: D3317805

fbshipit-source-id: b385f9f4e41db1253bb4d523cb07093c63e39da0
2016-05-18 12:43:24 -07:00
Alex Kotliarskyi 858643dbdf Add transform-react-jsx-source to react-native preset
Summary:
Putting this up as request for comments.

The PR adds [transform-react-jsx-source](https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source) to the list of plugins that come by default with the `react-native` preset. It will enable the use of a bunch of really cool tooling around JSX, however those are generally useful only in development mode. Is changing `react-native` preset the right thing to do in this case? Is there a way to enable this transform only in DEV? Should I add this somewhere else?
Closes https://github.com/facebook/react-native/pull/6351

Differential Revision: D3302906

Pulled By: frantic

fbshipit-source-id: 012d3a4142168f9f90d30d1686115d4dc3996eb9
2016-05-18 12:43:24 -07:00
Joel Marcey 18e1b1f197 Best effort selection of target platform and dev environment
Summary:
Right now, if you do a search and select a document in Getting Started, it will
always default to iOS/Mac. This adds a bit of JavaScript to do a best effort
selection based on the hashtags of the headers.

If a header is associated with multiple environments (e.g., Android Studio), we
just choose the first one. So it is not 100% perfect, but it is decent.

ref: https://github.com/facebook/react-native/issues/7574

** Test Plan **

Test locally by adding hash tags to the end of a doc URL and ensured that the toggler had the right
selection.

e.g., `http://localhost:8079/react-native/docs/getting-started.html#chocolatey` had `Android` and `Windows` chosen.
Closes https://github.com/facebook/react-native/pull/7608

Differential Revision: D3316802

Pulled By: JoelMarcey

fbshipit-source-id: 6e94d76725fb97b19b3708ddee8fba5df9350cdd
2016-05-18 09:13:24 -07:00
Konstantin Raev 42e517b7a3 Added timeouts to fs operations in packager
Reviewed By: davidaurelio

Differential Revision: D3316555

fbshipit-source-id: edf6e08569b6cb434219f4460367eec0827530fd
2016-05-18 08:43:24 -07:00
Mike Grabowski e3bbd300f6 Update upgrade command with 0.26 breaking changes
Summary:
Generally it feels that at some point we will just integrate `rnpm upgrade` directly here, but this is a super temporary quick workaround for the people upgrading to 0.26. We might want to change it later to 0.27 and update this message in case we have any other stuff to upgrade.

![screen shot 2016-05-18 at 14 28 47](https://cloud.githubusercontent.com/assets/2464966/15358460/df3b1b66-1d04-11e6-825c-fa18fa96ee6e.png)

It is supposed to work for 0.26.0, 0.26.1 and all the others up to 0.27.
Closes https://github.com/facebook/react-native/pull/7614

Differential Revision: D3316420

fbshipit-source-id: 3861b4228ee878464e18ba3de1f3e0c12d5f30d1
2016-05-18 07:28:52 -07:00
Konstantin Raev 921d0de2e8 Fixes NetowrkingModule construction with interceptors
Reviewed By: andreicoman11

Differential Revision: D3316229

fbshipit-source-id: 0dc5cce68b6649ebd2ff44c313ab94a0f5a06f66
2016-05-18 04:58:20 -07:00
RetroXeusPoktan 061151cdae Add QQ音乐 全民K歌 QQ空间 QQ to showcase
Summary:
Add QQ音乐 全民K歌 QQ空间 QQ to the showcase page
Closes https://github.com/facebook/react-native/pull/7582

Differential Revision: D3316209

fbshipit-source-id: 47e5ce7ec91fb5f4ff747e4ea9ea77476a49fdbc
2016-05-18 04:28:22 -07:00
Alex Kotliarskyi f203c5d78c Add "Open file" button to elements inspector
Summary:
Depends on #6351

Now you can open file directly from Elements Inspector!

Credit for the original implementation goes to jaredly

![zcichu7kem](https://cloud.githubusercontent.com/assets/192222/14573876/cb100f6e-030c-11e6-925f-6a6dff510145.gif)

**Test plan**

Made sure it doesn't crash the app with or without #6351 (i.e. can be merged safely before #6351 gets in).
Closes https://github.com/facebook/react-native/pull/7005

Differential Revision: D3313714

Pulled By: frantic

fbshipit-source-id: 3b80abd3e81a0db5ca5136e2d2c94c775fa04f3a
2016-05-17 20:58:25 -07:00
Joel Marcey 17be4c754e Core components tutorial
Summary:
Create the initial Core Components tutorial. The core components are `Text`, `Image`, `View`, `TextInput`, `ListView`.

1. Provide a summary for each core component, including a runnable sample.
2. Allow the tutorials for each component to be extended with more details and detailed examples, particularly after we add other tutorials (i.e., around state and props).
3. The samples should be runnable in a React Native simulator, if we can get that going in the docs.
4. Reorganize the docs sidebar to make the current Tutorial actually a Sample App, etc.
Closes https://github.com/facebook/react-native/pull/7593

Differential Revision: D3313563

Pulled By: JoelMarcey

fbshipit-source-id: cfe1d397d60822b8c531405d66b4e73694c7dbf9
2016-05-17 18:28:23 -07:00
Marc Horowitz 8199893574 Remove script argument to proxy loadApplicationScript
Summary:
The JSCExecutor API gets passed script data and a URL, but
the proxy's purpose in life is to load from the network, so the script
data is useless.  The code was failing to handle null script data, so
rather than just passing nullptr all the time, I removed the argument.
if there's a use case in the future for it, we can put it back.

Reviewed By: steveluscher

Differential Revision: D3312467

fbshipit-source-id: 986c48f1ef3c24e6b5569046ccb08d7864cdcd3d
2016-05-17 14:28:29 -07:00
Alexander Blom 0694a2991c Use infoLog instead of console.log
Reviewed By: sahrens

Differential Revision: D3304886

fbshipit-source-id: 79d3fd2b833380a18dfbee666f9bd5eaf25b0744
2016-05-17 12:43:40 -07:00
Alexander Blom 2a67f9b249 Move infoLog to OSS
Reviewed By: sahrens

Differential Revision: D3310270

fbshipit-source-id: 93261e6083a35e6869b384c8c230c861427e4060
2016-05-17 12:43:40 -07:00
Andrew Jack 6bbaff2944 Upgrade to OkHttp3
Summary:
Update to [OkHttp](https://github.com/square/okhttp) to [OkHttp3](https://publicobject.com/2015/12/12/com-squareup-okhttp3/)

We must also update:
- Fresco to 0.10.0
- okio to 1.8.0

**Motivation**
Reasons for upgrading:
* Issue #4021
* "We discovered that RN Android sometimes fails to connect to the latest stable version of NGINX when HTTP/2 is enabled. We aren't seeing errors with other HTTP clients so we think it's specific to RN and OkHttp. Square has fixed several HTTP/2 bugs over the past eight months." - ide
* OkHttp3 will be maintained & improved, but OkHttp2 will only receive [security fixes](https://publicobject.com/2016/02/11/okhttp-certificate-pinning-vulnerability/)
* Cleaner APIs - "Get and Set prefixes are avoided"
* Deprecated/Removed - HttpURLConnection & Apache HTTP
* React Native apps are currently being forced to bundle two versions of OkHttp (v2 & v3), if another library uses v3
* Improved WebSocket performance - [CHANGELOG.md](https://github.com/square/okhttp/blob/master
Closes https://github.com/facebook/react-native/pull/6113

Reviewed By: andreicoman11, lexs

Differential Revision: D3292375

Pulled By: bestander

fbshipit-source-id: 7c7043eaa2ea63f95854108b401c4066098d67f7
2016-05-17 12:43:39 -07:00
Chris Evans 5047f6f54c Add Copy Stack button to Red Box (iOS)
Summary:
Added a button to the iOS Red Box to enable copying of the error and stack trace to the clipboard to make it easier to paste the error and stack into bug reports or other text windows. Clicking the Copy button on the bottom of the RedBox screen or pressing Cmd-Option-C keyboard shortcut in the simulator will copy the full stack trace as text to the iOS pasteboard and when in the simulator you can then hit Command-C to get the text to the Mac's pasteboard for pasting in your favorite reporting/tracking app.

No tests for this change - I don't see any tests to extend for the RCTRedBoxWindow class. No impact on APIs or core React Native functionality. I validated it by running test React Native apps with red box errors and both hitting the copy button or using the command key shortcut in the emulator to get the text of the stack to the iOS pasteboard.
![redboxcopybutton](https://cloud.githubusercontent.com/assets/7173455/15258992/7b1e849c-1903-11e6-8813-6e853db5db54.png)
Closes https://github.com/facebook/react-native/pull/7557

Differential Revision: D3311743

Pulled By: javache

fbshipit-source-id: 76d1ac8ab93f40696c6a2369dae2245194db095a
2016-05-17 12:28:21 -07:00
Victor Calvello d62f9612f2 Add 'Vorterix' app to Showcase
Summary:
Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:

(You can skip this if you're fixing a typo or adding an app to the Showcase.)

Explain the **motivation** for making this change. What existing problem does the pull request solve?

Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide).

For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/mas
Closes https://github.com/facebook/react-native/pull/7590

Differential Revision: D3310675

fbshipit-source-id: 8dc4d50eed9237988b409c336695568832c0fe34
2016-05-17 12:13:32 -07:00
Martín Bigio fdc6240018 Reset hasError flag after HMR request
Summary:
This got broken recently. As a result of this, when a module throws while being required, either by regular load or hot reload, the module object is marked as `hasError`. Form that point all subsequent HMR updates will fail because it will think the module failed while executing the factory but the failure could have been on an old run of an old factory.

The fix is very simple: just reset `hasError` to false when accepting a module.
Closes https://github.com/facebook/react-native/pull/7567

Differential Revision: D3310685

fbshipit-source-id: 2f0b48ab7432b7c221d0c88a019a28969a8862b2
2016-05-17 11:58:31 -07:00
Alexander Blom 7446d42d33 Delete jsc-internal
Reviewed By: dcaspi

Differential Revision: D3304892

fbshipit-source-id: fd7ac66c81c93785c3c7887a107c8f1df222aefa
2016-05-17 11:28:34 -07:00
Ben Nham 5bb5ea7135 Reload image without dispatch_async if completion handler is already on main thread
Summary:
This works with D3305161 to minimize image flashing. After D3305161, the completion handler passed to `-[RCTImageLoader loadImageWithoutClipping:size:scale:resizeMode:progressBlock:completionBlock:]` may be called back on the main queue in the case of a cached image. In this case, we want to set the image view's image property synchronously rather than on the next runloop iteration via dispatch_async. This minimizes the amount of image flashing the user sees when displaying a cached image.

The exception to this case is for blurred images. A blur can be an expensive (taking multiple ms on the CPU), so we always make sure to perform the blur off the main queue even if the image is cached and the callback came back on the main queue.

Reviewed By: nicklockwood

Differential Revision: D3310176

fbshipit-source-id: 6820782527b65e4956879cf06e8ed2c09c622a58
2016-05-17 10:43:30 -07:00
Adam Comella fe5c0d2d06 iOS: Enable views to be nested within <Text>
Summary:
Previously, only Text and Image could be nested within Text. Now, any
view can be nested within Text. One restriction of this feature is
that developers must give inline views a width and a height via
the style prop.

Previously, inline Images were supported by using iOS's built-in support
for rendering images with an NSAttributedString via NSTextAttachment.
However, NSAttributedString doesn't support rendering arbitrary views.

This change adds support for nesting views within Text by creating one
NSTextAttachment per inline view. The NSTextAttachments act as placeholders.
They are set to be the size of the corresponding view. After the text is
laid out, we query the text system to find out where it has positioned each
NSTextAttachment. We then position the views to be at those locations.

This commit also contains a change in `RCTShadowText.m`
`_setParagraphStyleOnAttributedString:heightOfTallestSubview:`. It now only sets
`lineHeight`, `textAlign`, and `writingDirection` when they've actua
Closes https://github.com/facebook/react-native/pull/7304

Differential Revision: D3269333

Pulled By: nicklockwood

fbshipit-source-id: 2b59f1c5445a4012f9c29df9f10f5010060ea517
2016-05-17 10:43:30 -07:00
Sean Kelley 908041b80b Round alpha channel when interpolating colors to the nearest thousandth.
Summary:
This fixes an issue where animations for values near zero could end up formatted
with exponents (e.g. `1.452e-10`), which is not valid for an `rgba` color spec.
This commit arbitrarily rounds it to the nearest thousandth to prevent this type
of formatting while still maintaining high-enough resolution in the alpha channel.

One way this could bubble up to the user is as PropType validation failures:

```
Failed propType: Invalid prop `backgroundColor` supplied to `RCTView`: rgba(0, 0, 0, 9.838983123336224e-7)
```
Closes https://github.com/facebook/react-native/pull/7597

Differential Revision: D3310941

Pulled By: vjeux

fbshipit-source-id: 0c95facaef5b69c021662af9fb6f268d890ecc3e
2016-05-17 10:13:29 -07:00
David Aurelio 9092198b1b Terminate startup code with a null byte, too (indexed file random access bundles)
Reviewed By: javache

Differential Revision: D3310269

fbshipit-source-id: d0fbf5afd6baecf50ec568e8694a15c96e6a9c85
2016-05-17 09:58:44 -07:00
Alexander Blom f71329946c Don't barf if property throws
Reviewed By: davidaurelio

Differential Revision: D3305125

fbshipit-source-id: 48143195cd1d48a9f99f4d93dfdf62b51ba6b4c1
2016-05-17 05:13:24 -07:00
Michał Gregorczyk 89b68419ff Add jsc initialization markers to cxx bridge
Reviewed By: lexs

Differential Revision: D3303919

fbshipit-source-id: 0dbbe89de9de165a9d7a1597be050f37ed10a878
2016-05-17 04:28:25 -07:00
Fred Liu 12fcacb58f Full width styling for quick actions
Summary: - Used absolute positioning to render full-width quick actions

Reviewed By: fkgozali

Differential Revision: D3302467

fbshipit-source-id: cdcac88b20eff7a71be1ab8bb39e6888216dad84
2016-05-17 01:43:27 -07:00
Thorben Primke a52e684121 Updated Embed Into Existing Android App Readme
Summary:
Motivation: Fix the documentation so that others don't stumble.

**Test plan**: Integrating RN into an existing Android app doesn't cause the `ConnectivityManagerCompat` exception anymore.

 ---

The instructions where still pointing at maven central. This updates the dependency and provides instructions on how to reference the local maven folder in `node_modules`.
Closes https://github.com/facebook/react-native/pull/7586

Differential Revision: D3309932

fbshipit-source-id: 8e4fc4c0ae5641af886c9d9a0feb1c8172e94fcf
2016-05-16 23:43:22 -07:00
Tobias Ritzau ee77e50c4a Collapse native/{fb,jni,xplatinit} into one DSO
Reviewed By: dcolascione

Differential Revision: D3201488

fbshipit-source-id: 0cf965ee16e360329285d834a4c575d8f1061f15
2016-05-16 22:58:39 -07:00
Fred Liu c779e233b6 Improve SwipeableListView performance
Summary:
- Removed unnecessary rerending of `SwipeableListView` by properly managing `SwipeableListViewDataSource`
- Simplified `SwipeableRow` logic and improved swiping performance
- Added bounce effect
- Locked `ListView` from being scrollable when `SwipeableRow` is being swiped; behaviour mirrors that of Android on iOS and significantly improves framerates

Reviewed By: fkgozali

Differential Revision: D3307599

fbshipit-source-id: 168b6b72ef1f9e47d0145cf9e1baecbab3564b84
2016-05-16 20:28:27 -07:00
Yu Huang 34907c3810 Fix List View _updateVisibleRows firing for all row component at view loading time issue
Summary:
Sometimes list view row components(even the ones unseen) have a frame but its x and y is 0 or uninitialized at view loading time, which will lead to _updateVisibleRows firing for all the row components incorrectly at loading time.
This is to make the code more defensive.

Reviewed By: sahrens

Differential Revision: D3308515

fbshipit-source-id: 0e30fdf10345629bfd9de3fe0ad641bb1903fe00
2016-05-16 18:28:21 -07:00
Gabe Levi 0c40f96c32 Upgrade to v0.25.0
Reviewed By: jeffmo

Differential Revision: D3308221

fbshipit-source-id: c17b5ed861cfc201b8bd36f9c01218da25960001
2016-05-16 17:13:23 -07:00
邓国梁 2bb4e69382 add showcase for xiaoxian
Summary:
RT
Closes https://github.com/facebook/react-native/pull/7082

Differential Revision: D3306415

Pulled By: mkonicek

fbshipit-source-id: d2d3bfca68a890e10393c3904bdff222ec414865
2016-05-16 13:13:28 -07:00
Yu Zheng(Sam) 67d9a7bae9 Mark SupportAlertFragment and AlertFragment as Public to fix crash is…
Summary:
Hi,
This changelist is for fixing a crash issue on Android devices. For detail please see, https://github.com/facebook/react-native/issues/7080

In short, the crash occurred when a RN app with a Alert dialog shown at front switches back after it is destroyed at background due to lack of phone memory.

My fix is to let those Alert fragment classes accessible from android.support.v4.app.Fragment instantiate() function, so that it won't crash. And since other UI will be reloaded whatever, mListener is set to null to avoid any callback happen.
Closes https://github.com/facebook/react-native/pull/7105

Differential Revision: D3212435

Pulled By: mkonicek

fbshipit-source-id: d900a33a4f0fd49258be94c277def55555ec8d73
2016-05-16 12:58:30 -07:00
Janic Duplessis aac7f5b362 Fix tests buck build when running locally
Summary:
Got this error when trying to run instrumentation tests locally with `./scripts/run-android-local-integration-test.sh`

```
C:\Users\janic\Developer\react-native\ReactAndroid\src\androidTest\java\com\facebook\react\tests\DatePickerDialogTestCase.java:110: error: cannot access com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
    return (DialogFragment) getActivity().getSupportFragmentManager()
                                         ^
  class file for com.facebook.react.modules.core.DefaultHardwareBackBtnHandler not found
C:\Users\janic\Developer\react-native\ReactAndroid\src\androidTest\java\com\facebook\react\tests\ViewRenderingTestCase.java:82: error: cannot access com.facebook.react.touch.ReactInterceptingViewGroup
    assertEquals("Incorrect (or not applied) opacity", expectedOpacity, view.getAlpha());
                                                                            ^
  class file for com.facebook.react.touch.ReactInterceptingViewGroup not found
C:\Users\janic\Developer
Closes https://github.com/facebook/react-native/pull/7575

Differential Revision: D3306330

fbshipit-source-id: a6d8afd060b54d56f9e2b97f45b642d7b7f46209
2016-05-16 12:28:40 -07:00
Tore Hanssen df40f482b0 Callback with image height and width from IOSImagePicker
Reviewed By: javache

Differential Revision: D3301645

fbshipit-source-id: c771d491447f8ed9dc6c8685e273b99656336b0a
2016-05-16 11:13:23 -07:00
Martín Bigio 6457effb7b Avoid clearing out factory on DEV mode
Summary:
grabbou pointed out this issue.

We recently started cleaning out the factory function after module are required to save some memory. This broke HMR on some edge cases because sometimes the factory function may need to be re-executed. This PR just wraps the optimization into `__DEV__` to make sure we don't use it while developing.
Closes https://github.com/facebook/react-native/pull/7568

Differential Revision: D3305120

Pulled By: martinbigio

fbshipit-source-id: 741cffbb327d118f0bd0ec34dc1af53d4f94880e
2016-05-16 10:43:21 -07:00
Alexander Blom 3fe2e6581c Remove duplicate install of nativeFlushQueueImmediate
Summary:
This was accidentally added twice.

**Test plan (required)**

CI
Closes https://github.com/facebook/react-native/pull/7584

Differential Revision: D3305537

Pulled By: astreet

fbshipit-source-id: 7d652f9c7762cb66715f1b7235a186513d9b938d
2016-05-16 10:28:45 -07:00
Martin Konicek 10429b5822 Test that Picker selection is updated correctly
Summary:
A React pull request broke the Picker: https://github.com/facebook/react/pull/6572
This pull request adds a test so that we prevent breakages like this in the future.

`PickerAndroidExample` in the UIExplorer before the breakage (video): https://vid.me/h5Rg
`PickerAndroidExample` in the UIExplorer after the breakage (video): https://vid.me/atFc

**Test plan**

`./scripts/run-android-local-integration-test.sh`

The test is failing, we can only merge this once React is fixed.

<img width="837" alt="screen shot 2016-05-04 at 5 13 31 pm" src="https://cloud.githubusercontent.com/assets/346214/15020827/aca8d718-121b-11e6-91f6-a338293d7d92.png">
Closes https://github.com/facebook/react-native/pull/7390

Differential Revision: D3305223

Pulled By: mkonicek

fbshipit-source-id: 89137812940c578efff3e76864ab7cadccc9f2ea
2016-05-16 09:13:28 -07:00
Nick Lockwood d2934e58b3 Expose UIManager queue via a static function to prevent race conditions
Summary: Having UI modules access the shadowQueue via UIManager.methodQueue is fragile and leads to race conditions in startup, sometimes resulting in an error where the methodQueue is set twice, or not at all.

Reviewed By: javache

Differential Revision: D3304890

fbshipit-source-id: 7198d28314dbec798877fcaaf17ae017d50157e9
2016-05-16 08:13:21 -07:00
Naman Goel 1673e00e2b correct flow types
Summary:
The function should have optional arguments, but not nullable types.
Closes https://github.com/facebook/react-native/pull/7580

Differential Revision: D3304994

Pulled By: davidaurelio

fbshipit-source-id: 9918817429126ea16dd3076b61a3493c3a3a8388
2016-05-16 07:58:26 -07:00
Alexander Blom f13322c820 Add support for dumping current React hierarchy
Reviewed By: sebmarkbage

Differential Revision: D3287391

fbshipit-source-id: 0ef1cd25a910dcc0a50ed52a2cc0ae702a9654f4
2016-05-16 07:28:29 -07:00
Alexander Blom 610cfdc819 Add support for custom files in BugReporting
Reviewed By: astreet

Differential Revision: D3287186

fbshipit-source-id: ce7000ac110b78a4fb94c66229046617c292675f
2016-05-16 07:28:29 -07:00
Pieter De Baets 6a3fc86778 Remove 64-bit restriction from React Native tests
Reviewed By: majak

Differential Revision: D3298189

fbshipit-source-id: f25b223d677a0ec8e1a665dd54de685e00d497b8
2016-05-16 05:28:23 -07:00
David Aurelio 2f41c56f59 change to fixed size offset table + indexed lookup
Reviewed By: javache

Differential Revision: D3281989

fbshipit-source-id: 52db9f441dd46024eafac46ae8d32b26640cad71
2016-05-16 04:43:24 -07:00
Nick Lockwood 516bf7bd94 Fixed NativeEventListener deregistration
Summary:
The `EmitterSubscription.remove()` method was previously calling `this.subscriber.removeSubscription(this)` directly, bypassing the mechanism in `NativeEventEmitter` that keeps track of the number of subscriptions.

This meant that native event modules (subclasses of `RCTEventEmitter`) would keep sending events even after all the listeners had been removed. This wasn't a huge overhead, since these modules are singletons and only send one message over the bridge per event, regardless of the number of listeners, but it's still undesirable.

This fixes the problem by routing the `EmitterSubscription.remove()` method through the `EventEmitter` so that `NativeEventEmitter` can apply the additional native calls.

I've also improved the architecture so that each `NativeEventEmitter` uses its own `EventEmitter`, but they currently all still share the same `EventSubscriptionVendor` so that legacy code which registers events via `RCTDeviceEventEmitter` still works.

Reviewed By: vjeux

Differential Revision: D3292361

fbshipit-source-id: d60e881d50351523d2112473703bea826641cdef
2016-05-16 04:13:56 -07:00
Alexander Blom 9a899bed65 Memoize lazy module configs
Reviewed By: javache

Differential Revision: D3293010

fbshipit-source-id: feab7bb0a1f8990baea9c0aca8c9c3cccbcb04e8
2016-05-16 03:58:23 -07:00
Dave Miller ccef1b2941 More touch logging
Reviewed By: andreicoman11

Differential Revision: D3298518

fbshipit-source-id: 0ddbba5c05848ac164f6f1562b26609924978169
2016-05-16 02:58:21 -07:00
Shane O'Sullivan fa5d1fefa1 Make the root view background color explicit
Summary:
The default white background for the root view causes an unattractive flash when loading any app with a non-white background.  Almost every developer has to search for how to fix it.

This change makes the background color explicit in the file, so it can be easily change to match the color of the splash screen and the main screen without having to search for it.

**Test plan (required)**
Launched my app and it still has a white flash for the background before loading the app.  I changed the background colors to other values and the flash was that color.
Closes https://github.com/facebook/react-native/pull/7570

Differential Revision: D3303451

Pulled By: vjeux

fbshipit-source-id: 61dbab768095c430bcb583bafb57575a7d767e6f
2016-05-14 22:43:20 -07:00
Yao Bin Then 433c0e8021 Add comments on NetInfo.js
Summary:
Further describe the methods available on NetInfo.

Question: How do I update the docs on the website?
Closes https://github.com/facebook/react-native/pull/7375

Differential Revision: D3303300

fbshipit-source-id: 4343d490f65e4e47b93f2c98a645cb675d2cf708
2016-05-14 21:50:24 -07:00
David Aurelio e34c9c4e5f Include forwarding modules into npm package
Reviewed By: bestander

Differential Revision: D3301666

fbshipit-source-id: 723d87f1df217f0f1f78661bb6c41a9bbfab6814
2016-05-14 16:28:26 -07:00
Spencer Ahrens c2a8e52d72 Fix source map utils
Reviewed By: javache

Differential Revision: D3302496

fbshipit-source-id: de15a289497119c7f600b8796452fc9573ea7394
2016-05-14 10:43:30 -07:00
Marc Horowitz 3a2d024514 avoid waiting on the main thread just to send a notification
Reviewed By: nicklockwood

Differential Revision: D3234837

fbshipit-source-id: 587a68df7d08999d4357583ba3160b315bd85239
2016-05-13 17:29:00 -07:00
Marc Horowitz 9e9dfd2ac9 Introduce non-copyable JSBigString for managing large strings efficiently
Reviewed By: astreet

Differential Revision: D3234836

fbshipit-source-id: 2b95b585dc1215988b88cf0d609c778a95b362a1
2016-05-13 17:29:00 -07:00
Marc Horowitz f433ed716c Make the pattern for adding systrace sections more concise
Reviewed By: astreet

Differential Revision: D3234834

fbshipit-source-id: 73e191deb4dd0e06d4c242e46a582051d4bcc9ba
2016-05-13 17:29:00 -07:00
Marc Horowitz 267264f1ec Add a bunch more systrace logging
Reviewed By: tadeuzagallo

Differential Revision: D3234831

fbshipit-source-id: 177f9b987e938c59d50fc07bce01fb5c912652e7
2016-05-13 17:29:00 -07:00
Marc Horowitz 86f2eb18e5 Support JS Systrace on all platforms
Reviewed By: tadeuzagallo

Differential Revision: D3234830

fbshipit-source-id: 94cc870d47d620c8bd8d35f83d0b017e5ddba90d
2016-05-13 17:29:00 -07:00
Marc Horowitz b7fe8e68be Finer granularity systracing
Reviewed By: astreet

Differential Revision: D3234826

fbshipit-source-id: 900ab93b847949413a134a87a20b0d826c58da3d
2016-05-13 17:29:00 -07:00
Marc Horowitz 86e18ace94 make RCTFBSystrace actually work
Reviewed By: tadeuzagallo

Differential Revision: D3234824

fbshipit-source-id: 9f2f7efd6877fe7b9096dedecb311d226eae7bc8
2016-05-13 17:29:00 -07:00
Marc Horowitz 418e16f711 Support flow events in RCTProfile
Summary: Add begin/end async flow to the callbacks for external profiling

Reviewed By: tadeuzagallo

Differential Revision: D3234812

fbshipit-source-id: cdbe46883fd566d50a04de9ad67f947876a78a0c
2016-05-13 17:29:00 -07:00
Marc Horowitz 05832c3469 Catch any std::exception from unbundling
Reviewed By: davidaurelio

Differential Revision: D3148408

fbshipit-source-id: 78dc47e1b092cb7fd06307d505a795d2009cee4d
2016-05-13 17:29:00 -07:00
Marc Horowitz 448b48ca85 remove useless text from js errors
Reviewed By: astreet

Differential Revision: D3127112

fbshipit-source-id: 56ee9da8cd7d409c7b13864e6d4cc863146d19f0
2016-05-13 17:29:00 -07:00
Marc Horowitz 7762297f77 add supportsWebWorkers
Reviewed By: lexs

Differential Revision: D3149754

fbshipit-source-id: 58bce886aff526bc7152eaca2f43a654dcb5ad2a
2016-05-13 17:29:00 -07:00
Adam Comella 03edc75b56 iOS: Provide correct animation function for hiding keyboard
Summary:
Currently, `7 << 16` is used as the animation easing function for both the showing and hiding of the keyboard.

Instead, `7 << 16` should be used for showing the keyboard and `6 << 16` for hiding the keyboard.

For details, see: http://stackoverflow.com/questions/18870447/how-to-use-the-default-ios7-uianimation-curve/19439283#19439283

**Test plan (required)**

Testing animating the position of a view to coordinate with the keyboard hiding and showing and it looks good.

Adam Comella
Microsoft Corp.
Closes https://github.com/facebook/react-native/pull/7372

Differential Revision: D3300464

Pulled By: nicklockwood

fbshipit-source-id: 3fdc161f709de11deb7de511aad28767f5d80bd6
2016-05-13 15:43:22 -07:00
Arno Fortelny cac5ce3b93 RefreshControl on initial render will not beginRefresh when refreshing state is false
Summary:
There's an edge case in the `RefreshControl` which causes it to show as refreshing when the state is set to false. When the component is initialized with `refreshing` set to `true` on initial render but set to `false` before `layoutSubviews` is called, it will call `beginRefresh` and ignore its state. That's because `layoutSubviews` never checks if `_currentRefreshingState` is in fact still `true`, it merely assumes it is.

This is fixed by simply doing a check for `_currentRefreshingState` before entering `beginRefresh` from `layoutSubviews`.
Closes https://github.com/facebook/react-native/pull/7556

Differential Revision: D3300124

fbshipit-source-id: d1dce8612e2c03b1f14284d513803d00af4b5c8a
2016-05-13 14:43:26 -07:00
Hedger Wang 8975bb8e70 didFocus event should fired after transition finishes.
Summary:
For now, we emit `didFocus` event before fully resetting the transition and
hiding the stale scenes, so handler for `didFocus` event does not get the right
scenes in place. We should emit the didFocus event after resetting the transition and
hiding the stale scenes.

Reviewed By: ericvicenti

Differential Revision: D3299002

fbshipit-source-id: 6a79528097aabc4f61d4dba322820c228c889a84
2016-05-13 12:58:24 -07:00
Jimmy Mayoukou 2849bafbc3 Fix typo in ViewPagerAndroid.android.js
Summary: Closes https://github.com/facebook/react-native/pull/7456

Differential Revision: D3299061

fbshipit-source-id: 417995191d675e2ae625926df93412c660d1931a
2016-05-13 11:58:23 -07:00
Nathan Spaun b67d4a20d7 Add FpsListener to React Scroll Views
Summary:
We want to give people the ability to log scroll performance (including Fb).
This adds an interface that can be enabled and disabled from the react scroll views.
This is a prerequisite to implementing the actual framerate logger that will log dropped
frames while scrolling in prod.

Reviewed By: astreet

Differential Revision: D3283588

fbshipit-source-id: ed9736cb9ed3f441511647f36b1460092bd91e56
2016-05-13 11:28:21 -07:00
Olivier Notteghem fd376660df Fix race condition when clicks are received on react root view and react context is pending
Reviewed By: astreet

Differential Revision: D3297574

fbshipit-source-id: 27ac81342d19947e9425a845d7ef7c73cc4cf090
2016-05-13 09:43:22 -07:00
Niklas Lindroos cc683bc088 Fixed a few language errors in the Introducing Hot Reloading blog post.
Summary:
Have completed the CLA.
Closes https://github.com/facebook/react-native/pull/7552

Differential Revision: D3298347

Pulled By: vjeux

fbshipit-source-id: 1945a3d266b19ad66d03e03e8f36baf44ca804b8
2016-05-13 08:58:24 -07:00
Dave Miller e491f7d5c6 Reverted commit D3292369
Reviewed By: andreicoman11

Differential Revision: D3292369

fbshipit-source-id: 1a41d845ff46c44544f37d85e8154557a979a4ff
2016-05-13 04:28:27 -07:00
qingfeng 1322e06cb2 Add "SAY a Short Video Community" app to the showcase.
Summary:
SAY is "Q & A community-based short video", All based react native development

- You can shoot video
- Video Filters can be switched
- Private chat

![image](https://cloud.githubusercontent.com/assets/18495/15239741/7a1152be-1917-11e6-9c93-0e75b3a7bd60.png)

![image](https://cloud.githubusercontent.com/assets/18495/15239758/ae7a08c0-1917-11e6-82ea-f0f998785bd6.png)

![image](https://cloud.githubusercontent.com/assets/18495/15239776/c89b5588-1917-11e6-8962-5803865a02b4.png)

![image](https://cloud.githubusercontent.com/assets/18495/15239783/d0500670-1917-11e6-9699-708b1d9ebceb.png)
Closes https://github.com/facebook/react-native/pull/7547

Differential Revision: D3297728

fbshipit-source-id: 3ef57a8042c5de34d56ce4fcde1b62cccbb3f422
2016-05-13 01:58:34 -07:00
Matt Mcnamee b66f098193 Added 'Whisky Journal' to Showcase
Summary: Closes https://github.com/facebook/react-native/pull/7541

Differential Revision: D3297739

fbshipit-source-id: fc87f1b409cffe26049292ca7cfb421cf0fb1c26
2016-05-13 01:58:34 -07:00
Olivier Notteghem 99b106658f Reverted commit D3278698
Summary:
= Breaking Change (for experimental features) =

Major API changes in  NavigationAnimatedView

= New prop `transition` for scene renderer

In NavigationAnimatedView, we should not use `position` as a proxy of the
transtion which happens whenever navigation state changes.

Because `position` does not change unless navigation index changes, it won't
be possible to build animations for actions that replace navigation state
without changing the index.

This diff introduces an abstract prop `transition` that is exposed to the scene
renderers.

= Replace `applyAnimation` with `configureTransition`.

Expose a new optional prop  `configureTransition` that allows people to configure
transitions easily.

For instance, to configure the transition, do this:

```
function configureTransition() {
  return {
    dutation: 123,
    easing: Easing.easeInOut,
  };
}

```
<NavigationAnimatedView configureTransition={configureTransition) />

```

Reviewed By: ericvicenti

Differential Revision: D3278698

fbshipit-source-id: b790b92e0fabb42488ff1135b1c37a3f0e9420f7
2016-05-12 18:58:45 -07:00
Konstantin Raev 588183b166 Increased timeouts and retries for instrumentation tests
Summary:
Tests fail quite often but retries seem to help.
Closes https://github.com/facebook/react-native/pull/7537

Differential Revision: D3295833

fbshipit-source-id: e74be5d62b7fb9c1859477e51bf18fe3d1d25ce4
2016-05-12 17:43:35 -07:00
David Aurelio b4e970f6c3 Use continuous module IDs for random access bundles
Reviewed By: bestander

Differential Revision: D3292980

fbshipit-source-id: ab5791d31add42a26cf55a0309564330c383eaa2
2016-05-12 17:28:25 -07:00
Fred Liu 3f1cd5aa4e Center align text on Android
Summary: Center align text on Android

Reviewed By: fkgozali

Differential Revision: D3295222

fbshipit-source-id: c707d1102c8aa86aa04dc9b34a4746b1ff1543bc
2016-05-12 16:09:30 -07:00
Christopher Chedeau 90663bcb12 Add link to the survey at the bottom of the page
Summary:
![simulator screen shot may 11 2016 3 03 14 pm](https://cloud.githubusercontent.com/assets/197597/15198889/3374abe6-178f-11e6-95e5-378004ea45aa.png)

<img width="1333" alt="screen shot 2016-05-11 at 3 03 25 pm" src="https://cloud.githubusercontent.com/assets/197597/15198886/2e545a8a-178f-11e6-8fd1-975ca700e49b.png">
Closes https://github.com/facebook/react-native/pull/7524

Reviewed By: JoelMarcey

Differential Revision: D3293684

Pulled By: vjeux

fbshipit-source-id: c0e7d138040eae49c6b7f7398de8d50f0ed2f564
2016-05-12 14:43:22 -07:00
Alexander Blom e4bb9f52e5 Move all hooks over to new helper
Reviewed By: astreet

Differential Revision: D3292927

fbshipit-source-id: baafa6ded3872391ae1319b2855b2616ec79c2ad
2016-05-12 14:33:28 -07:00
Alexander Blom cafd63440a Add helper for installing method as hook
Reviewed By: astreet

Differential Revision: D3292457

fbshipit-source-id: 590637dbf868097214ca9cb7b9a6543dc48a898f
2016-05-12 14:33:28 -07:00
Reem Helou a53d0f99dd Add an array of scrollviewlisteners in RCTScrollableProtocol
Summary: We are deprecating nativeScrollDelegate property in RCTScrollableProtocol in favor of a scrollListener api. To register/unregister from scroll events use the addScrollListener/removeScrollListener methods

Reviewed By: javache

Differential Revision: D3278218

fbshipit-source-id: 54373cae8e9f8efa7cdbd40c51bcf21d368acf75
2016-05-12 12:17:20 -07:00
Fred Liu 853074d1ee Pass along scrollResponder()
Summary: Allows calls to `scrollResponder()` without crashing

Reviewed By: fkgozali

Differential Revision: D3293272

fbshipit-source-id: c46d99c0d7430a37fc364fa3aaf77dd192d7ab37
2016-05-12 11:49:24 -07:00
Konstantin Raev 2c3ca4c058 Open sourced 4 more instrumentation tests for RN Android
Summary:
More instrumentation tests in OSS means less work for FB engineers to investigate if a PR breaks some internal tests.

+ increased timeouts and retries for OSS tests runner

Reviewed By: andreicoman11

Differential Revision: D3292582

fbshipit-source-id: 3f8aa4d3536450ea3af7acff044b9bb62be0f9db
2016-05-12 10:52:30 -07:00
Dave Miller 31c8b3bd61 Add some defensive loggin in Android touch handling
Reviewed By: andreicoman11

Differential Revision: D3292369

fbshipit-source-id: 43315f7d8e81f1a0ec238c08c3cb689e6648fd0b
2016-05-12 10:01:21 -07:00
Dave Miller f2c1868b56 Update the timestamp we send in touch events to be the more accurate timestamp we use
Summary: Use the more accurate timestamp that we have computed for the touch event rather than the event timestamp that Android provides.

Reviewed By: andreicoman11

Differential Revision: D3292705

fbshipit-source-id: dad082ab74406d391481d16cdac19629751aa1eb
2016-05-12 09:40:26 -07:00
Nick Lockwood 2525feb37f Updated Websocket to use new event system
Reviewed By: javache

Differential Revision: D3292473

fbshipit-source-id: f9a9e0a1b5a12f7fa8b36ebdba88405370f91c54
2016-05-12 08:30:24 -07:00
Michał Gregorczyk cd691e2cc2 Let jsc know it will run web worker
Reviewed By: astreet

Differential Revision: D3287543

fbshipit-source-id: 2a9de938feec417aef34c0ff26f138730b32929f
2016-05-12 08:13:21 -07:00
Martin Kralik 44bf85a041 different coalescing key for touchCancel events
Summary:
On iPad we may get two touch cancel events in direct succession. They would have the same coalescing key, which would result in unsuccesful attempt to coalesce them.
This diff fixes it by making sure two cancel events cannot have the same coalescing key.

(An alternative fix would be implementing coalescing logic for cancel events, but that sounds more complicated. It would be neccessary if there is a legit scenario where big number of cancel events are emitted in succesion.)

Reviewed By: javache

Differential Revision: D3292405

fbshipit-source-id: 1f269771dc81fdd637cf6ac3ee4725e5e2fec679
2016-05-12 08:11:24 -07:00
Konstantin Raev 428c563c75 Revert "Fix RefreshControl race condition"
Summary:
This reverts commit 8fbce3099d.
Reverts https://github.com/facebook/react-native/pull/7317 because it breaks instrumentation tests https://circleci.com/gh/facebook/react-native/6521
Closes https://github.com/facebook/react-native/pull/7529

Differential Revision: D3292461

fbshipit-source-id: 7dcde05adefe41e6b3c28697fccfa232a45f0742
2016-05-12 04:50:25 -07:00
Pieter De Baets 1b5b5d9c5b Improve error handling in route generation script
Reviewed By: sam-swarr

Differential Revision: D3282748

fbshipit-source-id: 17301c6639a7517d1171a67567f220471020b78d
2016-05-12 04:29:24 -07:00
Konstantin Raev 9b56ff6bab Open sourced ProgressBarTest
Reviewed By: andreicoman11

Differential Revision: D3287396

fbshipit-source-id: 1a814b72ebcb5d13106c6a612b341f217c4dc5a6
2016-05-12 04:07:08 -07:00
Ben Roth 36b085422d Correctly test for HMR parameter in the bundle URL
Summary: Closes https://github.com/facebook/react-native/pull/7525

Differential Revision: D3292314

Pulled By: javache

fbshipit-source-id: 04090f7f1253e055278a6774f7e135833ae3d6f6
2016-05-12 03:30:23 -07:00
Sakari Tuominen 85d2086321 Fix a bug, if clipData is null we can not ask getItemCount() from it
Summary:
I ran into problems when using Clipboard.getString() in Android. The method seems to work fine when there is *something* in the clipboard, but when it's empty the app crashes. I think I've tracked down the bug to ClipboardModule.java@58.

When clipData is null, the promise is resolved with an empty string. However, after that the rest of the block is executed. There should be a return or an 'else if' like in my pr.

Screenshot of the error message when clipboard is empty:

![image](https://cloud.githubusercontent.com/assets/7509183/15206922/44bd2094-182b-11e6-9400-6a59c513de24.png)
Closes https://github.com/facebook/react-native/pull/7527

Differential Revision: D3292232

fbshipit-source-id: d2191286c49ee31233203fab4648449964b9d950
2016-05-12 01:45:21 -07:00
Hedger Wang 55c308615a Expose a new prop transition for scene renderer.
Summary:
= Breaking Change (for experimental features) =

Major API changes in  NavigationAnimatedView

= New prop `transition` for scene renderer

In NavigationAnimatedView, we should not use `position` as a proxy of the
transtion which happens whenever navigation state changes.

Because `position` does not change unless navigation index changes, it won't
be possible to build animations for actions that replace navigation state
without changing the index.

This diff introduces an abstract prop `transition` that is exposed to the scene
renderers.

= Replace `applyAnimation` with `configureTransition`.

Expose a new optional prop  `configureTransition` that allows people to configure
transitions easily.

For instance, to configure the transition, do this:

```
function configureTransition() {
  return {
    dutation: 123,
    easing: Easing.easeInOut,
  };
}

```
<NavigationAnimatedView configureTransition={configureTransition) />

```

Reviewed By: ericvicenti

Differential Revision: D3278698

fbshipit-source-id: 25ebad286d8b41f46c35c0f32d6023ebd01f19e7
2016-05-11 18:45:31 -07:00
Fred Liu 763e9cce27 SwipeableListView quick actions
Summary:
- Implemented a quick actions template to allow children buttons to be rendered in the slideout of `SwipeableListView`
- Implemented `QuickActionsButton` to allow buttons to be used in the slideout view

Reviewed By: fkgozali

Differential Revision: D3271946

fbshipit-source-id: b7c7412f44be64dea7084ca36c4b0f4f3842b008
2016-05-11 18:02:20 -07:00
Janic Duplessis 8fbce3099d Fix RefreshControl race condition
Summary:
There was a race condition with `SwipeRefreshLayout` that cause the `RefreshControl` to keep refreshing when it shouldn't.

It was caused because we have to use `post` to set the refreshing state otherwise it doesn't work when setting `refreshing=true` on initial mount. What happened is that `post` doesn't guarantee the order the runnables will be called so calling post with `refreshing=true` followed by `refreshing=false` caused the `resfreshing=false` runnable to be called before the `resfreshing=true` one. This made it stay in refreshing state when it should not.

```
D/test    ( 6171): setRefreshing true
W/ReactNativeJS( 6171): false
D/test    ( 6171): setRefreshing false
D/test    ( 6171): setRefreshing post false
D/test    ( 6171): setRefreshing post true
```

This change adds an instance variable and uses it in the `post` runnable to make sure the last set value is always used.

**Test plan (required)**
Tested that it fixed the issue in the [original issue app](https://github.com/digisqu
Closes https://github.com/facebook/react-native/pull/7317

Differential Revision: D3290464

Pulled By: andreicoman11

fbshipit-source-id: 15cabcfc6d2f191443be96e8845b924ce66c369f
2016-05-11 17:16:23 -07:00
Joel Marcey 20c8899b31 Revert "Add docs survey link to site header"
Summary:
This reverts commit 4a7f2192f9.

Adding the link breaks mobile
Closes https://github.com/facebook/react-native/pull/7520

Differential Revision: D3290347

Pulled By: JoelMarcey

fbshipit-source-id: 950db67eb14b5fe8816d4c782b32d233a8697ae7
2016-05-11 17:09:20 -07:00
Kevin Johnson 31250ad150 Ability to disable/lock the android ViewPager scroll from props
Summary:
This is a nice feature to have.

I've tested this by copying and renaming the ViewPager java and javascript files from the react-native repo and including them in a project. Whats the best way to test this directly from the repo?
Closes https://github.com/facebook/react-native/pull/5968

Differential Revision: D3240651

Pulled By: mkonicek

fbshipit-source-id: 5f1d157216df4f3314915496188a92aec1b85e91
2016-05-11 16:51:17 -07:00
Hedger Wang c3714d7ed7 Kill <NavigationView />
Summary:
= Breaking Change (for experimental features) =

<NavigationView /> serves no obvious benefit and it's costly to maintain it.

- its features could be completely replaced by NavigationAnimatedView without
  enabling the transtion animation.

- it could also be replaced with a simple view and normal scene renderer.

Reviewed By: ericvicenti

Differential Revision: D3280547

fbshipit-source-id: 4ac3dbe92ceb9107a1f6e77a78bd6021485e78a9
2016-05-11 16:23:26 -07:00
Alexander Blom 0934470676 Remove JS config completely
Reviewed By: astreet

Differential Revision: D3276230

fbshipit-source-id: b3ab7e7dc149696b7db1049613fbb6a2a19bc8fb
2016-05-11 11:39:20 -07:00
Joel Marcey 4a7f2192f9 Add docs survey link to site header
Summary:
**Test plan**

Tested locally to see link in header bar and that it takes you to survey.

![screenshot 2016-05-11 08 21 11](https://cloud.githubusercontent.com/assets/3757713/15186299/593fd4f6-1751-11e6-847f-ea73ce4e2809.png)
Closes https://github.com/facebook/react-native/pull/7514

Differential Revision: D3287859

fbshipit-source-id: 48dfdbae74622330f192d5763c5e47025f1887b1
2016-05-11 11:05:52 -07:00
Nick Lockwood 9ee1f37bad Added native event emitter
Summary:
This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/

I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already).

Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`.

JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing:

    const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
    RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... });

You'd now write:

    const Keyboard = require('Keyboard');
    Keyboard.addListener('keyboardWillShow', (event) => { ... });

Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of:

     this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...);

Write:

    this.addListenerOn(Keyboard, 'keyboardWillShow', ...);

This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module.

As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff.

For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface.

Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem).

Reviewed By: javache

Differential Revision: D3269966

fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 06:27:29 -07:00
Spencer Ahrens 2d43663ac8 Remove cruft from ScrollView
Reviewed By: dmmiller

Differential Revision: D3287077

fbshipit-source-id: dfc238469bce540d69c38d3e17c87163e10d7b23
2016-05-11 05:22:20 -07:00
楚怀 4b4c040af7 fix FPSGraph label setText not execute on main thread
Summary:
Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:

(You can skip this if you're fixing a typo or adding an app to the Showcase.)

Explain the **motivation** for making this change. What existing problem does the pull request solve?
as apple states, all UIKit methods should execute on main thread, while FPSGraph is not.

Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide).

For more info, see the ["Pull Requests"
Closes https://github.com/facebook/react-native/pull/7500

Differential Revision: D3287011

Pulled By: tadeuzagallo

fbshipit-source-id: 23c4248c8dc65d337afb12626e597dfb6a621e96
2016-05-11 04:11:25 -07:00
Spencer Ahrens 892e509a0c Fix flow and lint for ScrollView
Summary:
- getScrollResponder returns a...ScrollView!
- no more var
- no more string refs

Reviewed By: spicyj

Differential Revision: D3286596

fbshipit-source-id: d9e6e0a318eadd2521c9f5c69d2ec368f1d7b626
2016-05-11 03:00:27 -07:00
Fred Liu 052cd7eb8a SwipeableListView
Summary:
- Updated SwipeableListView to be much more performant by checking `rowHasChanged` more vigorously
- New `SwipeableListViewDataSource` used to mask implementation details from caller

Reviewed By: fkgozali

Differential Revision: D3272172

fbshipit-source-id: 02f66ed7fce7d587118ad7d82b20f8e78db44b7b
2016-05-11 00:25:20 -07:00
Alex Leventer a0384dad3c Add Splash to Showcase
Summary:
Add an additional react native app to the showcase page.
Closes https://github.com/facebook/react-native/pull/7499

Differential Revision: D3286553

fbshipit-source-id: f057217b4db6cb1134e15abbca1067483066a68e
2016-05-10 23:25:48 -07:00
Alex Ledak 2a150cc30a Update name & app icon for 'New Music' app
Summary:
Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:

(You can skip this if you're fixing a typo or adding an app to the Showcase.)

Explain the **motivation** for making this change. What existing problem does the pull request solve?

Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide).

For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/mas
Closes https://github.com/facebook/react-native/pull/7420

Differential Revision: D3286539

fbshipit-source-id: a7df51f0ae485a0c9efa52ec47e8ac023a2319bf
2016-05-10 23:23:30 -07:00
ztodorovic 3abccd9640 Add "Nabava.net" app to the showcase.
Summary: Closes https://github.com/facebook/react-native/pull/7486

Differential Revision: D3286537

fbshipit-source-id: 1057fea7baa8f42470239c78834c5af41f1450ac
2016-05-10 23:20:21 -07:00
Elijah Windsor 574e81e8cb Adding clapit to showcase
Summary:
Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:

(You can skip this if you're fixing a typo or adding an app to the Showcase.)

Explain the **motivation** for making this change. What existing problem does the pull request solve?

Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide).

For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/mas
Closes https://github.com/facebook/react-native/pull/7494

Differential Revision: D3286536

fbshipit-source-id: cb6be7717d977017ae7b1a297543c3269bceb53b
2016-05-10 23:17:23 -07:00
Dan Seethaler a053f53860 Typo fix.
Summary: Closes https://github.com/facebook/react-native/pull/7487

Differential Revision: D3286538

fbshipit-source-id: 4c989e7a444b1f93ac4bc27131e25b93305b1148
2016-05-10 22:45:21 -07:00
nemothecapt df9a2b20a1 React.PropTypes.oneOf accepts array as parameter, non-array given
Summary:
React.PropTypes.oneOf accepts array as parameter, non-array given. this make the app throw a warning.
Closes https://github.com/facebook/react-native/pull/7498

Differential Revision: D3285737

fbshipit-source-id: 3d86b4b36d696e054f9225d7e3a9eddf91477813
2016-05-10 21:04:23 -07:00
Ben Alpert a95405f419 Update to React 15.1.0-alpha.1
Reviewed By: zpao

Differential Revision: D3283057

fbshipit-source-id: c37ea3818597e5c0f37ed3c7502c791c02183ea8
2016-05-10 15:58:24 -07:00
Olivier Notteghem 68b2f49a31 Improves react image loading handlers to mimic iOS behavior
Reviewed By: vjeux

Differential Revision: D3283011

fbshipit-source-id: acb04f8b2ccbab99a59040a7ca70aa46cbf73faa
2016-05-10 14:17:27 -07:00
Joel Marcey c1f0ce283c Getting Started Cleanup
Summary:
1. Remove note about upgrading Homebrew packages. That was kind of noisy.
2. Use local images instead of those stored on Akamai.
3. Add wording for modifying test project about actually opening a file.
4. Add note about keeping initial defaults for Android Studio install in tact.

Tested site locally. Images and new wording appeared as expected.
Closes https://github.com/facebook/react-native/pull/7477

Reviewed By: vjeux

Differential Revision: D3281639

Pulled By: JoelMarcey

fbshipit-source-id: ca956d97293ac3793431cb54f3560ee3e52c0dce
2016-05-10 11:17:27 -07:00
Martin Kralik 1b00534793 improve view clipping
Summary:
Previously we had an issue where we would clip visible views during an animation, like swiping back a VC.
The root cause was mismatch between a view's frame and what is visible on screen. This happens because during an animation the frame (and other properties) of the animated view has final values, even that it's not yet rendered at that position.

This diff fixes this issue by not looking for a clippingView above the react root view, since the animation for VC transitions happens at view higher above the root view.

Reviewed By: javache

Differential Revision: D3281655

fbshipit-source-id: 996b1a9f223c5b2274dd3d7c05b8936612af05ba
2016-05-10 10:51:24 -07:00
David Aurelio ce0594dece Add forwarding modules for modules required by React
Reviewed By: bestander

Differential Revision: D3281736

fbshipit-source-id: e061d3ec0b0d9ca4d3ee8234757457617dd6c985
2016-05-10 10:31:24 -07:00
David Aurelio d5ceb4175b Allow already loaded modules to be require’d by name string in dev mode
Summary:
The code to require modules by their name (rather than their numeric ID) was buggy, because it didn’t check whether the module factory was already executed and the module already existed.

This diff checks the already loaded modules, too, when loading modules by name.

Reviewed By: lexs

Differential Revision: D3281350

fbshipit-source-id: cef236e152fe5484f21c877d6cee37433fa11c76
2016-05-10 09:37:20 -07:00
Andy Street f1d12f6b38 Make runJSBundle synchronous
Summary: This is called on the JS thread so we don't need to post a runnable.

Reviewed By: lexs

Differential Revision: D3281388

fbshipit-source-id: ff292a710442c0b84c6ee31a2644a5c2ea650dc9
2016-05-10 09:21:24 -07:00
Andy Street 1c31aea83f Update "This shouldn't be possible. Congratulations." error message
Summary: Apparently this *is* possible. Congratulations to me.

Reviewed By: nicklockwood

Differential Revision: D3281361

fbshipit-source-id: 53c6ab83d959aa8a77880f4f772a7a7d7ccf4950
2016-05-10 09:17:23 -07:00
Tadeu Zagallo 65af925501 Avoid text relocations on arm trampoline
Summary:
fixes #7337

`ldr reg, =symbol` will cause a text relocation, which will fail to link if that's not allowed (which is the default for dynamic libraries).

Reviewed By: jspahrsummers

Differential Revision: D3281518

fbshipit-source-id: a057b4b9a54d568c8753a9dcd00365a86c7d93b0
2016-05-10 09:09:19 -07:00
Konstantin Raev 4c3286f2a2 Made UIManager set displayContstants
Reviewed By: andreicoman11

Differential Revision: D3281309

fbshipit-source-id: f5568f2127e7e26dcbd77b3845092346ac06c0db
2016-05-10 08:34:31 -07:00
Nurym Kudaibergen ff7f260ce9 Updated Babel supported transformations link
Summary:
Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:

(You can skip this if you're fixing a typo or adding an app to the Showcase.)

Explain the **motivation** for making this change. What existing problem does the pull request solve?

Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide).

For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/mas
Closes https://github.com/facebook/react-native/pull/7485

Differential Revision: D3281418

Pulled By: vjeux

fbshipit-source-id: 287b66f1ed9690bf488cf646f8eaf02e2956caa5
2016-05-10 06:53:20 -07:00
Andy Street 6ec5b953cb Red box c++ exceptions thrown while executing sync hooks
Summary: Also adds support for creating JS Error objects. When we clean up the way global functions are installed on JSC, we'll be able to use this pattern to auto-catch all c++ exceptions at this level.

Reviewed By: lexs

Differential Revision: D3276663

fbshipit-source-id: bf3561690013019a25e2be5a4d1b69ba9e002c72
2016-05-10 05:03:30 -07:00
Pieter De Baets ca1caf2713 Enable no-duplicate-props lint rule
Reviewed By: davidaurelio

Differential Revision: D3281234

fbshipit-source-id: 1b382694ad7a647464add4d1c856e7dde472205d
2016-05-10 05:00:24 -07:00
Tomas Roos cd9271567f Add flow directive to index.android.js
Summary:
iOS template contains flow directive in top comment. Android should too for consistency.
Closes https://github.com/facebook/react-native/pull/7482

Differential Revision: D3281131

fbshipit-source-id: d9a77196faed5e41753d09768721e1da1e6cc60a
2016-05-10 03:11:21 -07:00
Jimmy Mayoukou d3e5a96b15 Fix tintColor prop platform in Image
Summary:
`tintColor`, even in style, works on Android; this must be some old comment but that confused me when reading the docs.

**Test plan:**
- Launched website locally
Closes https://github.com/facebook/react-native/pull/7465

Differential Revision: D3278166

fbshipit-source-id: 4e986e0f2a9ba05de4404b93d28687596fbb4fad
2016-05-09 15:29:24 -07:00
James Ide 6d1790a625 Add li.st (Android) to the showcase
Summary: Closes https://github.com/facebook/react-native/pull/7478

Differential Revision: D3277536

fbshipit-source-id: 92a48007a5bd0c5e981c9b8956a045b7af4b1b40
2016-05-09 13:38:24 -07:00
Martin Konicek 128d93b8aa Revert Android websocket: Include cookies with request
Summary:
This reverts the commit https://github.com/facebook/react-native/commit/bf8b5499bba92e607be40c0ae0f12feffbfcc706
Pull request: https://github.com/facebook/react-native/pull/6851
Internal Phabricator revision: D3257466

See the pull request for discussion.

Reviewed By: bestander

Differential Revision: D3277433

fbshipit-source-id: 623f93e1bce47ac156ffab154c57495b85ffa936
2016-05-09 13:37:29 -07:00
Nikhilesh Sigatapu d4f6f61a96 Increase the stack size for the React queue on older Android phones
Summary:
Older Android devices don't have a large stack by default and with about 16 levels of views the app will crash. Modern Android devices are fine so we can continue to use the system defaults while using a 2MB stack on older Android devices.
Closes https://github.com/facebook/react-native/pull/7416

Differential Revision: D3276805

Pulled By: astreet

fbshipit-source-id: a7b31a1be62e13f333148ca0908eb01a11aa4562
2016-05-09 11:48:23 -07:00
Nick Lockwood 963a53b1a7 Fixed XHR tests
Summary:
The XMLHttpRequest jest tests were attempting to call a private method in XMLHttpRequestBase.js (denoted by an _ prefix).

JS doesn't actually have any language-level support for private methods, the use of _ prefix is just a convention. But to prevent casually calling private methods externally, we have a transform that mangles the names of prefixed methods so that that attempting to call them will fail.

Using a double _ bypasses this name-mangling mechanism, while still making it clear that the method is intended to be private.

Reviewed By: javache

Differential Revision: D3276261

fb-gh-sync-id: e0c17e1003d2df09d1a16f78ae9d95bef923d74e
fbshipit-source-id: e0c17e1003d2df09d1a16f78ae9d95bef923d74e
2016-05-09 10:36:24 -07:00
Will Fong e737891242 Add troubleshooting and modification for linux
Summary:
Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:

(You can skip this if you're fixing a typo or adding an app to the Showcase.)

Explain the **motivation** for making this change. What existing problem does the pull request solve?

Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide).

For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/mas
Closes https://github.com/facebook/react-native/pull/7471

Differential Revision: D3276360

Pulled By: JoelMarcey

fb-gh-sync-id: 30edd7086a3c4b88695dc91af76ef56d43306ce9
fbshipit-source-id: 30edd7086a3c4b88695dc91af76ef56d43306ce9
2016-05-09 09:58:19 -07:00
Atif Mansoor 146df61775 Fix bug on GettingStarted.md page when clicking nav buttons in FF
Summary:
Hi,
The [commit](https://github.com/facebook/react-native/commit/156d3ed7a290a7d0576c7e6d2b3fc03681d71ee9?_pjax=%23js-repo-pjax-container) by JoelMarcey is much appreciated.  However, when you click on the nav buttons in Firefox (v46.0.1, I'm on El-Capitan), it will switch the content but also navigate you to the React-Native homepage.  This doesn't happen in Chrome, so that's how it probably slipped through.
I propose these changes to fix that.

**Test plan**
Tested locally on FF and Chrome on El-Capitan
Closes https://github.com/facebook/react-native/pull/7435

Differential Revision: D3276285

Pulled By: vjeux

fb-gh-sync-id: c9a14059e609297fe273d02fe6c0a5f98ec7060c
fbshipit-source-id: c9a14059e609297fe273d02fe6c0a5f98ec7060c
2016-05-09 09:42:21 -07:00
Eric Vicenti b24d471fb2 Backed out D3033933
Reviewed By: hedgerwang

Differential Revision: D3273603

fb-gh-sync-id: e013fd3efd793960d076d2a01447b2a11f483ede
fbshipit-source-id: e013fd3efd793960d076d2a01447b2a11f483ede
2016-05-09 08:33:18 -07:00
Pieter De Baets 57ceeafd4f Move UI-specific logic from NativeModules to UIManager
Summary: Move all requires of UIManager to UIManager.js, so we can load the view manager configuration lazily when UIManager is required.

Reviewed By: majak

Differential Revision: D3270147

fb-gh-sync-id: 8208ee8d5919102ea5345e7031af47ee78162fe0
fbshipit-source-id: 8208ee8d5919102ea5345e7031af47ee78162fe0
2016-05-09 08:21:19 -07:00
Nick Lockwood ed1ee9bc0f Fixed crash due to inserting a nil object into an array
Summary: Fixed a crash where making a network request with unrecognized/unsupported scheme would crash by attempting to insert a nil RCTNetworkTask into an array.

Reviewed By: javache

Differential Revision: D3275810

fb-gh-sync-id: be208c6bf87d882209901b850dcc25da4ba79a08
fbshipit-source-id: be208c6bf87d882209901b850dcc25da4ba79a08
2016-05-09 08:18:47 -07:00
Jesse Sessler 191d278fda Modal Status Bar Translucent
Summary:
Currently the Modal component on Android is rendered below the Status Bar, which changes it's color to grey, and in the UIExplorer example the backdrop is just formatted to look the same color. In some scenarios users may want to preserve the color of their status bar and make it look as though the modal is appearing on top. This PR allows for that.

This GIF shows current behavior and new behavior with the translucentStatusBar prop set to true.

![](http://g.recordit.co/BSX5g9obRC.gif)

I've updated the UIExplorer app to demonstrate and the docs as shown below

![image](https://cloud.githubusercontent.com/assets/4265163/14742854/500e1292-086c-11e6-9275-71808b0cbed7.png)

Thanks!
Closes https://github.com/facebook/react-native/pull/7157

Differential Revision: D3264497

Pulled By: dmmiller

fb-gh-sync-id: 61346d99414d331d3420f44a4c5f6341b0973be6
fbshipit-source-id: 61346d99414d331d3420f44a4c5f6341b0973be6
2016-05-09 07:08:22 -07:00
Alexander Blom abca466037 Remove name property from native modules
Reviewed By: javache

Differential Revision: D3275674

fb-gh-sync-id: 7c100c78576b48f3163c37b93499dd54da73b96c
fbshipit-source-id: 7c100c78576b48f3163c37b93499dd54da73b96c
2016-05-09 05:46:23 -07:00
Will Fong 28cc8c9776 Fix typo (.bash instead of .bashrc)
Summary: Closes https://github.com/facebook/react-native/pull/7464

Differential Revision: D3275748

fb-gh-sync-id: a59e6f54425906c4018f122174a86d80afa021eb
fbshipit-source-id: a59e6f54425906c4018f122174a86d80afa021eb
2016-05-09 05:42:19 -07:00
Pieter De Baets d7de68241f Index UIManager.Commands correctly
Reviewed By: nicklockwood

Differential Revision: D3275652

fb-gh-sync-id: a38e9cf6a46f2aa373b4948aa419b42af402c784
fbshipit-source-id: a38e9cf6a46f2aa373b4948aa419b42af402c784
2016-05-09 05:25:23 -07:00
Andy Street 54f7ae1c02 Force react applications to be explicitly unmounted
Summary:
We previously were unmounting the react application unconditionally when the ReactRootView#onDetachedFromWindow. This is nice in that it automatically allows us to reclaim memory, but there are many scenarios where a ReactRootView can be embedded in another piece of UI that detaches its children as part of its normal function (e.g. ListView, RecyclerView, ViewPager, etc).

As such, we will now enforce that the hosting Activity/Fragment/??? explicitly calls unmountReactApplication in the same way it calls startReactApplication. For Applications extending ReactActivity/AbstractReactActivity, this will happen automatically in onDestroy.

Reviewed By: foghina

Differential Revision: D3265161

fb-gh-sync-id: 4d49b0c41256213f00874f57e784aa8741dbf394
fbshipit-source-id: 4d49b0c41256213f00874f57e784aa8741dbf394
2016-05-09 04:09:29 -07:00
UnoDeTantos f7ce0c1c2f Adds progressViewOffset to RefreshControl
Summary:
Add the possibility to define a progress view top offset to RefreshControl on android. As i comment in #6740, contentInset does the trick on IOS.

Looking android documentation seems that exists a possible solution:
http://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html#setProgressViewOffset(boolean, int, int)

This pull request implement that but keeping it simple, only a top offset.

For example, now we could put navigation bar over the scrollview (or listview) and define a progressViewOffset on RefreshView in order to start behind the navigation. At this point we could make some kind of coordinator layout to hide/show navigation on scroll.

To maintain the default behavior, start point is equal to start point minus progress circle diameter in order to create that progress circle before the start point.
Closes https://github.com/facebook/react-native/pull/6759

Differential Revision: D3240664

Pulled By: mkonicek

fb-gh-sync-id: ccf866272e871811c1c6dcc2a34f5c217967feee
fbshipit-source-id: ccf866272e871811c1c6dcc2a34f5c217967feee
2016-05-09 01:51:22 -07:00
MIYOKAWA, Nobuyoshi 2953a1a02e replace XCode to Xcode.
Summary:
Fixing very trivial typos.  'XCode' should be 'Xcode'.
Closes https://github.com/facebook/react-native/pull/7451

Differential Revision: D3275295

fb-gh-sync-id: 440972364229ca980e5211277baa342a20ac5d94
fbshipit-source-id: 440972364229ca980e5211277baa342a20ac5d94
2016-05-08 23:14:53 -07:00
Janic Duplessis 9aa37e8fb2 Make RefreshControl properly controlled by JS
Summary:
There was an issue with the way we made `RefreshControl` a controlled component when react doesn't render synchronously. This fixes the issue by using the same technique used in the commit 0cd2904b23 for`PickerAndroid`

**Test plan (required)**

Tested normal behaviour and tested that if not setting the `refreshing` prop to `true` during the `onRefresh` callback that the `RefreshControl` stops refreshing immediately. Also made sure that `setNativeProps` is only called if needed when the native refreshing state is not in sync with JS.

Fix #7414
Closes https://github.com/facebook/react-native/pull/7445

Differential Revision: D3274981

fb-gh-sync-id: a1b9d46329f552984e33d11fa0e29ad6da689511
fbshipit-source-id: a1b9d46329f552984e33d11fa0e29ad6da689511
2016-05-08 17:03:18 -07:00
Alessandro Nadalin b88d9d2380 Fixed typo
Summary: Closes https://github.com/facebook/react-native/pull/7448

Differential Revision: D3274779

fb-gh-sync-id: ae50ff1939778663af2f212fd510ed04ea640bb2
fbshipit-source-id: ae50ff1939778663af2f212fd510ed04ea640bb2
2016-05-08 11:47:49 -07:00
wusuopu 17f08070e8 Animated: Implement toJSON method of Animated
Summary:
Fix issue https://github.com/facebook/react-native/issues/7441
Closes https://github.com/facebook/react-native/pull/7442

Differential Revision: D3274594

fb-gh-sync-id: 720d7dcdaa97a2ee5235547bad6ba020398b57d2
fbshipit-source-id: 720d7dcdaa97a2ee5235547bad6ba020398b57d2
2016-05-08 01:09:21 -07:00
Ben Alpert 0cd2904b23 Fix Android picker controlling
Reviewed By: mkonicek

Differential Revision: D3261602

fb-gh-sync-id: da67d28420c5d855d672b481deeb92fa8b5d017c
fbshipit-source-id: da67d28420c5d855d672b481deeb92fa8b5d017c
2016-05-07 15:52:19 -07:00
Milan Pavlik 0fab691e6f Remove default argument syntax on getVersionedGithubPath()
Summary:
Removes ES6 syntax of default arguments to fix website building as per #7434. [More info](https://github.com/facebook/react-native/pull/7434).
Closes https://github.com/facebook/react-native/pull/7439

Differential Revision: D3274206

fb-gh-sync-id: 3a8f5950ead91cf59c27fd384e97bf9587005398
fbshipit-source-id: 3a8f5950ead91cf59c27fd384e97bf9587005398
2016-05-07 14:10:33 -07:00
Milan Pavlik 1d101f4236 Add versions to GitHub source links in documentation. #7428
Summary:
Documentation references to source code on GitHub should point to their respective versions in order to avoid confusion and prevent broken links when the master branch differs from the currently viewed version. [More details](https://github.com/facebook/react-native/issues/7428)

Originally links pointed to a hardcoded master branch on GitHub. The change augments the GitHub url to point to a respective branch of the code. The new urls are as follows:

next: `https://github.com/facebook/react-native/blob/master/<path>`
stable: `https://github.com/facebook/react-native/blob/0.25-stable/<path>`

The website module currently does not have a unit test setup. As a result, I performed the following tests:

1. Start the server with `RN_VERSION=next` and verified all links point to _master_
<img width="945" alt="screen shot 2016-05-07 at 14 10 58" src="https://cloud.githubusercontent.com/assets/1419286/15092307/ee66fb32-145d-11e6-92a0-d03169e517c2.png">
2. Start the server
Closes https://github.com/facebook/react-native/pull/7434

Differential Revision: D3274000

Pulled By: vjeux

fb-gh-sync-id: 70d766984d6b0835f0a18928d6831fd2c82b6c7b
fbshipit-source-id: 70d766984d6b0835f0a18928d6831fd2c82b6c7b
2016-05-07 08:36:21 -07:00
Alexander Blom c6d3ca858e Avoid bounce to same message queue
Reviewed By: astreet

Differential Revision: D3269661

fb-gh-sync-id: b22ea45de9e9ddbe8471f10e9c475a5746aec34e
fbshipit-source-id: b22ea45de9e9ddbe8471f10e9c475a5746aec34e
2016-05-07 02:36:28 -07:00
Sokovikov ff431734a4 Revert "Revert "fix border style without borderRadius""
Summary: Closes https://github.com/facebook/react-native/pull/7389

Differential Revision: D3269828

Pulled By: mkonicek

fb-gh-sync-id: 896789fcc76953a035cf8701d3a90211b92b6b80
fbshipit-source-id: 896789fcc76953a035cf8701d3a90211b92b6b80
2016-05-06 09:23:22 -07:00
prabakarviji d03d455cc4 Add Reach24 App to React Native showcase
Summary:
Submitting PR for adding Reach24 App to React Native showcase
Closes https://github.com/facebook/react-native/pull/7294

Differential Revision: D3245068

Pulled By: mkonicek

fb-gh-sync-id: a3eb316279f1c96dd2da33566730f690229405cf
fbshipit-source-id: a3eb316279f1c96dd2da33566730f690229405cf
2016-05-06 08:56:18 -07:00
David Aurelio 0c90ab493f Add Random Access Module file names to stack traces
Reviewed By: javache

Differential Revision: D3269450

fb-gh-sync-id: 41b2013d4422c2bcafa7628321d4626fd40aac5d
fbshipit-source-id: 41b2013d4422c2bcafa7628321d4626fd40aac5d
2016-05-06 05:54:23 -07:00
Alexander Blom 097f59112f Make MessageQueue.RemoteModules lazy
Reviewed By: javache

Differential Revision: D3252400

fb-gh-sync-id: 11a31fd2e1e41c6a3ad538b08ef177207780bd88
fbshipit-source-id: 11a31fd2e1e41c6a3ad538b08ef177207780bd88
2016-05-06 04:14:23 -07:00
Adam Comella deef8aade2 ScrollView: Always fire onScroll event for the resting scroll position
Summary:
When throttling scroll events with `scrollEventThrottle`, `onScroll`
is not guaranteed to be fired for the final scroll position
of the `ScrollView`. This can cause a component to render UI that
is consistent with the resting scroll position of the `ScrollView`.

This commit guarantees that an `onScroll` event will be fired for
the resting scroll position of the `ScrollView`.

**Test plan (required)**

Verified commit fixes a reduced repro. Also tested fix in a larger app.

Adam Comella
Microsoft Corp.
Closes https://github.com/facebook/react-native/pull/7366

Differential Revision: D3269303

Pulled By: javache

fb-gh-sync-id: f68ecb7e9c18d1ac255c6f872fb7eb4aadd07799
fbshipit-source-id: f68ecb7e9c18d1ac255c6f872fb7eb4aadd07799
2016-05-06 03:19:27 -07:00
Adam Comella 4d2c72b977 NavigatorIOS: Expose interactivePopGestureEnabled property
Summary:
Previously, the back swipe navigation gesture would be enabled when the navigation bar is shown and disabled when the navigation bar is hidden.

This change enables developers to control the back swipe gesture independently of the visibility of the navigation bar. An example use case would be that an app wants to render a custom navigation bar so it sets `navigationBarHidden` to true and it wants to enable the back swipe gesture so it sets `interactivePopGestureEnabled` to true.

**Test plan (required)**

- Created a test app to verify setting `interactivePopGestureEnabled` to `true` and `false` with the navigation bar both hidden and shown.
- Verified prop works in a larger app.

Adam Comella
Microsoft Corp.
Closes https://github.com/facebook/react-native/pull/7369

Differential Revision: D3269304

Pulled By: javache

fb-gh-sync-id: ec4324f6517cec4b4fc4f62c4394dc9208a8af6a
fbshipit-source-id: ec4324f6517cec4b4fc4f62c4394dc9208a8af6a
2016-05-06 03:18:20 -07:00
Fred Liu 2132ad1441 Swipeable list view enhancements
Summary:
- Removes "flickering" on first load
- Takes in Map<rowID, slideoutView> to display customs slideout view in `SwipeableRowListView`

Reviewed By: furdei

Differential Revision: D3267262

fb-gh-sync-id: a89806138b9172b3c184cc117504e205632a36d0
fbshipit-source-id: a89806138b9172b3c184cc117504e205632a36d0
2016-05-05 20:45:24 -07:00
Joel Marcey 156d3ed7a2 Refactor getting started navigation and information based on platform
Summary:
This simplifies the Quick Start section of the React Native documentation into
two pages. A Getting Started and a Tutorial.

The Getting Started page uses some CSS and Javascript magic (thanks vjeux for
the initial infra for this!!) to allow selection between platforms and have
instructions for Getting Started with React Native be shown according to the
selection -- all within the same page, realtime. A much cleaner interface.

I have made a pretty large presentation and information overhaul for each
platform as well. For example, requiring Android Studio for Android
development to make the Android SDK and build tools installation easier.
I added more screenshots to the Android sections since they are more complicated
than the more straightforward iOS. Screenshots for Android for Windows, Linux and
iOS are available now.

Some of the other pages such as `GettingStartedOnLinux` are now obsolete and deleted.

** Test Plan **

Tested locally and navigation works.
Closes https://github.com/facebook/react-native/pull/7418

Differential Revision: D3268621

Pulled By: vjeux

fb-gh-sync-id: 65f9181c9f959fadeffd254efddc5b64816eb1f4
fbshipit-source-id: 65f9181c9f959fadeffd254efddc5b64816eb1f4
2016-05-05 20:26:22 -07:00
Joel Marcey 359b381796 Revert "Refactor getting started navigation and information based on platform"
This reverts commit 0ea020d823.

fb-gh-sync-id: f46c4a1f927128fd34c2f8ce4be4c275468f2355
fbshipit-source-id: f46c4a1f927128fd34c2f8ce4be4c275468f2355
2016-05-05 18:49:33 -07:00
Joel Marcey 0ea020d823 Refactor getting started navigation and information based on platform
Summary:
This simplifies the Quick Start section of the React Native documentation into
two pages. A Getting Started and a Tutorial.

The Getting Started page uses some CSS and Javascript magic (thanks @vjeux for
the initial infra for this!!) to allow selection between platforms and have
instructions for Getting Started with React Native be shown according to the
selection -- all within the same page, realtime. A much cleaner interface.

I have made a pretty large presentation and information overhaul for each
platform as well. For example, requiring Android Studio for Android
development to make the Android SDK and build tools installation easier.
I added more screenshots to the Android sections since they are more complicated
than the more straightforward iOS. Screenshots for Android for Windows, Linux and
iOS are available now.

Some of the other pages such as `GettingStartedOnLinux` are now obsolete and deleted.

Test Plan:
Video demo (it's a 1m20s or so, peruse at your pace): https://www.facebook.com/pxlcld/nKW3

Inspection

Reviewers: lacker, bestander, mkonicek, vjeux

Reviewed By: vjeux

Subscribers: cdykes, vjeux, mjohnston, sema, balpert, tomocchino, hramos, caabernathy, jpearce

Differential Revision: https://phabricator.intern.facebook.com/D3265822

Signature: t1:3265822:1462479878:5453ec81808b59fd71c288b6cc79268fccd921bc
2016-05-05 18:40:47 -07:00
Jeff Bowen b1892e6099 Explain custom configuration debugging gotcha
Summary:
Debugging tools won't work if you're using a custom configuration and Cocoapods and didn't specify the configuration to be a `debug` configuration using the `xcodeproj` setting in your Podfile. When integrating React Native into an existing Xcode project, this is a common situation to encounter.

See https://github.com/facebook/react-native/issues/2246#issuecomment-217187621
Closes https://github.com/facebook/react-native/pull/7407

Differential Revision: D3268032

fb-gh-sync-id: f46c4a1f927128fd34c2f8ce4be4c275468f2355
fbshipit-source-id: f46c4a1f927128fd34c2f8ce4be4c275468f2355
2016-05-05 18:00:19 -07:00
Eric Vicenti 14eb427a80 Kill NavigationExperimental Containers
Summary:
The containers in NavigationExperimental are not appropraite because the state should be held by the app's architecture, be it redux, flux, or simple component state.

This diff moves the examples over to simple component state, but there are several other examples of how to use NavigationAnimatedView and the navigation reducers with redux:

- https://github.com/jlyman/RN-NavigationExperimental-Redux-Example
- Switching the f8 app with redux to navigation experimental: https://github.com/fbsamples/f8app/pull/14

Reviewed By: hedgerwang

Differential Revision: D3219911

fb-gh-sync-id: eb0b323e2c165c32027fbd00dc6197ad441d6552
fbshipit-source-id: eb0b323e2c165c32027fbd00dc6197ad441d6552
2016-05-05 16:52:19 -07:00
Russell Porter a412fd1504 Remove unused generated strings.xml files
Differential Revision: D3265330

fb-gh-sync-id: a0ca96e4c3db63051dc3c09ca99d492fb3624b51
fbshipit-source-id: a0ca96e4c3db63051dc3c09ca99d492fb3624b51
2016-05-05 16:08:26 -07:00
Sebastian Gronewold 07767e86a3 Navigator - velocity override
Summary:
This code change allows changing the transition velocity of the navigator
when switching views.

Whats the use case? If you have different types of
animations your animations might also need different transition velocities.
Closes https://github.com/facebook/react-native/pull/5439

Differential Revision: D3264719

Pulled By: ericvicenti

fb-gh-sync-id: 1bc0a1191ed3bd51b624acd161c3ee77fd748d72
fbshipit-source-id: 1bc0a1191ed3bd51b624acd161c3ee77fd748d72
2016-05-05 15:06:47 -07:00
Konstantin Raev 1958e93f3a Fixed instrumentation test runner OK sniffing
Summary:
Fixed regex that looks for OK sign

**Test plan (required)**

- circle CI results
Closes https://github.com/facebook/react-native/pull/7410

Differential Revision: D3265016

fb-gh-sync-id: 1eba7297116d31f8bf3393733141058cf6a7454a
fbshipit-source-id: 1eba7297116d31f8bf3393733141058cf6a7454a
2016-05-05 12:54:02 -07:00
Eric Vicenti 472f815e45 NavigationExperimental: Add key to NavigationSceneRendererProps
Summary:
This should simplify the renderScene usage a bit because the react key is required and this will make sure they are rendered with the right key automatically.

It changes the string to make sure people do not rely on this API for anything else.

Reviewed By: javache

Differential Revision: D3033933

fb-gh-sync-id: 036424af28693be32c3a3290f5c6667a6a6a04ac
fbshipit-source-id: 036424af28693be32c3a3290f5c6667a6a6a04ac
2016-05-05 11:58:26 -07:00
Fred Liu 2a7f6aeda7 Swipeable row container
Reviewed By: hedgerwang

Differential Revision: D3263983

fb-gh-sync-id: f77bb3dd7771c16e1f26398171b7784e3d89cb1d
fbshipit-source-id: f77bb3dd7771c16e1f26398171b7784e3d89cb1d
2016-05-05 11:49:29 -07:00
Hedger Wang b1cd1cba32 Fix propType check for NavigationCard.
Summary:
the prop type check for NavigationCard -> SceneView -> sceneRendererProps is wrong.
Fix it.

Reviewed By: ericvicenti

Differential Revision: D3265049

fb-gh-sync-id: 16e89c932f0f962db4b6a647d62d255ba98f6156
fbshipit-source-id: 16e89c932f0f962db4b6a647d62d255ba98f6156
2016-05-05 11:39:29 -07:00
Konstantin Raev b824722501 Fixed nvm for travis CI
Summary:
NVM started failing on Travis
Closes https://github.com/facebook/react-native/pull/7411

Differential Revision: D3265199

fb-gh-sync-id: 6c80210cafb0ec6510073ce913a36eb7edcac2c6
fbshipit-source-id: 6c80210cafb0ec6510073ce913a36eb7edcac2c6
2016-05-05 11:25:28 -07:00
Hedger Wang ef44781f3d Kill NavigationLegacyNavigator.
Summary:
NavigationLegacyNavigator was originally created to help people to migrate to the new
navigation library without API changes. Therefore we'd have to port all the old APIs that
don't necessarily seem align well with the new navigation library.

Consider the production usage of NavigationLegacyNavigator does not exist, it's better
to kill it and we'd just rename the `Navigator` to `NavigatorDeprecated` later instead.

Reviewed By: ericvicenti

Differential Revision: D3263704

fb-gh-sync-id: a851fda1516d694cb7d119f5a1344f8fc676f7fd
fbshipit-source-id: a851fda1516d694cb7d119f5a1344f8fc676f7fd
2016-05-05 10:56:41 -07:00
Jimmy Mayoukou dd8caf4cf2 Implement returnKeyType/returnKeyLabel on Android
Summary:
This PR implements [`returnKeyType`](http://facebook.github.io/react-native/docs/textinput.html#returnkeytype) on Android.

It is implemented with [`EditText.setImeOptions()`](http://developer.android.com/reference/android/widget/TextView.html#setImeOptions(int)) that allows us to specify options on an input, in this case change the return button icon. To be noted that it is also possible to specify a string instead of an icon with [`EditText.setImeActionLabel()`](http://developer.android.com/reference/android/widget/TextView.html#setImeActionLabel(java.lang.CharSequence, int)) while being 2 different things I added both of these behaviors in this PR since it was mostly at the same place/component.

**Problems encountered :**

- All the `ReactEditText`s were set to `IME_ACTION_DONE` by default ([reference](https://github.com/Bhullnatik/react-native/blob/a2157dbbe06e10e628900e9d4443948893623126/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java#L78)). I'm not sure
Closes https://github.com/facebook/react-native/pull/6905

Differential Revision: D3264755

Pulled By: dmmiller

fb-gh-sync-id: 4a090e31b620a245847c06ba1895cfea02e88d0f
fbshipit-source-id: 4a090e31b620a245847c06ba1895cfea02e88d0f
2016-05-05 10:22:19 -07:00
Christine Abernathy 02af7b57d0 Display platform-specific examples
Summary:
Rebased https://github.com/facebook/react-native/pull/7048

Sending a new PR to check that tests pass.

After adding simulator examples, it appears that some don't show up, ex:
http://facebook.github.io/react-native/docs/text.html#content

This is due to the fact that these examples are split into two platform-specific files. For this reason, the example code isn't being shown. The examples are also missing for TextInput.

**Test plan**

`cd website; npm install; npm start`

![screen shot 2016-05-05 at 12 25 41 pm](https://cloud.githubusercontent.com/assets/346214/15042571/08ee77e8-12bd-11e6-98a6-967dc5fefa07.png)

![screen shot 2016-05-05 at 12 25 20 pm](https://cloud.githubusercontent.com/assets/346214/15042573/119778f4-12bd-11e6-8cdd-fbf217223d45.png)

![screen shot 2016-05-05 at 12 25 35 pm](https://cloud.githubusercontent.com/assets/346214/15042570/071ae992-12bd-11e6-9cf6-5aaba5e7fa17.png)
Closes https://github.com/facebook/react-native/pull/7406

Differential Revision: D3264567

Pulled By: mkonicek

fb-gh-sync-id: cfb73eaed56a7b5c6c84ce313e113393d152e9a1
fbshipit-source-id: cfb73eaed56a7b5c6c84ce313e113393d152e9a1
2016-05-05 09:10:26 -07:00
Damien Varron 75c71cfbff Add instruction on how to have access to the bot in IssueGuidelines.md
Summary: Closes https://github.com/facebook/react-native/pull/7402

Differential Revision: D3264577

fb-gh-sync-id: 10c0e1f9f8eedf0b53e1b6c93f6aff61883169f6
fbshipit-source-id: 10c0e1f9f8eedf0b53e1b6c93f6aff61883169f6
2016-05-05 08:46:54 -07:00
Konstantin Raev 457e348081 Open sourced SwipeRefreshLayoutRecordingModule
Reviewed By: dmmiller

Differential Revision: D3264212

fb-gh-sync-id: 8790200a0d3c47050d6a91c906854b3a55640829
fbshipit-source-id: 8790200a0d3c47050d6a91c906854b3a55640829
2016-05-05 08:17:34 -07:00
Andy Street a1ba0918ab Protect against JS module call race condition while initializing react instance
Summary: Fixes a race condition where JS module functions could be called in between ##initializeWithInstance(catalystInstance);## and ##CatalystInstance#runJSBundle##, before the BatchedBridge in JS was set up. We now guarantee that all JS module methods that are called after `ReactContext#hasActiveCatalystInstance()` returns true will have the batched bridge created and ready to use.

Reviewed By: lexs

Differential Revision: D3258651

fb-gh-sync-id: 66e66533cc86d185e7c865376d6a5cdc6520d2d4
fbshipit-source-id: 66e66533cc86d185e7c865376d6a5cdc6520d2d4
2016-05-05 05:52:19 -07:00
Andy Street a69b883537 Simplify ReactRootView
Summary:
I need to do some refactoring of what happens when a ReactRootView is attached/detached from the window. In doing so, I found ReactRootView is way more complicated than it needs to be and very hard to understand. This simplifies it while maintaining functionality.

Basically:
- When you start a react application, if the root view is measured, we attach it to the instance.
- If it wasn't measured, we do nothing
- In onMeasure, if we have a ReactInstanceManager, but are not attached to it, attach to it.

None of this needs to depend on whether we're attached to the window or whether an attach is scheduled afaict.

Reviewed By: dmmiller

Differential Revision: D3260056

fb-gh-sync-id: 974c19ed75a07f83299472f75346bd6be7414128
fbshipit-source-id: 974c19ed75a07f83299472f75346bd6be7414128
2016-05-05 05:07:28 -07:00
Dave Miller a3146e41a2 Add pagingEnabled to HorizontalScrollView
Summary:
This adds support for pagingEnabled to the HorizontalScrollView.

This is an initial implementation.

Because Android doesn't provide great details about what is happening with a scroll view after you are done touching it, we have some post touch handling.  This is kicked off either by touch up or a fling call.
Once we are doing that handling, we start a runnable that basically checks if we are still scrolling.  If we are, we just schedule that runnable again and check a frame later.  If we are done scrolling (no onScrollChanged since we last fired), we could be in one of two states, the fling is done or we are done snapping to the page boundary.  If we are in the fling done case, we then check if we need to scroll to a page boundary.  If so, we call smoothScrollTo and schedule ourself to check onScroll events again until done with that scroll.  If we are done with both (either we only did momentum scroll or we did that and then snapped to page), we can then fire the final event and stop checking.  This logic is all in handlePostTouchScrolling.

Because of the decision to only do page scrolling after momentum ends, we do allow you to scroll through with momentum a number of pages and the transition can be a little strange where it stops a sec and then slides to be page aligned.  As a follow up, we can probably smooth that up by changing the value we pass to super.fling() that would adjust it to be let momentum carry it to the page boundary.

Reviewed By: weicool

Differential Revision: D3207608

fb-gh-sync-id: 02f62970ed9a5e3a5f9c0d959402756bc4b3699e
fbshipit-source-id: 02f62970ed9a5e3a5f9c0d959402756bc4b3699e
2016-05-05 04:14:22 -07:00
Chris Geirman a26afd2d73 clarify highlightRow usage with UIExplorer example
Summary:
Explain the **motivation** for making this change. What existing problem does the pull request solve?

> From the existing documentation, it's not clear how one would make use of the highlightRow attribute of ListView.renderRow. Special thanks to halilb for his assistance.

Example: When "Adding a function to do X", explain why it is necessary to have a way to do X.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

> Here's a screenshot of the UIExplorer with a row highlighted (using the FB Blue Color)
> <img width="466" alt="2016-03-16_1944" src="https://cloud.githubusercontent.com/assets/1640318/13835020/49b5b38c-ebb1-11e5-98fa-17201fd3db92.png">

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTIN
Closes https://github.com/facebook/react-native/pull/6495

Differential Revision: D3264092

Pulled By: nicklockwood

fb-gh-sync-id: 9608e8a66387f527b872928a3595ca4d17736b82
fbshipit-source-id: 9608e8a66387f527b872928a3595ca4d17736b82
2016-05-05 03:43:47 -07:00
Fred Liu d037840158 SwipeableRow fix
Reviewed By: fkgozali

Differential Revision: D3261975

fb-gh-sync-id: f0c75aca1b16f810420a78271937777c5068c9d2
fbshipit-source-id: f0c75aca1b16f810420a78271937777c5068c9d2
2016-05-04 19:51:18 -07:00
Fred Liu 9b1a3c79e9 Swipeable row component
Reviewed By: furdei

Differential Revision: D3259527

fb-gh-sync-id: c2503476eb708add8753de8c00d0c3f5f3058474
fbshipit-source-id: c2503476eb708add8753de8c00d0c3f5f3058474
2016-05-04 15:49:20 -07:00
Pieter De Baets e76998531c upgrade graceful-fs to v4.1.3 (2)
Reviewed By: bestander

Differential Revision: D3260213

fb-gh-sync-id: a23a275d022006dd44c993217385a3fe6d6ff88c
fbshipit-source-id: a23a275d022006dd44c993217385a3fe6d6ff88c
2016-05-04 13:58:29 -07:00
Pieter De Baets c4e79a4f54 upgrade node-haste to v2.11.0
Reviewed By: davidaurelio

Differential Revision: D3259486

fb-gh-sync-id: 220378ca5d2b83d30b196a2a825a9becaf6b2b32
fbshipit-source-id: 220378ca5d2b83d30b196a2a825a9becaf6b2b32
2016-05-04 13:58:28 -07:00
Adam Ernst 3105dc8d6e Reverted commit D3259193
Reviewed By: davidaurelio

Differential Revision: D3259193

fb-gh-sync-id: 563d40261fb0b1c1def2e0c33f6197d858a1c54a
fbshipit-source-id: 563d40261fb0b1c1def2e0c33f6197d858a1c54a
2016-05-04 12:12:27 -07:00
David Aurelio 7518c2f827 upgrade graceful-fs to v4.1.3
Reviewed By: davidaurelio

Differential Revision: D3259193

fb-gh-sync-id: d3f956fb1dc555df1bd5938a428c61de99b2fd95
fbshipit-source-id: d3f956fb1dc555df1bd5938a428c61de99b2fd95
2016-05-04 11:27:19 -07:00
Konstantin Raev 110d1587ae Added stability to OSS instrumentation tests
Summary:
This change makes all instrumentation tests to be executed in sequence in independent retriable processes.

With a new test being open sourced recently our CI stability degraded.
This PR should bring back stability because tests won't affect each other and will have shorter lifetime
Closes https://github.com/facebook/react-native/pull/7353

Differential Revision: D3259081

fb-gh-sync-id: 48ccdb5dbd561d416526497ff474378db9ca3c60
fbshipit-source-id: 48ccdb5dbd561d416526497ff474378db9ca3c60
2016-05-04 10:59:33 -07:00
Pieter De Baets 2760df761d Cleanup InitializeJavascriptAppEngine
Reviewed By: davidaurelio

Differential Revision: D3235141

fb-gh-sync-id: 86fc844c5e9d9ea57d504696bac30671c2079e7a
fbshipit-source-id: 86fc844c5e9d9ea57d504696bac30671c2079e7a
2016-05-04 10:50:32 -07:00
Jean-Richard Lai 2310494f32 Fix distanceFilter caching for LocationObserver
Summary:
Allow changing distanceFilter after _locationManager has been initialized.

There is 2 reasons for this PR:
- When calling `getCurrentPosition`, `_observerOptions` is possibly not set properly ( as it is set only in `startObserving`), in this case it would have default values so `distanceFilter` will be 0 in this case ( which can be the right value but we should be more explicit about it, `kCLDistanceFilterNone` or `RCT_DEFAULT_LOCATION_ACCURACY`):
<img width="961" alt="screen shot 2016-04-14 at 8 44 09 pm" src="https://cloud.githubusercontent.com/assets/159813/14551465/6aa8791a-0288-11e6-9c98-1687357f8c2a.png">
- Another issue is that `distanceFilter` is cached so it can't be changed afterwards:

```javascript
let options;
options = {
  enableHighAccuracy: true,
  distanceFilter: 20,
};

this.watchId = this.geolocation.watchPosition(
  () => { },
  () => { },
  options,
);

// => sets distanceFilter to 20

this.geolocation.clearWatch(this.watchId);

options = {
  enableHighAccuracy:
Closes https://github.com/facebook/react-native/pull/6987

Differential Revision: D3258956

fb-gh-sync-id: 00a1d1b29d732a54cdc30e20a7a9a2de3dd9b725
fbshipit-source-id: 00a1d1b29d732a54cdc30e20a7a9a2de3dd9b725
2016-05-04 10:35:30 -07:00
Alexander Blom 041185edfd Let JS modules use strings instead of ids on Android
Reviewed By: nicklockwood

Differential Revision: D3229626

fb-gh-sync-id: f8b2e8c9fc0d25d67f623cdbbe9930a02a40d1de
fbshipit-source-id: f8b2e8c9fc0d25d67f623cdbbe9930a02a40d1de
2016-05-04 10:29:26 -07:00
David Aurelio 5071907b27 Add verbose module names to dev builds
Summary:
This intends to make the devx story better after switching to numeric module IDs:

- `require('<verbose-name>)` works again for dev builds
- In dev builds, Systrace will use the verbose names of modules for markers

Reviewed By: bestander

Differential Revision: D3253318

fb-gh-sync-id: 3425d5086ce28634653a6c8c7f5f11afa1614902
fbshipit-source-id: 3425d5086ce28634653a6c8c7f5f11afa1614902
2016-05-04 09:33:22 -07:00
Martin Konicek 00167e4956 Add scripts for running tests locally
Summary:
I was going to add an integration test and realized it would be useful to have an easy way to run these test locally. Added scripts and documented how to use them.

**Test plan**

`./scripts/run-android-local-unit-tests.sh`

<img width="786" alt="screen shot 2016-05-04 at 3 55 26 pm" src="https://cloud.githubusercontent.com/assets/346214/15018667/7f4981cc-1212-11e6-9fcb-12493c29015c.png">

`./scripts/run-android-local-integration-tests.sh`

<img width="772" alt="screen shot 2016-05-04 at 3 57 23 pm" src="https://cloud.githubusercontent.com/assets/346214/15018677/90b54810-1212-11e6-83d4-58530eb41d79.png">

Buck check by replacing `which buck` by `which duck` in the scripts:

<img width="805" alt="screen shot 2016-05-04 at 4 09 37 pm" src="https://cloud.githubusercontent.com/assets/346214/15018696/aa008262-1212-11e6-9a22-173507cd771f.png">

Checked the website renders fine:

`cd website; npm install; npm start`

![screen shot 2016-05-04 at 4 05 23 pm](https://cloud.githubusercontent.com/asse
Closes https://github.com/facebook/react-native/pull/7386

Differential Revision: D3258717

fb-gh-sync-id: 023eb9fdb59b00f9507e2e0694ad0934bb564c03
fbshipit-source-id: 023eb9fdb59b00f9507e2e0694ad0934bb564c03
2016-05-04 08:58:18 -07:00
Nick Lockwood e72163f0f2 Added explicit init to observer modules
Summary:
Modules which call JS methods directly, or use `sendDeviceEventWithName:`, can trigger effects in JS without ever being referenced from the JS code. This breaks some assumptions in my earlier diff about when modules can be lazily loaded.

Pending a better solution, I've put explicit `init` methods in these modules to ensure they are eagerly initialized (the downside to this is that they'll still be initialized even if they are never used).

Reviewed By: javache

Differential Revision: D3258232

fb-gh-sync-id: f925bc2e5339c1fbfcc244d4613062c5ab848fc2
fbshipit-source-id: f925bc2e5339c1fbfcc244d4613062c5ab848fc2
2016-05-04 07:07:24 -07:00
Pieter De Baets a9a90aa2f0 Fix RCTProfileHookModules instantiating all modules
Reviewed By: tadeuzagallo

Differential Revision: D3235048

fb-gh-sync-id: bdcd72fb241c5136e884c1705e027f178939970b
fbshipit-source-id: bdcd72fb241c5136e884c1705e027f178939970b
2016-05-04 06:55:30 -07:00
673 changed files with 20117 additions and 7649 deletions
+5 -3
View File
@@ -221,17 +221,19 @@
"react/display-name": 0,
"react/jsx-boolean-value": 0,
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-undef": 1,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react/no-did-mount-set-state": [1, "allow-in-func"],
"react/no-did-update-set-state": [1, "allow-in-func"],
"react/no-multi-comp": 0,
"react/no-string-refs": 1,
"react/no-unknown-property": 0,
"react/prop-types": 0,
"react/react-in-jsx-scope": 0,
"react/react-in-jsx-scope": 1,
"react/self-closing-comp": 1,
"react/wrap-multilines": 0
"react/wrap-multilines": 0,
}
}
+6 -3
View File
@@ -46,6 +46,9 @@
# Ignore BUCK generated folders
.*\.buckd/
# Ignore RNPM
.*/local-cli/rnpm/.*
.*/node_modules/is-my-json-valid/test/.*\.json
.*/node_modules/iconv-lite/encodings/tables/.*\.json
.*/node_modules/y18n/test/.*\.json
@@ -85,9 +88,9 @@ suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-4]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-4]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-5]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-5]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version]
0.24.0
^0.25.0
+3
View File
@@ -49,3 +49,6 @@ node_modules
# OS X
.DS_Store
# Test generated files
/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
+2
View File
@@ -0,0 +1,2 @@
# rnpm
/local-cli/rnpm
+1
View File
@@ -5,6 +5,7 @@ osx_image: xcode7.3
install:
- mkdir -p /Users/travis/build/facebook/.nvm
- export NVM_DIR="/Users/travis/build/facebook/.nvm"
- brew install nvm
- source $(brew --prefix nvm)/nvm.sh
# testing with npm@2 for diversity, Circle tests with npm@3
- nvm install 4.4.3
+18 -7
View File
@@ -58,17 +58,20 @@ var ListViewSimpleExample = React.createClass({
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
renderSeparator={(sectionID, rowID) => <View key={`${sectionID}-${rowID}`} style={styles.separator} />}
renderSeparator={this._renderSeperator}
/>
</UIExplorerPage>
);
},
_renderRow: function(rowData: string, sectionID: number, rowID: number) {
_renderRow: function(rowData: string, sectionID: number, rowID: number, highlightRow: (sectionID: number, rowID: number) => void) {
var rowHash = Math.abs(hashCode(rowData));
var imgSource = THUMB_URLS[rowHash % THUMB_URLS.length];
return (
<TouchableHighlight onPress={() => this._pressRow(rowID)}>
<TouchableHighlight onPress={() => {
this._pressRow(rowID);
highlightRow(sectionID, rowID);
}}>
<View>
<View style={styles.row}>
<Image style={styles.thumb} source={imgSource} />
@@ -96,6 +99,18 @@ var ListViewSimpleExample = React.createClass({
this._genRows(this._pressData)
)});
},
_renderSeperator: function(sectionID: number, rowID: number, adjacentRowHighlighted: bool) {
return (
<View
key={`${sectionID}-${rowID}`}
style={{
height: adjacentRowHighlighted ? 4 : 1,
backgroundColor: adjacentRowHighlighted ? '#3B5998' : '#CCCCCC',
}}
/>
);
}
});
var THUMB_URLS = [
@@ -130,10 +145,6 @@ var styles = StyleSheet.create({
padding: 10,
backgroundColor: '#F6F6F6',
},
separator: {
height: 1,
backgroundColor: '#CCCCCC',
},
thumb: {
width: 64,
height: 64,
@@ -1,165 +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.
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
ScrollView,
Text,
TouchableHighlight,
TouchableOpacity
} = ReactNative;
var _getRandomRoute = function() {
return {
title: '#' + Math.ceil(Math.random() * 1000),
};
};
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var BreadcrumbNavSample = React.createClass({
componentWillMount: function() {
this._navBarRouteMapper = {
rightContentForRoute: function(route, navigator) {
return null;
},
titleContentForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={() => navigator.push(_getRandomRoute())}>
<Text style={styles.titleText}>{route.title}</Text>
</TouchableOpacity>
);
},
iconForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={() => { navigator.popToRoute(route); }}
style={styles.crumbIconPlaceholder}
/>
);
},
separatorForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={navigator.pop}
style={styles.crumbSeparatorPlaceholder}
/>
);
}
};
},
_renderScene: function(route, navigator) {
return (
<ScrollView style={styles.scene}>
<NavButton
onPress={() => { navigator.push(_getRandomRoute()); }}
text="Push"
/>
<NavButton
onPress={() => { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]); }}
text="Reset w/ 2 scenes"
/>
<NavButton
onPress={() => { navigator.popToTop(); }}
text="Pop to top"
/>
<NavButton
onPress={() => { navigator.replace(_getRandomRoute()); }}
text="Replace"
/>
<NavButton
onPress={() => { this.props.navigator.pop(); }}
text="Close breadcrumb example"
/>
</ScrollView>
);
},
render: function() {
return (
<Navigator
style={styles.container}
initialRoute={_getRandomRoute()}
renderScene={this._renderScene}
navigationBar={
<Navigator.BreadcrumbNavigationBar
routeMapper={this._navBarRouteMapper}
/>
}
/>
);
},
});
var styles = StyleSheet.create({
scene: {
paddingTop: 50,
flex: 1,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
container: {
overflow: 'hidden',
backgroundColor: '#dddddd',
flex: 1,
},
titleText: {
fontSize: 18,
color: '#666666',
textAlign: 'center',
fontWeight: 'bold',
lineHeight: 32,
},
crumbIconPlaceholder: {
flex: 1,
backgroundColor: '#666666',
},
crumbSeparatorPlaceholder: {
flex: 1,
backgroundColor: '#aaaaaa',
},
});
module.exports = BreadcrumbNavSample;
@@ -1,219 +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.
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
ScrollView,
TabBarIOS,
Text,
TouchableHighlight,
View,
} = ReactNative;
var _getRandomRoute = function() {
return {
randNumber: Math.ceil(Math.random() * 1000),
};
};
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var ROUTE_STACK = [
_getRandomRoute(),
_getRandomRoute(),
_getRandomRoute(),
];
var INIT_ROUTE_INDEX = 1;
class JumpingNavBar extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: props.initTabIndex,
};
}
handleWillFocus(route) {
var tabIndex = ROUTE_STACK.indexOf(route);
this.setState({ tabIndex, });
}
render() {
return (
<View style={styles.tabs}>
<TabBarIOS>
<TabBarIOS.Item
icon={require('image!tabnav_notification')}
selected={this.state.tabIndex === 0}
onPress={() => {
this.props.onTabIndex(0);
this.setState({ tabIndex: 0, });
}}>
<View />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={require('image!tabnav_list')}
selected={this.state.tabIndex === 1}
onPress={() => {
this.props.onTabIndex(1);
this.setState({ tabIndex: 1, });
}}>
<View />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={require('image!tabnav_settings')}
selected={this.state.tabIndex === 2}
onPress={() => {
this.props.onTabIndex(2);
this.setState({ tabIndex: 2, });
}}>
<View />
</TabBarIOS.Item>
</TabBarIOS>
</View>
);
}
}
var JumpingNavSample = React.createClass({
render: function() {
return (
<Navigator
debugOverlay={false}
style={styles.appContainer}
ref={(navigator) => {
this._navigator = navigator;
}}
initialRoute={ROUTE_STACK[INIT_ROUTE_INDEX]}
initialRouteStack={ROUTE_STACK}
renderScene={this.renderScene}
configureScene={() => ({
...Navigator.SceneConfigs.HorizontalSwipeJump,
})}
navigationBar={
<JumpingNavBar
ref={(navBar) => { this.navBar = navBar; }}
initTabIndex={INIT_ROUTE_INDEX}
routeStack={ROUTE_STACK}
onTabIndex={(index) => {
this._navigator.jumpTo(ROUTE_STACK[index]);
}}
/>
}
/>
);
},
renderScene: function(route, navigator) {
var backBtn;
var forwardBtn;
if (ROUTE_STACK.indexOf(route) !== 0) {
backBtn = (
<NavButton
onPress={() => {
navigator.jumpBack();
}}
text="jumpBack"
/>
);
}
if (ROUTE_STACK.indexOf(route) !== ROUTE_STACK.length - 1) {
forwardBtn = (
<NavButton
onPress={() => {
navigator.jumpForward();
}}
text="jumpForward"
/>
);
}
return (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>#{route.randNumber}</Text>
{backBtn}
{forwardBtn}
<NavButton
onPress={() => {
navigator.jumpTo(ROUTE_STACK[1]);
}}
text="jumpTo middle route"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Exit Navigation Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Came from jumping example',
});
}}
text="Nav Menu"
/>
</ScrollView>
);
},
});
var styles = StyleSheet.create({
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
appContainer: {
overflow: 'hidden',
backgroundColor: '#dddddd',
flex: 1,
},
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#EAEAEA',
},
tabs: {
height: 50,
}
});
module.exports = JumpingNavSample;
@@ -1,220 +0,0 @@
/**
* Copyright (c) 2013-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 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');
var ReactNative = require('react-native');
var {
NavigationExperimental,
ScrollView,
StyleSheet,
Text,
TouchableHighlight,
} = ReactNative;
var BreadcrumbNavSample = require('./BreadcrumbNavSample');
var NavigationBarSample = require('./NavigationBarSample');
var JumpingNavSample = require('./JumpingNavSample');
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
class NavMenu extends React.Component {
render() {
return (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>{this.props.message}</Text>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Swipe right to dismiss',
sceneConfig: Navigator.SceneConfigs.FloatFromRight,
});
}}
text="Float in from right"
/>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Swipe down to dismiss',
sceneConfig: Navigator.SceneConfigs.FloatFromBottom,
});
}}
text="Float in from bottom"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Pop"
/>
<NavButton
onPress={() => {
this.props.navigator.popToTop();
}}
text="Pop to top"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'navbar' });
}}
text="Navbar Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'jumping' });
}}
text="Jumping Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'breadcrumbs' });
}}
text="Breadcrumbs Example"
/>
<NavButton
onPress={() => {
this.props.onExampleExit();
}}
text="Exit <LegacyNavigator> Example"
/>
</ScrollView>
);
}
}
var TabBarExample = React.createClass({
statics: {
title: '<LegacyNavigator> (Experimental)',
description: 'Experimental navigator that ports the API of the current ' +
'Navigator component',
},
renderScene: function(route, nav) {
switch (route.id) {
case 'navbar':
return <NavigationBarSample navigator={nav} />;
case 'breadcrumbs':
return <BreadcrumbNavSample navigator={nav} />;
case 'jumping':
return <JumpingNavSample navigator={nav} />;
default:
return (
<NavMenu
message={route.message}
navigator={nav}
onExampleExit={this.props.onExampleExit}
/>
);
}
},
render: function() {
return (
<Navigator
ref={this._setNavigatorRef}
style={styles.container}
initialRoute={{ message: 'First Scene', }}
renderScene={this.renderScene}
configureScene={(route) => {
if (route.sceneConfig) {
return route.sceneConfig;
}
return Navigator.SceneConfigs.FloatFromBottom;
}}
/>
);
},
componentWillUnmount: function() {
this._listeners && this._listeners.forEach(listener => listener.remove());
},
_setNavigatorRef: function(navigator) {
if (navigator !== this._navigator) {
this._navigator = navigator;
if (navigator) {
var callback = (event) => {
console.log(
`LegacyNavigator: event ${event.type}`,
{
route: JSON.stringify(event.data.route),
target: event.target,
type: event.type,
}
);
};
// Observe focus change events from the owner.
this._listeners = [
navigator.navigationContext.addListener('willfocus', callback),
navigator.navigationContext.addListener('didfocus', callback),
];
}
}
},
});
var styles = StyleSheet.create({
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
container: {
flex: 1,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#ccc',
}
});
TabBarExample.external = true;
module.exports = TabBarExample;
@@ -1,202 +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.
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
ScrollView,
StyleSheet,
Text,
TouchableHighlight,
TouchableOpacity,
} = ReactNative;
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var NavigationBarRouteMapper = {
LeftButton: function(route, navigator, index, navState) {
if (index === 0) {
return null;
}
var previousRoute = navState.routeStack[index - 1];
return (
<TouchableOpacity
onPress={() => navigator.pop()}
style={styles.navBarLeftButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
{previousRoute.title}
</Text>
</TouchableOpacity>
);
},
RightButton: function(route, navigator, index, navState) {
return (
<TouchableOpacity
onPress={() => navigator.push(newRandomRoute())}
style={styles.navBarRightButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
Next
</Text>
</TouchableOpacity>
);
},
Title: function(route, navigator, index, navState) {
return (
<Text style={[styles.navBarText, styles.navBarTitleText]}>
{route.title} [{index}]
</Text>
);
},
};
function newRandomRoute() {
return {
title: '#' + Math.ceil(Math.random() * 1000),
};
}
var NavigationBarSample = React.createClass({
componentWillMount: function() {
var navigator = this.props.navigator;
var callback = (event) => {
console.log(
`NavigationBarSample : event ${event.type}`,
{
route: JSON.stringify(event.data.route),
target: event.target,
type: event.type,
}
);
};
// Observe focus change events from this component.
this._listeners = [
navigator.navigationContext.addListener('willfocus', callback),
navigator.navigationContext.addListener('didfocus', callback),
];
},
componentWillUnmount: function() {
this._listeners && this._listeners.forEach(listener => listener.remove());
},
render: function() {
return (
<Navigator
debugOverlay={false}
style={styles.appContainer}
initialRoute={newRandomRoute()}
renderScene={(route, navigator) => (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>{route.content}</Text>
<NavButton
onPress={() => {
navigator.immediatelyResetRouteStack([
newRandomRoute(),
newRandomRoute(),
newRandomRoute(),
]);
}}
text="Reset w/ 3 scenes"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Exit NavigationBar Example"
/>
</ScrollView>
)}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper}
style={styles.navBar}
/>
}
/>
);
},
});
var styles = StyleSheet.create({
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
navBar: {
backgroundColor: 'white',
},
navBarText: {
fontSize: 16,
marginVertical: 10,
},
navBarTitleText: {
color: '#373E4D',
fontWeight: '500',
marginVertical: 9,
},
navBarLeftButton: {
paddingLeft: 10,
},
navBarRightButton: {
paddingRight: 10,
},
navBarButtonText: {
color: '#5890FF',
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#EAEAEA',
},
});
module.exports = NavigationBarSample;
@@ -37,10 +37,9 @@ const {
Card: NavigationCard,
Header: NavigationHeader,
Reducer: NavigationReducer,
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
const NavigationBasicReducer = NavigationReducer.StackReducer({
const ExampleReducer = NavigationReducer.StackReducer({
getPushedReducerForAction: (action) => {
if (action.type === 'push') {
return (state) => state || {key: action.key};
@@ -58,37 +57,41 @@ const NavigationBasicReducer = NavigationReducer.StackReducer({
});
class NavigationAnimatedExample extends React.Component {
constructor(props, context) {
super(props, context);
this.state = ExampleReducer();
}
componentWillMount() {
this._renderCard = this._renderCard.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderNavigation = this._renderNavigation.bind(this);
this._renderScene = this._renderScene.bind(this);
this._renderTitleComponent = this._renderTitleComponent.bind(this);
this._handleAction = this._handleAction.bind(this);
}
_handleAction(action): boolean {
if (!action) {
return false;
}
const newState = ExampleReducer(this.state, action);
if (newState === this.state) {
return false;
}
this.setState(newState);
return true;
}
handleBackAction(): boolean {
return this._handleAction({ type: 'BackAction', });
}
render() {
return (
<NavigationRootContainer
reducer={NavigationBasicReducer}
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
persistenceKey="NavigationAnimExampleState"
renderNavigation={this._renderNavigation}
/>
);
}
handleBackAction() {
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
}
_renderNavigation(navigationState, onNavigate) {
if (!navigationState) {
return null;
}
return (
<NavigationAnimatedView
navigationState={navigationState}
navigationState={this.state}
style={styles.animatedView}
onNavigate={this._handleAction}
renderOverlay={this._renderHeader}
applyAnimation={(pos, navState) => {
Animated.timing(pos, {toValue: navState.index, duration: 500}).start();
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -22,12 +29,10 @@ const {
} = ReactNative;
const NavigationExampleRow = require('./NavigationExampleRow');
const {
RootContainer: NavigationRootContainer,
Reducer: NavigationReducer,
} = NavigationExperimental;
const StackReducer = NavigationReducer.StackReducer;
const NavigationBasicReducer = NavigationReducer.StackReducer({
const ExampleReducer = NavigationReducer.StackReducer({
getPushedReducerForAction: (action) => {
if (action.type === 'push') {
return (state) => state || {key: action.key};
@@ -45,47 +50,51 @@ const NavigationBasicReducer = NavigationReducer.StackReducer({
});
const NavigationBasicExample = React.createClass({
getInitialState: function() {
return ExampleReducer();
},
render: function() {
return (
<NavigationRootContainer
reducer={NavigationBasicReducer}
persistenceKey="NavigationBasicExampleState"
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
renderNavigation={(navState, onNavigate) => {
if (!navState) { return null; }
return (
<ScrollView style={styles.topView}>
<NavigationExampleRow
text={`Current page: ${navState.children[navState.index].key}`}
/>
<NavigationExampleRow
text={`Push page #${navState.children.length}`}
onPress={() => {
onNavigate({ type: 'push', key: 'page #' + navState.children.length });
}}
/>
<NavigationExampleRow
text="pop"
onPress={() => {
onNavigate(NavigationRootContainer.getBackAction());
}}
/>
<NavigationExampleRow
text="Exit Basic Nav Example"
onPress={this.props.onExampleExit}
/>
</ScrollView>
);
}}
/>
<ScrollView style={styles.topView}>
<NavigationExampleRow
text={`Current page: ${this.state.children[this.state.index].key}`}
/>
<NavigationExampleRow
text={`Push page #${this.state.children.length}`}
onPress={() => {
this._handleAction({ type: 'push', key: 'page #' + this.state.children.length });
}}
/>
<NavigationExampleRow
text="pop"
onPress={() => {
this._handleAction({ type: 'BackAction' });
}}
/>
<NavigationExampleRow
text="Exit Basic Nav Example"
onPress={this.props.onExampleExit}
/>
</ScrollView>
);
},
handleBackAction: function() {
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
_handleAction(action) {
if (!action) {
return false;
}
const newState = ExampleReducer(this.state, action);
if (newState === this.state) {
return false;
}
this.setState(newState);
return true;
},
handleBackAction() {
return this._handleAction({ type: 'BackAction' });
},
});
@@ -33,18 +33,15 @@ const {
const {
CardStack: NavigationCardStack,
StateUtils: NavigationStateUtils,
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
function createReducer(initialState) {
return (currentState, action) => {
return (currentState = initialState, action) => {
switch (action.type) {
case 'RootContainerInitialAction':
return initialState;
case 'push':
return NavigationStateUtils.push(currentState, {key: action.key});
case 'BackAction':
case 'back':
case 'pop':
return currentState.index > 0 ?
@@ -67,35 +64,46 @@ class NavigationCardStackExample extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {isHorizontal: true};
this.state = {
isHorizontal: true,
navState: ExampleReducer(undefined, {}),
};
}
componentWillMount() {
this._renderNavigation = this._renderNavigation.bind(this);
this._renderScene = this._renderScene.bind(this);
this._toggleDirection = this._toggleDirection.bind(this);
this._handleAction = this._handleAction.bind(this);
}
render() {
return (
<NavigationRootContainer
reducer={ExampleReducer}
renderNavigation={this._renderNavigation}
<NavigationCardStack
direction={this.state.isHorizontal ? 'horizontal' : 'vertical'}
navigationState={this.state.navState}
onNavigate={this._handleAction}
renderScene={this._renderScene}
style={styles.main}
/>
);
}
_renderNavigation(navigationState, onNavigate) {
return (
<NavigationCardStack
direction={this.state.isHorizontal ? 'horizontal' : 'vertical'}
navigationState={navigationState}
onNavigate={onNavigate}
renderScene={this._renderScene}
style={styles.main}
/>
);
_handleAction(action): boolean {
if (!action) {
return false;
}
const newState = ExampleReducer(this.state.navState, action);
if (newState === this.state.navState) {
return false;
}
this.setState({
navState: newState,
});
return true;
}
handleBackAction(): boolean {
return this._handleAction({ type: 'BackAction', });
}
_renderScene(/*NavigationSceneRendererProps*/ props) {
@@ -36,11 +36,8 @@ const {
const {
CardStack: NavigationCardStack,
Container: NavigationContainer,
Header: NavigationHeader,
Reducer: NavigationReducer,
RootContainer: NavigationRootContainer,
View: NavigationView,
} = NavigationExperimental;
@@ -177,6 +174,7 @@ class ExampleTabScreen extends React.Component {
<NavigationCardStack
style={styles.tabContent}
navigationState={this.props.navigationState}
onNavigate={this.props.onNavigate}
renderOverlay={this._renderHeader}
renderScene={this._renderScene}
/>
@@ -225,40 +223,42 @@ class ExampleTabScreen extends React.Component {
);
}
}
ExampleTabScreen = NavigationContainer.create(ExampleTabScreen);
class NavigationCompositionExample extends React.Component {
navRootContainer: NavigationRootContainer;
render() {
return (
<NavigationRootContainer
reducer={ExampleAppReducer}
persistenceKey="NavigationCompositionState"
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
renderNavigation={this.renderApp.bind(this)}
/>
);
state: NavigationParentState;
constructor() {
super();
this.state = ExampleAppReducer(undefined, {});
}
handleAction(action: Object): boolean {
if (!action) {
return false;
}
const newState = ExampleAppReducer(this.state, action);
if (newState === this.state) {
return false;
}
this.setState(newState);
return true;
}
handleBackAction(): boolean {
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
return this.handleAction({ type: 'BackAction' });
}
renderApp(navigationState: NavigationParentState, onNavigate: Function) {
if (!navigationState) {
render() {
if (!this.state) {
return null;
}
return (
<View style={styles.topView}>
<ExampleMainView
navigationState={navigationState}
navigationState={this.state}
onExampleExit={this.props.onExampleExit}
onNavigate={this.handleAction.bind(this)}
/>
<NavigationExampleTabBar
tabs={navigationState.children}
index={navigationState.index}
tabs={this.state.children}
index={this.state.index}
onNavigate={this.handleAction.bind(this)}
/>
</View>
);
@@ -266,34 +266,35 @@ class NavigationCompositionExample extends React.Component {
}
class ExampleMainView extends React.Component {
_renderScene: NavigationSceneRenderer;
_renderScene: Function;
_handleNavigation: Function;
componentWillMount() {
this._renderScene = this._renderScene.bind(this);
this._handleNavigation = this._handleNavigation.bind(this);
}
render() {
return (
<NavigationView
navigationState={this.props.navigationState}
style={styles.tabsContent}
renderScene={this._renderScene}
/>
<View style={styles.tabsContent}>
{this._renderScene()}
</View>
);
}
_renderScene(props: NavigationSceneRendererProps): ReactElement {
const {scene} = props;
_renderScene(): ReactElement {
const {navigationState} = this.props;
const childState = navigationState.children[navigationState.index];
return (
<ExampleTabScreen
key={'tab_screen' + scene.key}
navigationState={scene.navigationState}
onNavigate={this._handleNavigation.bind(this, scene.key)}
key={'tab_screen' + childState.key}
navigationState={childState}
onNavigate={this._handleNavigation}
/>
);
}
_handleNavigation(tabKey, action) {
_handleNavigation(action: Object) {
if (ExampleExitAction.match(action)) {
this.props.onExampleExit();
return;
@@ -302,8 +303,6 @@ class ExampleMainView extends React.Component {
}
}
ExampleMainView = NavigationContainer.create(ExampleMainView);
const styles = StyleSheet.create({
topView: {
flex: 1,
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -13,24 +20,20 @@
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
Text,
TouchableOpacity,
View,
} = ReactNative;
const NavigationExperimental = require('NavigationExperimental');
const React = require('react');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TouchableOpacity = require('TouchableOpacity');
const View = require('View');
const {
Container: NavigationContainer,
Reducer: NavigationReducer,
} = NavigationExperimental;
const {
JumpToAction,
} = NavigationReducer.TabsReducer;
var NavigationExampleTabBar = React.createClass({
const NavigationExampleTabBar = React.createClass({
render: function() {
return (
<View style={styles.tabBar}>
@@ -39,7 +42,7 @@ var NavigationExampleTabBar = React.createClass({
);
},
_renderTab: function(tab, index) {
var textStyle = [styles.tabButtonText];
const textStyle = [styles.tabButtonText];
if (this.props.index === index) {
textStyle.push(styles.selectedTabButtonText);
}
@@ -58,8 +61,6 @@ var NavigationExampleTabBar = React.createClass({
},
});
NavigationExampleTabBar = NavigationContainer.create(NavigationExampleTabBar);
const styles = StyleSheet.create({
tabBar: {
height: 50,
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -13,22 +20,19 @@
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
AsyncStorage,
ScrollView,
StyleSheet,
View,
} = ReactNative;
var NavigationExampleRow = require('./NavigationExampleRow');
const AsyncStorage = require('AsyncStorage');
const NavigationExampleRow = require('./NavigationExampleRow');
const React = require('react');
const ScrollView = require('ScrollView');
const StyleSheet = require('StyleSheet');
const View = require('View');
/*
* Heads up! This file is not the real navigation example- only a utility to switch between them.
*
* To learn how to use the Navigation API, take a look at the following example files:
*/
var EXAMPLES = {
const EXAMPLES = {
'Tabs': require('./NavigationTabsExample'),
'Basic': require('./NavigationBasicExample'),
'Animated Example': require('./NavigationAnimatedExample'),
@@ -37,9 +41,9 @@ var EXAMPLES = {
'Tic Tac Toe': require('./NavigationTicTacToeExample'),
};
var EXAMPLE_STORAGE_KEY = 'NavigationExperimentalExample';
const EXAMPLE_STORAGE_KEY = 'NavigationExperimentalExample';
var NavigationExperimentalExample = React.createClass({
const NavigationExperimentalExample = React.createClass({
statics: {
title: 'Navigation (Experimental)',
description: 'Upcoming navigation APIs and animated navigation views',
@@ -74,7 +78,7 @@ var NavigationExperimentalExample = React.createClass({
},
_renderMenu: function() {
var exitRow = null;
let exitRow = null;
if (this.props.onExampleExit) {
exitRow = (
<NavigationExampleRow
@@ -130,7 +134,7 @@ var NavigationExperimentalExample = React.createClass({
return this._renderMenu();
}
if (EXAMPLES[this.state.example]) {
var Component = EXAMPLES[this.state.example];
const Component = EXAMPLES[this.state.example];
return (
<Component
onExampleExit={this._exitInnerExample}
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -22,8 +29,6 @@ const {
View,
} = ReactNative;
const {
Container: NavigationContainer,
RootContainer: NavigationRootContainer,
Reducer: NavigationReducer,
} = NavigationExperimental;
@@ -55,7 +60,6 @@ class ExmpleTabPage extends React.Component {
);
}
}
ExmpleTabPage = NavigationContainer.create(ExmpleTabPage);
const ExampleTabsReducer = NavigationReducer.TabsReducer({
tabReducers: [
@@ -66,36 +70,40 @@ const ExampleTabsReducer = NavigationReducer.TabsReducer({
});
class NavigationTabsExample extends React.Component {
constructor() {
super();
this.state = ExampleTabsReducer(undefined, {});
}
render() {
return (
<NavigationRootContainer
reducer={ExampleTabsReducer}
persistenceKey="NAV_EXAMPLE_STATE_TABS"
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
renderNavigation={(navigationState) => {
if (!navigationState) { return null; }
return (
<View style={styles.topView}>
<ExmpleTabPage
tabs={navigationState.children}
index={navigationState.index}
onExampleExit={this.props.onExampleExit}
/>
<NavigationExampleTabBar
tabs={navigationState.children}
index={navigationState.index}
/>
</View>
);
}}
/>
<View style={styles.topView}>
<ExmpleTabPage
tabs={this.state.children}
index={this.state.index}
onExampleExit={this.props.onExampleExit}
onNavigate={this.handleAction.bind(this)}
/>
<NavigationExampleTabBar
tabs={this.state.children}
index={this.state.index}
onNavigate={this.handleAction.bind(this)}
/>
</View>
);
}
handleAction(action) {
if (!action) {
return false;
}
const newState = ExampleTabsReducer(this.state, action);
if (newState === this.state) {
return false;
}
this.setState(newState);
return true;
}
handleBackAction() {
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
return this.handleAction({ type: 'BackAction' });
}
}
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -16,19 +23,11 @@
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
Text,
TouchableHighlight,
View,
} = ReactNative;
const {
Container: NavigationContainer,
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
const React = require('react');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TouchableHighlight = require('TouchableHighlight');
const View = require('View');
type GameGrid = Array<Array<?string>>;
@@ -41,7 +40,7 @@ function parseGame(game: string): GameGrid {
for (let i = 0; i < 3; i++) {
const row = Array(3);
for (let j = 0; j < 3; j++) {
const turnIndex = gameTurns.indexOf(rowLetterMap[i]+j);
const turnIndex = gameTurns.indexOf(rowLetterMap[i] + j);
if (turnIndex === -1) {
row[j] = null;
} else {
@@ -60,13 +59,13 @@ function playTurn(game: string, row: number, col: number): string {
function getWinner(gameString: string): ?string {
const game = parseGame(gameString);
for (var i = 0; i < 3; i++) {
for (let i = 0; i < 3; i++) {
if (game[i][0] !== null && game[i][0] === game[i][1] &&
game[i][0] === game[i][2]) {
return game[i][0];
}
}
for (var i = 0; i < 3; i++) {
for (let i = 0; i < 3; i++) {
if (game[0][i] !== null && game[0][i] === game[1][i] &&
game[0][i] === game[2][i]) {
return game[0][i];
@@ -88,8 +87,8 @@ function isGameOver(gameString: string): boolean {
return true;
}
const game = parseGame(gameString);
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (game[i][j] === null) {
return false;
}
@@ -157,10 +156,9 @@ function GameEndOverlay(props) {
</View>
);
}
GameEndOverlay = NavigationContainer.create(GameEndOverlay);
function TicTacToeGame(props) {
var rows = parseGame(props.game).map((cells, row) =>
const rows = parseGame(props.game).map((cells, row) =>
<View key={'row' + row} style={styles.row}>
{cells.map((player, col) =>
<Cell
@@ -184,11 +182,11 @@ function TicTacToeGame(props) {
</View>
<GameEndOverlay
game={props.game}
onNavigate={props.onNavigate}
/>
</View>
);
}
TicTacToeGame = NavigationContainer.create(TicTacToeGame);
const GameActions = {
Turn: (row, col) => ({type: 'TicTacToeTurnAction', row, col }),
@@ -208,21 +206,34 @@ function GameReducer(lastGame: ?string, action: Object): string {
return lastGame;
}
type AppState = {
game: string,
};
class NavigationTicTacToeExample extends React.Component {
static GameView = TicTacToeGame;
static GameReducer = GameReducer;
static GameActions = GameActions;
_handleAction: Function;
state: AppState;
constructor() {
super();
this._handleAction = this._handleAction.bind(this);
this.state = {
game: ''
};
}
_handleAction(action: Object) {
const newState = GameReducer(this.state.game, action);
if (newState !== this.state.game) {
this.setState({
game: newState,
});
}
}
render() {
return (
<NavigationRootContainer
reducer={GameReducer}
persistenceKey="TicTacToeGameState"
renderNavigation={(game) => (
<TicTacToeGame
game={game}
onExampleExit={this.props.onExampleExit}
/>
)}
<TicTacToeGame
game={this.state && this.state.game}
onExampleExit={this.props.onExampleExit}
onNavigate={this._handleAction}
/>
);
}
+10 -2
View File
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -415,12 +422,13 @@ exports.examples = [
);
},
}, {
title: 'Inline images',
title: 'Inline views',
render: function() {
return (
<View>
<Text>
This text contains an inline image <Image source={require('./flux.png')} style={{width: 30, height: 11, resizeMode: 'cover'}}/>. Neat, huh?
This text contains an inline blue view <View style={{width: 25, height: 25, backgroundColor: 'steelblue'}} /> and
an inline image <Image source={require('./flux.png')} style={{width: 30, height: 11, resizeMode: 'cover'}}/>. Neat, huh?
</Text>
</View>
);
@@ -531,4 +531,43 @@ exports.examples = [
return <TokenizedTextExample />;
}
},
{
title: 'Return key',
render: function() {
var returnKeyTypes = [
'none',
'go',
'search',
'send',
'done',
'previous',
'next',
];
var returnKeyLabels = [
'Compile',
'React Native',
];
var examples = returnKeyTypes.map((type) => {
return (
<TextInput
key={type}
returnKeyType={type}
placeholder={'returnKeyType: ' + type}
style={styles.singleLine}
/>
);
});
var types = returnKeyLabels.map((type) => {
return (
<TextInput
key={type}
returnKeyLabel={type}
placeholder={'returnKeyLabel: ' + type}
style={styles.singleLine}
/>
);
});
return <View>{examples}{types}</View>;
}
},
];
+8 -3
View File
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -25,9 +32,7 @@ export type UIExplorerExampleAction = {
openExample: string;
};
import type {BackAction} from 'NavigationRootContainer';
export type UIExplorerAction = BackAction | UIExplorerListWithFilterAction | UIExplorerExampleAction;
export type UIExplorerAction = UIExplorerListWithFilterAction | UIExplorerExampleAction;
function ExampleListWithFilter(filter: ?string): UIExplorerListWithFilterAction {
return {
+66 -68
View File
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -16,29 +23,24 @@
*/
'use strict';
const AppRegistry = require('AppRegistry');
const AsyncStorage = require('AsyncStorage');
const BackAndroid = require('BackAndroid');
const Dimensions = require('Dimensions');
const DrawerLayoutAndroid = require('DrawerLayoutAndroid');
const Linking = require('Linking');
const React = require('react');
const ReactNative = require('react-native');
const {
AppRegistry,
BackAndroid,
Dimensions,
DrawerLayoutAndroid,
NavigationExperimental,
StyleSheet,
ToolbarAndroid,
View,
StatusBar,
} = ReactNative;
const {
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
const StatusBar = require('StatusBar');
const StyleSheet = require('StyleSheet');
const ToolbarAndroid = require('ToolbarAndroid');
const UIExplorerExampleList = require('./UIExplorerExampleList');
const UIExplorerList = require('./UIExplorerList');
const UIExplorerNavigationReducer = require('./UIExplorerNavigationReducer');
const UIExplorerStateTitleMap = require('./UIExplorerStateTitleMap');
const URIActionMap = require('./URIActionMap');
const View = require('View');
var DRAWER_WIDTH_LEFT = 56;
const DRAWER_WIDTH_LEFT = 56;
type Props = {
exampleFromAppetizeParams: string,
@@ -49,14 +51,13 @@ type State = {
};
class UIExplorerApp extends React.Component {
_handleOpenInitialExample: Function;
_handleAction: Function;
_renderDrawerContent: Function;
state: State;
constructor(props: Props) {
super(props);
this._handleOpenInitialExample = this._handleOpenInitialExample.bind(this);
this.state = {
initialExampleUri: props.exampleFromAppetizeParams,
};
this._handleAction = this._handleAction.bind(this);
this._renderDrawerContent = this._renderDrawerContent.bind(this);
}
componentWillMount() {
@@ -64,38 +65,28 @@ class UIExplorerApp extends React.Component {
}
componentDidMount() {
// There's a race condition if we try to navigate to the specified example
// from the initial props at the same time the navigation logic is setting
// up the initial navigation state. This hack adds a delay to avoid this
// scenario. So after the initial example list is shown, we then transition
// to the initial example.
setTimeout(this._handleOpenInitialExample, 500);
Linking.getInitialURL().then((url) => {
AsyncStorage.getItem('UIExplorerAppState', (err, storedString) => {
const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams);
const urlAction = URIActionMap(url);
const launchAction = exampleAction || urlAction;
if (err || !storedString) {
const initialAction = launchAction || {type: 'InitialAction'};
this.setState(UIExplorerNavigationReducer(null, initialAction));
return;
}
const storedState = JSON.parse(storedString);
if (launchAction) {
this.setState(UIExplorerNavigationReducer(storedState, launchAction));
return;
}
this.setState(storedState);
});
});
}
render() {
return (
<NavigationRootContainer
persistenceKey="UIExplorerStateNavState"
ref={navRootRef => { this._navigationRootRef = navRootRef; }}
reducer={UIExplorerNavigationReducer}
renderNavigation={this._renderApp.bind(this)}
linkingActionMap={URIActionMap}
/>
);
}
_handleOpenInitialExample() {
if (this.state.initialExampleUri) {
const exampleAction = URIActionMap(this.state.initialExampleUri);
if (exampleAction && this._navigationRootRef) {
this._navigationRootRef.handleNavigation(exampleAction);
}
}
this.setState({initialExampleUri: null});
}
_renderApp(navigationState, onNavigate) {
if (!navigationState) {
if (!this.state) {
return null;
}
return (
@@ -110,42 +101,42 @@ class UIExplorerApp extends React.Component {
this._overrideBackPressForDrawerLayout = false;
}}
ref={(drawer) => { this.drawer = drawer; }}
renderNavigationView={this._renderDrawerContent.bind(this, onNavigate)}
renderNavigationView={this._renderDrawerContent}
statusBarBackgroundColor="#589c90">
{this._renderNavigation(navigationState, onNavigate)}
{this._renderApp()}
</DrawerLayoutAndroid>
);
}
_renderDrawerContent(onNavigate) {
_renderDrawerContent() {
return (
<View style={styles.drawerContentWrapper}>
<UIExplorerExampleList
list={UIExplorerList}
displayTitleRow={true}
disableSearch={true}
onNavigate={(action) => {
this.drawer && this.drawer.closeDrawer();
onNavigate(action);
}}
onNavigate={this._handleAction}
/>
</View>
);
}
_renderNavigation(navigationState, onNavigate) {
if (navigationState.externalExample) {
var Component = UIExplorerList.Modules[navigationState.externalExample];
_renderApp() {
const {
externalExample,
stack,
} = this.state;
if (externalExample) {
const Component = UIExplorerList.Modules[externalExample];
return (
<Component
onExampleExit={() => {
onNavigate(NavigationRootContainer.getBackAction());
this._handleAction({ type: 'BackAction' });
}}
ref={(example) => { this._exampleRef = example; }}
/>
);
}
const {stack} = navigationState;
const title = UIExplorerStateTitleMap(stack.children[stack.index]);
const index = stack.children.length <= 1 ? 1 : stack.index;
@@ -178,6 +169,7 @@ class UIExplorerApp extends React.Component {
title={title}
/>
<UIExplorerExampleList
onNavigate={this._handleAction}
list={UIExplorerList}
{...stack.children[0]}
/>
@@ -185,6 +177,17 @@ class UIExplorerApp extends React.Component {
);
}
_handleAction(action: Object): boolean {
this.drawer && this.drawer.closeDrawer();
const newState = UIExplorerNavigationReducer(this.state, action);
if (this.state !== newState) {
this.setState(newState);
AsyncStorage.setItem('UIExplorerAppState', JSON.stringify(this.state));
return true;
}
return false;
}
_handleBackButtonPress() {
if (this._overrideBackPressForDrawerLayout) {
// This hack is necessary because drawer layout provides an imperative API
@@ -200,12 +203,7 @@ class UIExplorerApp extends React.Component {
) {
return true;
}
if (this._navigationRootRef) {
return this._navigationRootRef.handleNavigation(
NavigationRootContainer.getBackAction()
);
}
return false;
return this._handleAction({ type: 'BackAction' });
}
}
+51 -44
View File
@@ -23,6 +23,8 @@
*/
'use strict';
const AsyncStorage = require('AsyncStorage');
const Linking = require('Linking');
const React = require('react');
const ReactNative = require('react-native');
const UIExplorerList = require('./UIExplorerList.ios');
@@ -42,7 +44,6 @@ const {
const {
CardStack: NavigationCardStack,
Header: NavigationHeader,
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
import type { NavigationSceneRendererProps } from 'NavigationTypeDefinition';
@@ -55,81 +56,86 @@ type Props = {
exampleFromAppetizeParams: string,
};
type State = {
initialExampleUri: ?string,
type State = UIExplorerNavigationState & {
externalExample?: string,
};
class UIExplorerApp extends React.Component {
_navigationRootRef: ?NavigationRootContainer;
_renderNavigation: Function;
_renderOverlay: Function;
_renderScene: Function;
_renderCard: Function;
_renderTitleComponent: Function;
_handleOpenInitialExample: Function;
_handleAction: Function;
state: State;
constructor(props: Props) {
super(props);
this._handleOpenInitialExample = this._handleOpenInitialExample.bind(this);
this.state = {
initialExampleUri: props.exampleFromAppetizeParams,
};
}
componentWillMount() {
this._renderNavigation = this._renderNavigation.bind(this);
this._handleAction = this._handleAction.bind(this);
this._renderOverlay = this._renderOverlay.bind(this);
this._renderScene = this._renderScene.bind(this);
this._renderTitleComponent = this._renderTitleComponent.bind(this);
}
componentDidMount() {
// There's a race condition if we try to navigate to the specified example
// from the initial props at the same time the navigation logic is setting
// up the initial navigation state. This hack adds a delay to avoid this
// scenario. So after the initial example list is shown, we then transition
// to the initial example.
setTimeout(this._handleOpenInitialExample, 500);
Linking.getInitialURL().then((url) => {
AsyncStorage.getItem('UIExplorerAppState', (err, storedString) => {
const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams);
const urlAction = URIActionMap(url);
const launchAction = exampleAction || urlAction;
if (err || !storedString) {
const initialAction = launchAction || {type: 'InitialAction'};
this.setState(UIExplorerNavigationReducer(null, initialAction));
return;
}
const storedState = JSON.parse(storedString);
if (launchAction) {
this.setState(UIExplorerNavigationReducer(storedState, launchAction));
return;
}
this.setState(storedState);
});
});
Linking.addEventListener('url', (url) => {
this._handleAction(URIActionMap(url));
});
}
render() {
return (
<NavigationRootContainer
persistenceKey="UIExplorerState"
reducer={UIExplorerNavigationReducer}
ref={navRootRef => { this._navigationRootRef = navRootRef; }}
renderNavigation={this._renderNavigation}
linkingActionMap={URIActionMap}
/>
);
}
_handleOpenInitialExample() {
if (this.state.initialExampleUri) {
const exampleAction = URIActionMap(this.state.initialExampleUri);
if (exampleAction && this._navigationRootRef) {
this._navigationRootRef.handleNavigation(exampleAction);
}
_handleAction(action: Object) {
if (!action) {
return;
}
const newState = UIExplorerNavigationReducer(this.state, action);
if (this.state !== newState) {
this.setState(newState);
AsyncStorage.setItem('UIExplorerAppState', JSON.stringify(this.state));
}
this.setState({initialExampleUri: null});
}
_renderNavigation(navigationState: UIExplorerNavigationState, onNavigate: Function) {
if (!navigationState) {
render() {
if (!this.state) {
return null;
}
if (navigationState.externalExample) {
var Component = UIExplorerList.Modules[navigationState.externalExample];
if (this.state.externalExample) {
const Component = UIExplorerList.Modules[this.state.externalExample];
return (
<Component
onExampleExit={() => {
onNavigate(NavigationRootContainer.getBackAction());
this._handleAction({ type: 'BackAction' });
}}
/>
);
}
const {stack} = navigationState;
return (
<NavigationCardStack
navigationState={stack}
navigationState={this.state.stack}
style={styles.container}
renderOverlay={this._renderOverlay}
renderScene={this._renderScene}
onNavigate={this._handleAction}
/>
);
}
@@ -156,6 +162,7 @@ class UIExplorerApp extends React.Component {
if (state.key === 'AppList') {
return (
<UIExplorerExampleList
onNavigate={this._handleAction}
list={UIExplorerList}
style={styles.exampleContainer}
{...state}
@@ -194,9 +201,9 @@ AppRegistry.registerComponent('UIExplorerApp', () => UIExplorerApp);
UIExplorerList.ComponentExamples.concat(UIExplorerList.APIExamples).forEach((Example: UIExplorerExample) => {
const ExampleModule = Example.module;
if (ExampleModule.displayName) {
var Snapshotter = React.createClass({
const Snapshotter = React.createClass({
render: function() {
var Renderable = UIExplorerExampleList.makeRenderable(ExampleModule);
const Renderable = UIExplorerExampleList.makeRenderable(ExampleModule);
return (
<SnapshotViewIOS>
<Renderable />
+7 -10
View File
@@ -27,7 +27,6 @@ const React = require('react');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TextInput = require('TextInput');
const NavigationContainer = require('NavigationContainer');
const TouchableHighlight = require('TouchableHighlight');
const View = require('View');
const UIExplorerActions = require('./UIExplorerActions');
@@ -56,6 +55,13 @@ class UIExplorerExampleList extends React.Component {
}) {
}
static makeRenderable(example: any): ReactClass<any> {
return example.examples ?
createExamplePage(null, example) :
example;
}
render(): ?ReactElement {
const filterText = this.props.filter || '';
const filterRegex = new RegExp(String(filterText), 'i');
@@ -160,15 +166,6 @@ class UIExplorerExampleList extends React.Component {
}
}
function makeRenderable(example: any): ReactClass<any> {
return example.examples ?
createExamplePage(null, example) :
example;
}
UIExplorerExampleList = NavigationContainer.create(UIExplorerExampleList);
UIExplorerExampleList.makeRenderable = makeRenderable;
const styles = StyleSheet.create({
listContainer: {
flex: 1,
@@ -138,12 +138,6 @@ static ControlBlock propertiesUpdateBlock()
- (void)setUp
{
#if __LP64__
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) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"IntegrationTests/RCTRootViewIntegrationTestApp", nil);
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 153 KiB

@@ -31,12 +31,6 @@
- (void)setUp
{
#if __LP64__
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) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"IntegrationTests/IntegrationTestsApp", nil);
}
@@ -32,12 +32,6 @@
- (void)setUp
{
#if __LP64__
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) || version.majorVersion >= 9, @"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;
}
@@ -40,8 +40,8 @@ var ComponentExamples: Array<UIExplorerExample> = [
module: require('./ModalExample'),
},
{
key: 'PickerAndroidExample',
module: require('./PickerAndroidExample'),
key: 'PickerExample',
module: require('./PickerExample'),
},
{
key: 'ProgressBarAndroidExample',
+17 -6
View File
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -20,7 +27,7 @@ export type UIExplorerExample = {
module: Object;
};
var ComponentExamples: Array<UIExplorerExample> = [
const ComponentExamples: Array<UIExplorerExample> = [
{
key: 'ActivityIndicatorIOSExample',
module: require('./ActivityIndicatorIOSExample'),
@@ -69,6 +76,10 @@ var ComponentExamples: Array<UIExplorerExample> = [
key: 'NavigatorIOSExample',
module: require('./NavigatorIOSExample'),
},
{
key: 'PickerExample',
module: require('./PickerExample'),
},
{
key: 'PickerIOSExample',
module: require('./PickerIOSExample'),
@@ -131,7 +142,7 @@ var ComponentExamples: Array<UIExplorerExample> = [
},
];
var APIExamples: Array<UIExplorerExample> = [
const APIExamples: Array<UIExplorerExample> = [
{
key: 'AccessibilityIOSExample',
module: require('./AccessibilityIOSExample'),
@@ -144,6 +155,10 @@ var APIExamples: Array<UIExplorerExample> = [
key: 'AdSupportIOSExample',
module: require('./AdSupportIOSExample'),
},
{
key: 'AlertExample',
module: require('./AlertExample').AlertExample,
},
{
key: 'AlertIOSExample',
module: require('./AlertIOSExample'),
@@ -208,10 +223,6 @@ var APIExamples: Array<UIExplorerExample> = [
key: 'NavigationExperimentalExample',
module: require('./NavigationExperimental/NavigationExperimentalExample'),
},
{
key: 'NavigationExperimentalLegacyNavigatorExample',
module: require('./NavigationExperimental/LegacyNavigator/LegacyNavigatorExample'),
},
{
key: 'NetInfoExample',
module: require('./NetInfoExample'),
-1
View File
@@ -46,7 +46,6 @@ var ViewBorderStyleExample = React.createClass({
<View>
<View style={{
borderWidth: 1,
borderRadius: 5,
borderStyle: this.state.showBorder ? 'dashed' : null,
padding: 5
}}>
@@ -109,6 +109,7 @@ var ViewPagerAndroidExample = React.createClass({
return {
page: 0,
animationsAreEnabled: true,
scrollEnabled: true,
progress: {
position: 0,
offset: 0,
@@ -167,6 +168,7 @@ var ViewPagerAndroidExample = React.createClass({
<ViewPagerAndroid
style={styles.viewPager}
initialPage={0}
scrollEnabled={this.state.scrollEnabled}
onPageScroll={this.onPageScroll}
onPageSelected={this.onPageSelected}
onPageScrollStateChanged={this.onPageScrollStateChanged}
@@ -174,6 +176,13 @@ var ViewPagerAndroidExample = React.createClass({
ref={viewPager => { this.viewPager = viewPager; }}>
{pages}
</ViewPagerAndroid>
<View style={styles.buttons}>
<Button
enabled={true}
text={this.state.scrollEnabled ? 'Scroll Enabled' : 'Scroll Disabled'}
onPress={() => this.setState({scrollEnabled: !this.state.scrollEnabled})}
/>
</View>
<View style={styles.buttons}>
{ animationsAreEnabled ?
<Button
@@ -69,6 +69,7 @@ class Animated {
__getNativeConfig(): Object {
throw new Error('This JS animated node type cannot be used as native animated node');
}
toJSON(): any { return this.__getValue(); }
}
type AnimationConfig = {
+1 -1
View File
@@ -233,7 +233,7 @@ function createInterpolationFromStringOutputRange(
// 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...'
return outputRange[0].replace(stringShapeRegex, () => {
const val = +interpolations[i++](input);
const rounded = shouldRound && i < 4 ? Math.round(val) : val;
const rounded = shouldRound && i < 4 ? Math.round(val) : Math.round(val * 1000) / 1000;
return String(rounded);
});
};
@@ -142,6 +142,9 @@ describe('Animated', () => {
expect(anim.__getValue()).toBe(15);
});
it('convert to JSON', () => {
expect(JSON.stringify(new Animated.Value(10))).toBe('10');
});
});
@@ -294,4 +294,14 @@ describe('Interpolation', () => {
outputRange: ['20deg', '30rad'],
})).toThrow();
});
it('should round the alpha channel of a color to the nearest thousandth', () => {
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 1)'],
});
expect(interpolation(1e-12)).toBe('rgba(0, 0, 0, 0)');
expect(interpolation(2 / 3)).toBe('rgba(0, 0, 0, 0.667)');
});
});
+2 -2
View File
@@ -17,6 +17,7 @@ var ReactNative = require('ReactNative');
var invariant = require('fbjs/lib/invariant');
var renderApplication = require('renderApplication');
const infoLog = require('infoLog');
if (__DEV__) {
// In order to use Cmd+P to record/dump perf data, we need to make sure
@@ -87,8 +88,7 @@ var AppRegistry = {
'__DEV__ === ' + String(__DEV__) +
', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') +
', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON');
console.log(msg);
BugReporting.init();
infoLog(msg);
BugReporting.addSource('AppRegistry.runApplication' + runCount++, () => msg);
invariant(
runnables[appKey] && runnables[appKey].run,
+1 -2
View File
@@ -13,8 +13,7 @@
const MessageQueue = require('MessageQueue');
const BatchedBridge = new MessageQueue(
__fbBatchedBridgeConfig.remoteModuleConfig,
__fbBatchedBridgeConfig.localModulesConfig,
() => global.__fbBatchedBridgeConfig
);
// TODO: Move these around to solve the cycle in a cleaner way.
@@ -13,7 +13,6 @@
const BatchedBridge = require('BatchedBridge');
const RemoteModules = BatchedBridge.RemoteModules;
const Platform = require('Platform');
function normalizePrefix(moduleName: string): string {
return moduleName.replace(/^(RCT|RK)/, '');
@@ -52,67 +51,14 @@ Object.keys(RemoteModules).forEach((moduleName) => {
module = config && BatchedBridge.processModuleConfig(config, module.moduleID);
RemoteModules[moduleName] = module;
}
Object.defineProperty(NativeModules, moduleName, {
configurable: true,
enumerable: true,
value: module,
});
return module;
},
});
});
/**
* Copies the ViewManager constants and commands into UIManager. This is
* only needed for iOS, which puts the constants in the ViewManager
* namespace instead of UIManager, unlike Android.
*
* We'll eventually move this logic to UIManager.js, once all
* the call sites accessing NativeModules.UIManager directly have
* been removed #9344445
*/
if (Platform.OS === 'ios') {
const UIManager = NativeModules.UIManager;
UIManager && Object.keys(UIManager).forEach(viewName => {
const viewConfig = UIManager[viewName];
if (viewConfig.Manager) {
let constants;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Constants', {
configurable: true,
enumerable: true,
get: () => {
if (constants) {
return constants;
}
constants = {};
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
viewManager && Object.keys(viewManager).forEach(key => {
const value = viewManager[key];
if (typeof value !== 'function') {
constants[key] = value;
}
});
return constants;
},
});
let commands;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Commands', {
configurable: true,
enumerable: true,
get: () => {
if (commands) {
return commands;
}
commands = {};
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
viewManager && Object.keys(viewManager).forEach((key, index) => {
const value = viewManager[key];
if (typeof value === 'function') {
commands[key] = index;
}
});
return commands;
},
});
}
});
}
module.exports = NativeModules;
@@ -11,13 +11,15 @@
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
const BatchedBridge = require('BatchedBridge');
BatchedBridge.registerCallableModule(
'RCTEventEmitter',
ReactNativeEventEmitter
);
const RCTEventEmitter = {
register(eventEmitter: any) {
BatchedBridge.registerCallableModule(
'RCTEventEmitter',
eventEmitter
);
}
};
// Completely locally implemented - no native hooks.
module.exports = ReactNativeEventEmitter;
module.exports = RCTEventEmitter;
+45 -18
View File
@@ -11,13 +11,19 @@
*/
'use strict';
const BugReportingNativeModule = require('NativeModules').BugReporting;
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
const Map = require('Map');
const infoLog = require('infoLog');
import type {EmitterSubscription} from 'EventEmitter';
import type EmitterSubscription from 'EmitterSubscription';
type ExtraData = { [key: string]: string };
type SourceCallback = () => string;
type DebugData = { extras: ExtraData, files: ExtraData };
function defaultExtras() {
BugReporting.addFileSource('react_hierarchy.txt', () => require('dumpReactTree')());
}
/**
* A simple class for collecting bug report data. Components can add sources that will be queried when a bug report
@@ -26,17 +32,15 @@ type SourceCallback = () => string;
* returned by `addSource` when they are unmounted.
*/
class BugReporting {
static _extraSources: Map<string, SourceCallback> = new Map();
static _fileSources: Map<string, SourceCallback> = new Map();
static _subscription: ?EmitterSubscription = null;
static _sources: Map<string, SourceCallback> = new Map();
static _subscription: EmitterSubscription = null;
/**
* `init` is called in `AppRegistry.runApplication`, so you shouldn't have to worry about it.
*/
static init() {
static _maybeInit() {
if (!BugReporting._subscription) {
BugReporting._subscription = RCTDeviceEventEmitter
.addListener('collectBugExtraData', BugReporting.collectExtraData, null);
defaultExtras();
}
}
@@ -49,11 +53,28 @@ class BugReporting {
* Conflicts trample with a warning.
*/
static addSource(key: string, callback: SourceCallback): {remove: () => void} {
if (BugReporting._sources.has(key)) {
console.warn(`BugReporting.addSource called multiple times for same key '${key}'`);
return this._addSource(key, callback, BugReporting._extraSources);
}
/**
* Maps a string key to a simple callback that should return a string payload to be attached
* to a bug report. Source callbacks are called when `collectExtraData` is called.
*
* Returns an object to remove the source when the component unmounts.
*
* Conflicts trample with a warning.
*/
static addFileSource(key: string, callback: SourceCallback): {remove: () => void} {
return this._addSource(key, callback, BugReporting._fileSources);
}
static _addSource(key: string, callback: SourceCallback, source: Map<string, SourceCallback>): {remove: () => void} {
BugReporting._maybeInit();
if (source.has(key)) {
console.warn(`BugReporting.add* called multiple times for same key '${key}'`);
}
BugReporting._sources.set(key, callback);
return {remove: () => { BugReporting._sources.delete(key); }};
source.set(key, callback);
return {remove: () => { source.delete(key); }};
}
/**
@@ -62,16 +83,22 @@ class BugReporting {
* If available, this will call `NativeModules.BugReporting.setExtraData(extraData)`
* after collecting `extraData`.
*/
static collectExtraData(): ExtraData {
static collectExtraData(): DebugData {
const extraData: ExtraData = {};
for (const [key, callback] of BugReporting._sources) {
for (const [key, callback] of BugReporting._extraSources) {
extraData[key] = callback();
}
console.log('BugReporting extraData:', extraData);
const fileData: ExtraData = {};
for (const [key, callback] of BugReporting._fileSources) {
fileData[key] = callback();
}
infoLog('BugReporting extraData:', extraData);
const BugReportingNativeModule = require('NativeModules').BugReporting;
BugReportingNativeModule &&
BugReportingNativeModule.setExtraData &&
BugReportingNativeModule.setExtraData(extraData);
return extraData;
BugReportingNativeModule.setExtraData(extraData, fileData);
return { extras: extraData, files: fileData };
}
}
+142
View File
@@ -0,0 +1,142 @@
/**
* Copyright (c) 2013-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 dumpReactTree
* @flow
*/
'use strict';
const ReactNativeMount = require('ReactNativeMount');
const getReactData = require('getReactData');
const INDENTATION_SIZE = 2;
const MAX_DEPTH = 2;
const MAX_STRING_LENGTH = 50;
/**
* Dump all React Native root views and their content. This function tries
* it best to get the content but ultimately relies on implementation details
* of React and will fail in future versions.
*/
function dumpReactTree() {
try {
return getReactTree();
} catch (e) {
return 'Failed to dump react tree: ' + e;
}
}
function getReactTree() {
let output = '';
const rootIds = Object.getOwnPropertyNames(ReactNativeMount._instancesByContainerID);
for (const rootId of rootIds) {
const instance = ReactNativeMount._instancesByContainerID[rootId];
output += `============ Root ID: ${rootId} ============\n`;
output += dumpNode(instance, 0);
output += `============ End root ID: ${rootId} ============\n`;
}
return output;
}
function dumpNode(node: Object, identation: number) {
const data = getReactData(node);
if (data.nodeType === 'Text') {
return indent(identation) + data.text + '\n';
} else if (data.nodeType === 'Empty') {
return '';
}
let output = indent(identation) + `<${data.name}`;
if (data.nodeType === 'Composite') {
for (const propName of Object.getOwnPropertyNames(data.props || {})) {
if (isNormalProp(propName)) {
try {
const value = convertValue(data.props[propName]);
if (value) {
output += ` ${propName}=${value}`;
}
} catch (e) {
const message = `[Failed to get property: ${e}]`;
output += ` ${propName}=${message}`;
}
}
}
}
let childOutput = '';
for (const child of data.children || []) {
childOutput += dumpNode(child, identation + 1);
}
if (childOutput) {
output += '>\n' + childOutput + indent(identation) + `</${data.name}>\n`;
} else {
output += ' />\n';
}
return output;
}
function isNormalProp(name: string): boolean {
switch (name) {
case 'children':
case 'key':
case 'ref':
return false;
default:
return true;
}
}
function convertObject(object: Object, depth: number) {
if (depth >= MAX_DEPTH) {
return '[...omitted]';
}
let output = '{';
let first = true;
for (const key of Object.getOwnPropertyNames(object)) {
if (!first) {
output += ', ';
}
output += `${key}: ${convertValue(object[key], depth + 1)}`;
first = false;
}
return output + '}';
}
function convertValue(value, depth = 0): ?string {
if (!value) {
return null;
}
switch (typeof value) {
case 'string':
return JSON.stringify(possiblyEllipsis(value).replace('\n', '\\n'));
case 'boolean':
case 'number':
return JSON.stringify(value);
case 'function':
return '[function]';
case 'object':
return convertObject(value, depth);
default:
return null;
}
}
function possiblyEllipsis(value: string) {
if (value.length > MAX_STRING_LENGTH) {
return value.slice(0, MAX_STRING_LENGTH) + '...';
} else {
return value;
}
}
function indent(size: number) {
return ' '.repeat(size * INDENTATION_SIZE);
}
module.exports = dumpReactTree;
+168
View File
@@ -0,0 +1,168 @@
/**
* 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 getReactData
* @flow
*/
'use strict';
/**
* Convert a react internal instance to a sanitized data object.
*
* This is shamelessly stolen from react-devtools:
* https://github.com/facebook/react-devtools/blob/master/backend/getData.js
*/
function getData(element: Object): Object {
var children = null;
var props = null;
var state = null;
var context = null;
var updater = null;
var name = null;
var type = null;
var text = null;
var publicInstance = null;
var nodeType = 'Native';
// If the parent is a native node without rendered children, but with
// multiple string children, then the `element` that gets passed in here is
// a plain value -- a string or number.
if (typeof element !== 'object') {
nodeType = 'Text';
text = element + '';
} else if (element._currentElement === null || element._currentElement === false) {
nodeType = 'Empty';
} else if (element._renderedComponent) {
nodeType = 'NativeWrapper';
children = [element._renderedComponent];
props = element._instance.props;
state = element._instance.state;
context = element._instance.context;
if (context && Object.keys(context).length === 0) {
context = null;
}
} else if (element._renderedChildren) {
children = childrenList(element._renderedChildren);
} else if (element._currentElement && element._currentElement.props) {
// This is a native node without rendered children -- meaning the children
// prop is just a string or (in the case of the <option>) a list of
// strings & numbers.
children = element._currentElement.props.children;
}
if (!props && element._currentElement && element._currentElement.props) {
props = element._currentElement.props;
}
// != used deliberately here to catch undefined and null
if (element._currentElement != null) {
type = element._currentElement.type;
if (typeof type === 'string') {
name = type;
} else if (element.getName) {
nodeType = 'Composite';
name = element.getName();
// 0.14 top-level wrapper
// TODO(jared): The backend should just act as if these don't exist.
if (element._renderedComponent && element._currentElement.props === element._renderedComponent._currentElement) {
nodeType = 'Wrapper';
}
if (name === null) {
name = 'No display name';
}
} else if (element._stringText) {
nodeType = 'Text';
text = element._stringText;
} else {
name = type.displayName || type.name || 'Unknown';
}
}
if (element._instance) {
var inst = element._instance;
updater = {
setState: inst.setState && inst.setState.bind(inst),
forceUpdate: inst.forceUpdate && inst.forceUpdate.bind(inst),
setInProps: inst.forceUpdate && setInProps.bind(null, element),
setInState: inst.forceUpdate && setInState.bind(null, inst),
setInContext: inst.forceUpdate && setInContext.bind(null, inst),
};
publicInstance = inst;
// TODO: React ART currently falls in this bucket, but this doesn't
// actually make sense and we should clean this up after stabilizing our
// API for backends
if (inst._renderedChildren) {
children = childrenList(inst._renderedChildren);
}
}
return {
nodeType,
type,
name,
props,
state,
context,
children,
text,
updater,
publicInstance,
};
}
function setInProps(internalInst, path: Array<string | number>, value: any) {
var element = internalInst._currentElement;
internalInst._currentElement = {
...element,
props: copyWithSet(element.props, path, value),
};
internalInst._instance.forceUpdate();
}
function setInState(inst, path: Array<string | number>, value: any) {
setIn(inst.state, path, value);
inst.forceUpdate();
}
function setInContext(inst, path: Array<string | number>, value: any) {
setIn(inst.context, path, value);
inst.forceUpdate();
}
function setIn(obj: Object, path: Array<string | number>, value: any) {
var last = path.pop();
var parent = path.reduce((obj_, attr) => obj_ ? obj_[attr] : null, obj);
if (parent) {
parent[last] = value;
}
}
function childrenList(children) {
var res = [];
for (var name in children) {
res.push(children[name]);
}
return res;
}
function copyWithSetImpl(obj, path, idx, value) {
if (idx >= path.length) {
return value;
}
var key = path[idx];
var updated = Array.isArray(obj) ? obj.slice() : {...obj};
// $FlowFixMe number or string is fine here
updated[key] = copyWithSetImpl(obj[key], path, idx + 1, value);
return updated;
}
function copyWithSet(obj: Object | Array<any>, path: Array<string | number>, value: any): Object | Array<any> {
return copyWithSetImpl(obj, path, 0, value);
}
module.exports = getData;
+9 -2
View File
@@ -108,8 +108,15 @@ didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
BOOL isMovie = [mediaType isEqualToString:(NSString *)kUTTypeMovie];
NSString *key = isMovie ? UIImagePickerControllerMediaURL : UIImagePickerControllerReferenceURL;
NSURL *imageURL = info[key];
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSNumber *width = 0;
NSNumber *height = 0;
if (image) {
height = @(image.size.height);
width = @(image.size.width);
}
if (imageURL) {
[self _dismissPicker:picker args:@[imageURL.absoluteString]];
[self _dismissPicker:picker args:@[imageURL.absoluteString, RCTNullIfNil(height), RCTNullIfNil(width)]];
return;
}
@@ -120,7 +127,7 @@ didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
// WARNING: Using ImageStoreManager may cause a memory leak because the
// image isn't automatically removed from store once we're done using it.
[_bridge.imageStoreManager storeImage:originalImage withBlock:^(NSString *tempImageTag) {
[self _dismissPicker:picker args:tempImageTag ? @[tempImageTag] : nil];
[self _dismissPicker:picker args:tempImageTag ? @[tempImageTag, height, width] : nil];
}];
}
@@ -127,7 +127,7 @@ var DrawerLayoutAndroid = React.createClass({
* - idle, meaning there is no interaction with the navigation view happening at the time
* - dragging, meaning there is currently an interaction with the navigation view
* - settling, meaning that there was an interaction with the navigation view, and the
* navigation view is now finishing it's closing or opening animation
* navigation view is now finishing its closing or opening animation
*/
onDrawerStateChanged: ReactPropTypes.func,
/**
@@ -6,19 +6,12 @@
* 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 RCTDeviceEventEmitter
* @providesModule Keyboard
* @flow
*/
'use strict';
var EventEmitter = require('EventEmitter');
var BatchedBridge = require('BatchedBridge');
const NativeEventEmitter = require('NativeEventEmitter');
const KeyboardObserver = require('NativeModules').KeyboardObserver;
var RCTDeviceEventEmitter = new EventEmitter();
BatchedBridge.registerCallableModule(
'RCTDeviceEventEmitter',
RCTDeviceEventEmitter
);
module.exports = RCTDeviceEventEmitter;
module.exports = new NativeEventEmitter(KeyboardObserver);
@@ -308,6 +308,15 @@ var NavigatorIOS = React.createClass({
*/
translucent: PropTypes.bool,
/**
* A Boolean value that indicates whether the interactive pop gesture is enabled. Useful
* for enabling/disabling the back swipe navigation gesture. If this prop is not provided,
* the default behavior is for the back swipe gesture to be enabled when the navigation bar
* is shown and disabled when the navigation bar is hidden. Once you've provided
* the interactivePopGestureEnabled prop, you can never restore the default behavior.
*/
interactivePopGestureEnabled: PropTypes.bool,
},
navigator: (undefined: ?Object),
@@ -686,7 +695,8 @@ var NavigatorIOS = React.createClass({
style={styles.transitioner}
vertical={this.props.vertical}
requestedTopOfStack={this.state.requestedTopOfStack}
onNavigationComplete={this._handleNavigationComplete}>
onNavigationComplete={this._handleNavigationComplete}
interactivePopGestureEnabled={this.props.interactivePopGestureEnabled}>
{items}
</NavigatorTransitionerIOS>
</StaticContainer>
@@ -52,7 +52,11 @@ var PickerAndroid = React.createClass({
},
getInitialState: function() {
return this._stateFromProps(this.props);
var state = this._stateFromProps(this.props);
return {
...state,
initialSelectedIndex: state.selectedIndex,
};
},
componentWillReceiveProps: function(nextProps) {
@@ -87,7 +91,7 @@ var PickerAndroid = React.createClass({
mode: this.props.mode,
onSelect: this._onChange,
prompt: this.props.prompt,
selected: this.state.selectedIndex,
selected: this.state.initialSelectedIndex,
testID: this.props.testID,
style: [styles.pickerAndroid, this.props.style],
};
@@ -105,15 +109,24 @@ var PickerAndroid = React.createClass({
this.props.onValueChange(null, position);
}
}
this._lastNativePosition = event.nativeEvent.position;
this.forceUpdate();
},
componentDidMount: function() {
this._lastNativePosition = this.state.initialSelectedIndex;
},
componentDidUpdate: function() {
// The picker is a controlled component. This means we expect the
// on*Change handlers to be in charge of updating our
// `selectedValue` prop. That way they can also
// disallow/undo/mutate the selection of certain values. In other
// words, the embedder of this component should be the source of
// truth, not the native component.
if (this.refs[REF_PICKER] && this.state.selectedIndex !== event.nativeEvent.position) {
if (this.refs[REF_PICKER] && this.state.selectedIndex !== this._lastNativePosition) {
this.refs[REF_PICKER].setNativeProps({selected: this.state.selectedIndex});
this._lastNativePosition = this.state.selectedIndex;
}
},
});
@@ -20,7 +20,7 @@ const View = require('View');
const requireNativeComponent = require('requireNativeComponent');
if (Platform.OS === 'android') {
var RefreshLayoutConsts = require('NativeModules').UIManager.AndroidSwipeRefreshLayout.Constants;
var RefreshLayoutConsts = require('UIManager').AndroidSwipeRefreshLayout.Constants;
} else {
var RefreshLayoutConsts = {SIZE: {}};
}
@@ -86,7 +86,7 @@ const RefreshControl = React.createClass({
/**
* Whether the view should be indicating an active refresh.
*/
refreshing: React.PropTypes.bool,
refreshing: React.PropTypes.bool.isRequired,
/**
* The color of the refresh indicator.
* @platform ios
@@ -121,10 +121,32 @@ const RefreshControl = React.createClass({
* Size of the refresh indicator, see RefreshControl.SIZE.
* @platform android
*/
size: React.PropTypes.oneOf(RefreshLayoutConsts.SIZE.DEFAULT, RefreshLayoutConsts.SIZE.LARGE),
size: React.PropTypes.oneOf([RefreshLayoutConsts.SIZE.DEFAULT, RefreshLayoutConsts.SIZE.LARGE]),
/**
* Progress view top offset
* @platform android
*/
progressViewOffset: React.PropTypes.number,
},
_nativeRef: {},
_nativeRef: (null: any),
_lastNativeRefreshing: false,
componentDidMount() {
this._lastNativeRefreshing = this.props.refreshing;
},
componentDidUpdate(prevProps: {refreshing: boolean}) {
// RefreshControl is a controlled component so if the native refreshing
// value doesn't match the current js refreshing prop update it to
// the js value.
if (this.props.refreshing !== prevProps.refreshing) {
this._lastNativeRefreshing = this.props.refreshing;
} else if (this.props.refreshing !== this._lastNativeRefreshing) {
this._nativeRef.setNativeProps({refreshing: this.props.refreshing});
this._lastNativeRefreshing = this.props.refreshing;
}
},
render() {
return (
@@ -137,11 +159,13 @@ const RefreshControl = React.createClass({
},
_onRefresh() {
this._lastNativeRefreshing = true;
this.props.onRefresh && this.props.onRefresh();
if (this._nativeRef) {
this._nativeRef.setNativeProps({refreshing: this.props.refreshing});
}
// The native component will start refreshing so force an update to
// make sure it stays in sync with the js component.
this.forceUpdate();
},
});
+5 -5
View File
@@ -13,7 +13,7 @@
var Dimensions = require('Dimensions');
var Platform = require('Platform');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var Keyboard = require('Keyboard');
var ReactNative = require('ReactNative');
var Subscribable = require('Subscribable');
var TextInputState = require('TextInputState');
@@ -476,10 +476,10 @@ var ScrollResponderMixin = {
componentWillMount: function() {
this.keyboardWillOpenTo = null;
this.additionalScrollOffset = 0;
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', this.scrollResponderKeyboardWillShow);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillHide', this.scrollResponderKeyboardWillHide);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardDidShow', this.scrollResponderKeyboardDidShow);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardDidHide', this.scrollResponderKeyboardDidHide);
this.addListenerOn(Keyboard, 'keyboardWillShow', this.scrollResponderKeyboardWillShow);
this.addListenerOn(Keyboard, 'keyboardWillHide', this.scrollResponderKeyboardWillHide);
this.addListenerOn(Keyboard, 'keyboardDidShow', this.scrollResponderKeyboardDidShow);
this.addListenerOn(Keyboard, 'keyboardDidHide', this.scrollResponderKeyboardDidHide);
},
/**
+70 -55
View File
@@ -11,30 +11,26 @@
*/
'use strict';
var ColorPropType = require('ColorPropType');
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var Platform = require('Platform');
var PointPropType = require('PointPropType');
var RCTScrollView = require('NativeModules').UIManager.RCTScrollView;
var RCTScrollViewManager = require('NativeModules').ScrollViewManager;
var React = require('React');
var ReactNative = require('ReactNative');
var ScrollResponder = require('ScrollResponder');
var StyleSheet = require('StyleSheet');
var StyleSheetPropType = require('StyleSheetPropType');
var View = require('View');
var ViewStylePropTypes = require('ViewStylePropTypes');
const ColorPropType = require('ColorPropType');
const EdgeInsetsPropType = require('EdgeInsetsPropType');
const Platform = require('Platform');
const PointPropType = require('PointPropType');
const RCTScrollViewManager = require('NativeModules').ScrollViewManager;
const React = require('React');
const ReactNative = require('ReactNative');
const ScrollResponder = require('ScrollResponder');
const StyleSheet = require('StyleSheet');
const StyleSheetPropType = require('StyleSheetPropType');
const View = require('View');
const ViewStylePropTypes = require('ViewStylePropTypes');
var deprecatedPropType = require('deprecatedPropType');
var dismissKeyboard = require('dismissKeyboard');
var flattenStyle = require('flattenStyle');
var invariant = require('fbjs/lib/invariant');
var requireNativeComponent = require('requireNativeComponent');
var processDecelerationRate = require('processDecelerationRate');
var PropTypes = React.PropTypes;
var SCROLLVIEW = 'ScrollView';
var INNERVIEW = 'InnerScrollView';
const deprecatedPropType = require('deprecatedPropType');
const dismissKeyboard = require('dismissKeyboard');
const flattenStyle = require('flattenStyle');
const invariant = require('fbjs/lib/invariant');
const processDecelerationRate = require('processDecelerationRate');
const PropTypes = React.PropTypes;
const requireNativeComponent = require('requireNativeComponent');
/**
* Component that wraps platform ScrollView while providing
@@ -51,7 +47,7 @@ var INNERVIEW = 'InnerScrollView';
* Doesn't yet support other contained responders from blocking this scroll
* view from becoming the responder.
*/
var ScrollView = React.createClass({
const ScrollView = React.createClass({
propTypes: {
...View.propTypes,
/**
@@ -119,7 +115,7 @@ var ScrollView = React.createClass({
* </ScrollView>
* );
* ...
* var styles = StyleSheet.create({
* const styles = StyleSheet.create({
* contentContainer: {
* paddingVertical: 20
* }
@@ -222,7 +218,6 @@ var ScrollView = React.createClass({
* 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,
/**
@@ -332,6 +327,15 @@ var ScrollView = React.createClass({
* @platform android
*/
endFillColor: ColorPropType,
/**
* Tag used to log scroll performance on this scroll view. Will force
* momentum events to be turned on (see sendMomentumEvents). This doesn't do
* anything out of the box and you need to implement a custom native
* FpsListener for it to be useful.
* @platform android
*/
scrollPerfTag: PropTypes.string,
},
mixins: [ScrollResponder.Mixin],
@@ -341,7 +345,7 @@ var ScrollView = React.createClass({
},
setNativeProps: function(props: Object) {
this.refs[SCROLLVIEW].setNativeProps(props);
this._scrollViewRef && this._scrollViewRef.setNativeProps(props);
},
/**
@@ -359,16 +363,16 @@ var ScrollView = React.createClass({
* implement this method so that they can be composed while providing access
* to the underlying scroll responder's methods.
*/
getScrollResponder: function(): ReactComponent {
getScrollResponder: function(): ScrollView {
return this;
},
getScrollableNode: function(): any {
return ReactNative.findNodeHandle(this.refs[SCROLLVIEW]);
return ReactNative.findNodeHandle(this._scrollViewRef);
},
getInnerViewNode: function(): any {
return ReactNative.findNodeHandle(this.refs[INNERVIEW]);
return ReactNative.findNodeHandle(this._innerViewRef);
},
/**
@@ -392,7 +396,6 @@ var ScrollView = React.createClass({
} else {
({x, y, animated} = y || {});
}
// $FlowFixMe - Don't know how to pass Mixin correctly. Postpone for now
this.getScrollResponder().scrollResponderScrollTo({x: x || 0, y: y || 0, animated: animated !== false});
},
@@ -407,7 +410,7 @@ var ScrollView = React.createClass({
_handleScroll: function(e: Object) {
if (__DEV__) {
if (this.props.onScroll && !this.props.scrollEventThrottle && Platform.OS === 'ios') {
console.log(
console.log( // eslint-disable-line no-console-disallow
'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 ' +
@@ -425,18 +428,29 @@ var ScrollView = React.createClass({
},
_handleContentOnLayout: function(e: Object) {
var {width, height} = e.nativeEvent.layout;
const {width, height} = e.nativeEvent.layout;
this.props.onContentSizeChange && this.props.onContentSizeChange(width, height);
},
_scrollViewRef: (null: ?ScrollView),
_setScrollViewRef: function(ref: ?ScrollView) {
this._scrollViewRef = ref;
},
_innerViewRef: (null: ?View),
_setInnerViewRef: function(ref: ?View) {
this._innerViewRef = ref;
},
render: function() {
var contentContainerStyle = [
const contentContainerStyle = [
this.props.horizontal && styles.contentContainerHorizontal,
this.props.contentContainerStyle,
];
let style, childLayoutProps;
if (__DEV__ && this.props.style) {
var style = flattenStyle(this.props.style);
var childLayoutProps = ['alignItems', 'justifyContent']
style = flattenStyle(this.props.style);
childLayoutProps = ['alignItems', 'justifyContent']
.filter((prop) => style && style[prop] !== undefined);
invariant(
childLayoutProps.length === 0,
@@ -445,34 +459,34 @@ var ScrollView = React.createClass({
);
}
var contentSizeChangeProps = {};
let contentSizeChangeProps = {};
if (this.props.onContentSizeChange) {
contentSizeChangeProps = {
onLayout: this._handleContentOnLayout,
};
}
var contentContainer =
const contentContainer =
<View
{...contentSizeChangeProps}
ref={INNERVIEW}
ref={this._setInnerViewRef}
style={contentContainerStyle}
removeClippedSubviews={this.props.removeClippedSubviews}
collapsable={false}>
{this.props.children}
</View>;
var alwaysBounceHorizontal =
const alwaysBounceHorizontal =
this.props.alwaysBounceHorizontal !== undefined ?
this.props.alwaysBounceHorizontal :
this.props.horizontal;
var alwaysBounceVertical =
const alwaysBounceVertical =
this.props.alwaysBounceVertical !== undefined ?
this.props.alwaysBounceVertical :
!this.props.horizontal;
var props = {
const props = {
...this.props,
alwaysBounceHorizontal,
alwaysBounceVertical,
@@ -496,7 +510,7 @@ var ScrollView = React.createClass({
sendMomentumEvents: (this.props.onMomentumScrollBegin || this.props.onMomentumScrollEnd) ? true : false,
};
var onRefreshStart = this.props.onRefreshStart;
const onRefreshStart = this.props.onRefreshStart;
if (onRefreshStart) {
// this is necessary because if we set it on props, even when empty,
// it'll trigger the default pull-to-refresh behavior on native.
@@ -504,12 +518,12 @@ var ScrollView = React.createClass({
function() { onRefreshStart && onRefreshStart(this.endRefreshing); }.bind(this);
}
var { decelerationRate } = this.props;
const { decelerationRate } = this.props;
if (decelerationRate) {
props.decelerationRate = processDecelerationRate(decelerationRate);
}
var ScrollViewClass;
let ScrollViewClass;
if (Platform.OS === 'ios') {
ScrollViewClass = RCTScrollView;
} else if (Platform.OS === 'android') {
@@ -524,12 +538,12 @@ var ScrollView = React.createClass({
'ScrollViewClass must not be undefined'
);
var refreshControl = this.props.refreshControl;
const refreshControl = this.props.refreshControl;
if (refreshControl) {
if (Platform.OS === 'ios') {
// On iOS the RefreshControl is a child of the ScrollView.
return (
<ScrollViewClass {...props} ref={SCROLLVIEW}>
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
{refreshControl}
{contentContainer}
</ScrollViewClass>
@@ -541,21 +555,21 @@ var ScrollView = React.createClass({
return React.cloneElement(
refreshControl,
{style: props.style},
<ScrollViewClass {...props} style={styles.base} ref={SCROLLVIEW}>
<ScrollViewClass {...props} style={styles.base} ref={this._setScrollViewRef}>
{contentContainer}
</ScrollViewClass>
);
}
}
return (
<ScrollViewClass {...props} ref={SCROLLVIEW}>
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
{contentContainer}
</ScrollViewClass>
);
}
});
var styles = StyleSheet.create({
const styles = StyleSheet.create({
base: {
flex: 1,
},
@@ -565,20 +579,21 @@ var styles = StyleSheet.create({
},
});
let nativeOnlyProps, AndroidScrollView, AndroidHorizontalScrollView, RCTScrollView;
if (Platform.OS === 'android') {
var nativeOnlyProps = {
nativeOnlyProps = {
nativeOnly: {
sendMomentumEvents: true,
}
};
var AndroidScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
var AndroidHorizontalScrollView = requireNativeComponent(
AndroidScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
AndroidHorizontalScrollView = requireNativeComponent(
'AndroidHorizontalScrollView',
ScrollView,
nativeOnlyProps
);
} else if (Platform.OS === 'ios') {
var nativeOnlyProps = {
nativeOnlyProps = {
nativeOnly: {
onMomentumScrollBegin: true,
onMomentumScrollEnd : true,
@@ -586,7 +601,7 @@ if (Platform.OS === 'android') {
onScrollEndDrag: true,
}
};
var RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
}
module.exports = ScrollView;
@@ -11,4 +11,6 @@
*/
'use strict';
module.exports = null;
const NativeEventEmitter = require('NativeEventEmitter');
module.exports = new NativeEventEmitter('StatusBarManager');
@@ -11,24 +11,26 @@
*/
'use strict';
const NativeEventEmitter = require('NativeEventEmitter');
const StatusBar = require('StatusBar');
const StatusBarManager = require('NativeModules').StatusBarManager;
import type {StatusBarStyle, StatusBarAnimation} from 'StatusBar';
/**
* Deprecated. Use `StatusBar` instead.
*/
const StatusBarIOS = {
class StatusBarIOS extends NativeEventEmitter {
setStyle(style: StatusBarStyle, animated?: boolean) {
console.warn('`StatusBarIOS.setStyle` is deprecated. Use `StatusBar.setBarStyle` instead.');
StatusBar.setBarStyle(style, animated);
},
}
setHidden(hidden: boolean, animation?: StatusBarAnimation) {
console.warn('`StatusBarIOS.setHidden` is deprecated. Use `StatusBar.setHidden` instead.');
StatusBar.setHidden(hidden, animation);
},
}
setNetworkActivityIndicatorVisible(visible: boolean) {
console.warn(
@@ -36,7 +38,7 @@ const StatusBarIOS = {
'Use `StatusBar.setNetworkActivityIndicatorVisible` instead.'
);
StatusBar.setNetworkActivityIndicatorVisible(visible);
},
};
}
}
module.exports = StatusBarIOS;
module.exports = new StatusBarIOS(StatusBarManager);
+43 -8
View File
@@ -120,6 +120,7 @@ var TextInput = React.createClass({
* Determines which keyboard to open, e.g.`numeric`.
*
* The following values work across platforms:
*
* - default
* - numeric
* - email-address
@@ -150,22 +151,54 @@ var TextInput = React.createClass({
'dark',
]),
/**
* Determines how the return key should look.
* @platform ios
* Determines how the return key should look. On Android you can also use
* `returnKeyLabel`.
*
* The following values work across platforms:
*
* - done
* - go
* - next
* - search
* - send
*
* The following values work on Android only:
*
* - none
* - previous
*
* The following values work on iOS only:
*
* - default
* - emergency-call
* - google
* - join
* - route
* - yahoo
*/
returnKeyType: PropTypes.oneOf([
'default',
// Cross-platform
'done',
'go',
'google',
'join',
'next',
'route',
'search',
'send',
'yahoo',
'done',
// Android-only
'none',
'previous',
// iOS-only
'default',
'emergency-call',
'google',
'join',
'route',
'yahoo',
]),
/**
* Sets the return key to the label. Use it instead of `returnKeyType`.
* @platform android
*/
returnKeyLabel: PropTypes.string,
/**
* Limits the maximum number of characters that can be entered. Use this
* instead of implementing the logic in JS to avoid flicker.
@@ -531,6 +564,8 @@ var TextInput = React.createClass({
children={children}
editable={this.props.editable}
selectTextOnFocus={this.props.selectTextOnFocus}
returnKeyType={this.props.returnKeyType}
returnKeyLabel={this.props.returnKeyLabel}
/>;
return (
@@ -128,7 +128,7 @@ var ViewPagerAndroid = React.createClass({
* When false, the content does not scroll.
* The default value is true.
*/
scrollEnabled: React.PropTypes.bool,
scrollEnabled: ReactPropTypes.bool,
},
componentDidMount: function() {
@@ -207,7 +207,7 @@ var ViewPagerAndroid = React.createClass({
/**
* A helper function to scroll to a specific page in the ViewPager.
* The transition between pages will be *not* be animated.
* The transition between pages will *not* be animated.
*/
setPageWithoutAnimation: function(selectedPage: number) {
UIManager.dispatchViewManagerCommand(
@@ -137,9 +137,10 @@ var ListView = React.createClass({
* a renderable component to be rendered as the row. By default the data
* is exactly what was put into the data source, but it's also possible to
* provide custom extractors. ListView can be notified when a row is
* being highlighted by calling highlightRow function. The separators above and
* below will be hidden when a row is highlighted. The highlighted state of
* a row can be reset by calling highlightRow(null).
* being highlighted by calling `highlightRow(sectionID, rowID)`. This
* sets a boolean value of adjacentRowHighlighted in renderSeparator, allowing you
* to control the separators above and below the highlighted row. The highlighted
* state of a row can be reset by calling highlightRow(null).
*/
renderRow: PropTypes.func.isRequired,
/**
@@ -595,12 +596,19 @@ var ListView = React.createClass({
var rowID = rowIDs[rowIdx];
var frame = this._childFrames[totalIndex];
totalIndex++;
if(this.props.renderSeparator &&
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)){
totalIndex++;
}
if (!frame) {
break;
}
var rowVisible = visibleSection[rowID];
var min = isVertical ? frame.y : frame.x;
var max = min + (isVertical ? frame.height : frame.width);
if ((!min && !max) || (min === max)) {
break;
}
if (min > visibleMax || max < visibleMin) {
if (rowVisible) {
visibilityChanged = true;
@@ -35,7 +35,6 @@
const Animated = require('Animated');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationContainer = require('NavigationContainer');
const NavigationPagerPanResponder = require('NavigationPagerPanResponder');
const NavigationPagerStyleInterpolator = require('NavigationPagerStyleInterpolator');
const NavigationPointerEventsContainer = require('NavigationPointerEventsContainer');
@@ -70,7 +69,8 @@ class SceneView extends React.Component<any, SceneViewProps, any> {
static propTypes = {
sceneRenderer: PropTypes.func.isRequired,
sceneRendererProps: NavigationPropTypes.SceneRenderer,
sceneRendererProps:
PropTypes.shape(NavigationPropTypes.SceneRenderer).isRequired,
};
shouldComponentUpdate(nextProps: SceneViewProps, nextState: any): boolean {
@@ -92,7 +92,7 @@ class NavigationCard extends React.Component<any, Props, any> {
props: Props;
static propTypes = {
...NavigationPropTypes.SceneRenderer,
...NavigationPropTypes.SceneRendererProps,
onComponentRef: PropTypes.func.isRequired,
panHandlers: NavigationPropTypes.panHandlers,
pointerEvents: PropTypes.string.isRequired,
@@ -138,6 +138,11 @@ class NavigationCard extends React.Component<any, Props, any> {
</Animated.View>
);
}
static CardStackPanResponder = NavigationCardStackPanResponder;
static CardStackStyleInterpolator = NavigationCardStackStyleInterpolator;
static PagerPanResponder = NavigationPagerPanResponder;
static PagerStyleInterpolator = NavigationPagerStyleInterpolator;
}
const styles = StyleSheet.create({
@@ -156,12 +161,5 @@ const styles = StyleSheet.create({
});
NavigationCard = NavigationPointerEventsContainer.create(NavigationCard);
NavigationCard = NavigationContainer.create(NavigationCard);
// Export these buil-in interaction modules.
NavigationCard.CardStackPanResponder = NavigationCardStackPanResponder;
NavigationCard.CardStackStyleInterpolator = NavigationCardStackStyleInterpolator;
NavigationCard.PagerPanResponder = NavigationPagerPanResponder;
NavigationCard.PagerStyleInterpolator = NavigationPagerStyleInterpolator;
module.exports = NavigationCard;
@@ -35,7 +35,6 @@
const NavigationAnimatedView = require('NavigationAnimatedView');
const NavigationCard = require('NavigationCard');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationContainer = require('NavigationContainer');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationPropTypes = require('NavigationPropTypes');
const React = require('React');
@@ -48,6 +47,7 @@ const {PropTypes} = React;
const {Directions} = NavigationCardStackPanResponder;
import type {
NavigationActionCaller,
NavigationParentState,
NavigationSceneRenderer,
NavigationSceneRendererProps,
@@ -60,6 +60,7 @@ import type {
type Props = {
direction: NavigationGestureDirection,
navigationState: NavigationParentState,
onNavigate: NavigationActionCaller,
renderOverlay: ?NavigationSceneRenderer,
renderScene: NavigationSceneRenderer,
};
@@ -89,6 +90,7 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
static propTypes = {
direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]),
navigationState: NavigationPropTypes.navigationParentState.isRequired,
onNavigate: NavigationPropTypes.SceneRendererProps.onNavigate,
renderOverlay: PropTypes.func,
renderScene: PropTypes.func.isRequired,
};
@@ -120,6 +122,7 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
navigationState={this.props.navigationState}
renderOverlay={this.props.renderOverlay}
renderScene={this._renderScene}
onNavigate={this.props.onNavigate}
// $FlowFixMe - style should be declared
style={[styles.animatedView, this.props.style]}
/>
@@ -155,4 +158,4 @@ const styles = StyleSheet.create({
},
});
module.exports = NavigationContainer.create(NavigationCardStack);
module.exports = NavigationCardStack;
@@ -34,7 +34,6 @@
const React = require('React');
const ReactNative = require('react-native');
const NavigationContainer = require('NavigationContainer');
const NavigationHeaderTitle = require('NavigationHeaderTitle');
const NavigationHeaderBackButton = require('NavigationHeaderBackButton');
const NavigationPropTypes = require('NavigationPropTypes');
@@ -49,6 +48,7 @@ const {
} = ReactNative;
import type {
NavigationActionCaller,
NavigationSceneRenderer,
NavigationSceneRendererProps,
NavigationStyleInterpolator,
@@ -64,8 +64,9 @@ type Props = NavigationSceneRendererProps & {
renderLeftComponent: NavigationSceneRenderer,
renderRightComponent: NavigationSceneRenderer,
renderTitleComponent: NavigationSceneRenderer,
style?: any;
viewProps?: any;
onNavigate: NavigationActionCaller,
style?: any,
viewProps?: any,
};
type SubViewName = 'left' | 'title' | 'right';
@@ -86,7 +87,14 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
},
renderLeftComponent: (props: NavigationSceneRendererProps) => {
return props.scene.index > 0 ? <NavigationHeaderBackButton /> : null;
if (props.scene.index === 0) {
return null;
}
return (
<NavigationHeaderBackButton
onNavigate={props.onNavigate}
/>
);
},
renderRightComponent: (props: NavigationSceneRendererProps) => {
@@ -95,7 +103,7 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
};
static propTypes = {
...NavigationPropTypes.SceneRenderer,
...NavigationPropTypes.SceneRendererProps,
renderLeftComponent: PropTypes.func,
renderRightComponent: PropTypes.func,
renderTitleComponent: PropTypes.func,
@@ -199,6 +207,11 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
</Animated.View>
);
}
static HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT;
static Title = NavigationHeaderTitle;
static BackButton = NavigationHeaderBackButton;
}
const styles = StyleSheet.create({
@@ -244,10 +257,4 @@ const styles = StyleSheet.create({
},
});
const NavigationHeaderContainer = NavigationContainer.create(NavigationHeader);
NavigationHeaderContainer.HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT;
NavigationHeaderContainer.Title = NavigationHeaderTitle;
NavigationHeaderContainer.BackButton = NavigationHeaderBackButton;
module.exports = NavigationHeaderContainer;
module.exports = NavigationHeader;
@@ -1,4 +1,11 @@
/**
* Copyright (c) 2013-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 examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -18,8 +25,6 @@
const React = require('react');
const ReactNative = require('react-native');
const NavigationContainer = require('NavigationContainer');
const NavigationRootContainer = require('NavigationRootContainer');
const {
Image,
@@ -33,7 +38,7 @@ type Props = {
}
const NavigationHeaderBackButton = (props: Props) => (
<TouchableOpacity style={styles.buttonContainer} onPress={() => props.onNavigate(NavigationRootContainer.getBackAction())}>
<TouchableOpacity style={styles.buttonContainer} onPress={() => props.onNavigate({type: 'BackAction'})}>
<Image style={styles.button} source={require('./assets/back-icon.png')} />
</TouchableOpacity>
);
@@ -57,4 +62,4 @@ const styles = StyleSheet.create({
}
});
module.exports = NavigationContainer.create(NavigationHeaderBackButton);
module.exports = NavigationHeaderBackButton;
@@ -1,446 +0,0 @@
/**
* Copyright (c) 2013-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.
*
* Facebook, Inc. ("Facebook") owns all right, title and interest, including
* all intellectual property and other proprietary rights, in and to the React
* Native CustomComponents software (the "Software"). Subject to your
* compliance with these terms, you are hereby granted a non-exclusive,
* worldwide, royalty-free copyright license to (1) use and copy the Software;
* and (2) reproduce and distribute the Software as part of your own software
* ("Your Software"). Facebook reserves all rights not expressly granted to
* you in this license agreement.
*
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @providesModule NavigationLegacyNavigator
* @flow
*/
'use strict';
const Animated = require('Animated');
const NavigationAnimatedValueSubscription = require('NavigationAnimatedValueSubscription');
const NavigationAnimatedView = require('NavigationAnimatedView');
const NavigationCard = require('NavigationCard');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationContext = require('NavigationContext');
const NavigationLegacyNavigatorRouteStack = require('NavigationLegacyNavigatorRouteStack');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationPagerPanResponder = require('NavigationPagerPanResponder');
const NavigationPagerStyleInterpolator = require('NavigationPagerStyleInterpolator');
const NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
const NavigatorNavigationBar = require('NavigatorNavigationBar');
const NavigatorSceneConfigs = require('NavigatorSceneConfigs');
const React = require('React');
const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
import type {
NavigationActionCaller,
NavigationAnimatedValue,
NavigationAnimationSetter,
NavigationParentState,
NavigationSceneRenderer,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
type Props = {
configureScene: any,
initialRoute: any,
initialRouteStack: any,
navigationBar: any,
navigationBarNavigator: any,
navigator: any,
onDidFocus: any,
onWillFocus: any,
renderScene: any,
renderScene: any,
style: any,
};
type State = {
presentedIndex: number,
routeStack: Array<any>,
};
const RouteStack = NavigationLegacyNavigatorRouteStack;
/**
* NavigationLegacyNavigator is meant to replace Navigator seemlessly with
* minimum API changes.
*
* While the APIs remain compatible with Navigator, it is built with good
* intention by using the new Navigation API such as
* `NavigationAnimatedView`...etc.
*/
class NavigationLegacyNavigator extends React.Component<any, Props, State> {
static BreadcrumbNavigationBar: any;
static NavigationBar: any;
static SceneConfigs: any;
_applyAnimation: NavigationAnimationSetter;
_navigationBarRef: any;
_onNavigationBarRef: (ref: any) => void;
_onPositionChange: (data: {value: number}) => void;
_positionListener: ?NavigationAnimatedValueSubscription;
_previousStack: NavigationLegacyNavigatorRouteStack;
_renderCard: NavigationSceneRenderer;
_renderHeader: NavigationSceneRenderer;
_renderScene: NavigationSceneRenderer;
_routeFocused: any;
_routeToFocus: any;
_onNavigate: NavigationActionCaller;
_stack: NavigationLegacyNavigatorRouteStack;
_useAnimation: boolean;
navigationContext: NavigationContext;
parentNavigator: any;
props: Props;
state: State;
constructor(props: Props, context: any) {
super(props, context);
const stack = this._getInitialRouteStack();
// Unfortunately, due to historical reasons, the `state` has been exposed
// as public members of the navigator, therefore we'd keep private state
// as private members.
this._previousStack = stack;
this._stack = stack;
this._useAnimation = false;
// Legacy members portred from `Navigator`.
this.parentNavigator = props.navigator;
this.navigationContext = new NavigationContext();
this.state = {
routeStack: stack.toArray(),
presentedIndex: stack.index,
};
}
jumpTo(route: any): void {
this._applyStack(this._stack.jumpTo(route));
}
jumpForward(): void {
this._applyStack(this._stack.jumpForward());
}
jumpBack(): void {
this._applyStack(this._stack.jumpBack());
}
push(route: any): void {
this._applyStack(this._stack.push(route));
}
pop(): void {
this._applyStack(this._stack.pop());
}
replaceAtIndex(route: any, index: number): void {
this._applyStack(this._stack.replaceAtIndex(index, route));
}
replace(route: any): void {
this.replaceAtIndex(route, this._stack.index);
}
replacePrevious(route: any): void {
this.replaceAtIndex(route, this._stack.index - 1);
}
popToTop(): void {
this._applyStack(this._stack.slice(0, 1));
}
popToRoute(route: any): void {
this._applyStack(this._stack.popToRoute(route));
}
replacePreviousAndPop(route: any): void {
this._applyStack(this._stack.replacePreviousAndPop(route));
}
resetTo(route: any): void {
this._applyStack(this._stack.resetTo(route));
}
immediatelyResetRouteStack(routes: Array<any>): void {
this._applyStack(this._stack.resetRoutes(routes), true);
}
getCurrentRoutes(): Array<any> {
return this._stack.toArray();
}
// Life cycle and private methods below.
shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
this,
nextProps,
nextState
);
}
componentWillMount(): void {
this._applyAnimation = this._applyAnimation.bind(this);
this._onNavigate = this._onNavigate.bind(this);
this._onNavigationBarRef = this._onNavigationBarRef.bind(this);
this._onPositionChange = this._onPositionChange.bind(this);
this._renderCard = this._renderCard.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderScene = this._renderScene.bind(this);
this._willFocus();
}
componentDidMount(): void {
this._didFocus();
}
componentWillUnmount(): void {
this._positionListener && this._positionListener.remove();
}
componentWillUpdate(nextProps: Props, nextState: State): void {
this._willFocus();
}
componentDidUpdate(prevProps: Props, prevState: State): void {
if (this._useAnimation) {
// will play animation.
return;
}
this._didFocus();
}
render(): ReactElement {
return (
<NavigationAnimatedView
applyAnimation={this._applyAnimation}
navigationState={this._stack.toNavigationState()}
onNavigate={this._onNavigate}
renderOverlay={this._renderHeader}
renderScene={this._renderCard}
style={this.props.style}
/>
);
}
_getInitialRouteStack(): RouteStack {
const {initialRouteStack, initialRoute} = this.props;
const routes = initialRouteStack || [initialRoute];
const index = initialRoute ?
routes.indexOf(initialRoute) :
routes.length - 1;
return new RouteStack(index, routes);
}
_renderHeader(props: NavigationSceneRendererProps): ?ReactElement {
// `_renderHeader` is always called before `_renderCard`. We should
// subscribe to the position here.
this._positionListener && this._positionListener.remove();
this._positionListener = new NavigationAnimatedValueSubscription(
props.position,
this._onPositionChange,
);
const {navigationBar, navigationBarNavigator} = this.props;
if (!navigationBar) {
return null;
}
return React.cloneElement(
navigationBar,
{
key: 'header_' + props.scene.key,
ref: this._onNavigationBarRef,
navigator: navigationBarNavigator || this,
navState: {...this.state},
}
);
}
_renderCard(props: NavigationSceneRendererProps): ReactElement {
const {scene} = props;
const {configureScene} = this.props;
// Default getters for style and pan responders.
let styleGetter = NavigationCardStackStyleInterpolator.forHorizontal;
let panResponderGetter = NavigationCardStackPanResponder.forHorizontal;
if (configureScene) {
const route = RouteStack.getRouteByNavigationState(scene.navigationState);
const config = configureScene(route, this.state.routeStack);
if (config) {
const gestures = config.gestures || {};
if (gestures.pop && gestures.pop.direction === 'left-to-right') {
// pass, will use default getters.
} else if (gestures.pop && gestures.pop.direction === 'top-to-bottom') {
styleGetter = NavigationCardStackStyleInterpolator.forVertical;
panResponderGetter = NavigationCardStackPanResponder.forVertical;
} else if (
gestures.jumpBack &&
gestures.jumpForward &&
gestures.jumpBack.direction === 'left-to-right' &&
gestures.jumpForward.direction === 'right-to-left'
) {
styleGetter = NavigationPagerStyleInterpolator.forHorizontal;
panResponderGetter = NavigationPagerPanResponder.forHorizontal;
} else if (__DEV__) {
console.warn('unsupported scene configuration', config);
}
}
}
const style = styleGetter(props);
const panHandlers = panResponderGetter(props);
return (
<NavigationCard
{...props}
key={'card_' + props.scene.key}
panHandlers={panHandlers}
renderScene={this._renderScene}
style={style}
/>
);
}
_renderScene(props: NavigationSceneRendererProps): ReactElement {
const {navigationState} = props.scene;
const route = RouteStack.getRouteByNavigationState(navigationState);
return this.props.renderScene(route, this);
}
_applyStack(
stack: NavigationLegacyNavigatorRouteStack,
noAnimation: ?boolean,
): void {
if (stack !== this._stack) {
this._previousStack = this._stack;
this._stack = stack;
this._useAnimation = noAnimation ||
this._previousStack.index !== stack.index;
this.setState({
presentedIndex: stack.index,
routeStack: stack.toArray(),
});
}
}
_onNavigationBarRef(navigationBarRef: any): void {
this._navigationBarRef = navigationBarRef;
const {navigationBar} = this.props;
if (navigationBar && typeof navigationBar.ref === 'function') {
navigationBar.ref(navigationBarRef);
}
}
_onPositionChange(data: {value: number}): void {
const fromIndex = this._previousStack.index;
const toIndex = this._stack.index;
if (
fromIndex !== toIndex &&
this._navigationBarRef &&
typeof this._navigationBarRef.updateProgress === 'function'
) {
const progress = (data.value - fromIndex) / (toIndex - fromIndex);
this._navigationBarRef.updateProgress(progress, fromIndex, toIndex);
}
const diff = this._stack.index - data.value;
// When animation stops, the `diff` can still be very a small non-zero value
// (e.g. 0.00000002). Call `willFocus` when `diff` is small enough.
if (diff < 0.05) {
this._didFocus();
}
}
_applyAnimation(
position: NavigationAnimatedValue,
nextState: NavigationParentState,
prevState: NavigationParentState,
): void {
const {index} = nextState;
if (!this._useAnimation) {
position.setValue(index);
return;
}
Animated.timing(
position,
{
duration: 500,
toValue: index,
}
).start();
}
_willFocus(): void {
const route = this._stack.get(this._stack.index);
if (this._routeToFocus === route) {
return;
}
this._routeToFocus = route;
this.navigationContext.emit('willfocus', {route: route});
this.props.onWillFocus && this.props.onWillFocus(route);
}
_didFocus(): void {
const route = this._stack.get(this._stack.index);
if (this._routeFocused === route) {
return;
}
this._routeFocused = route;
this.navigationContext.emit('didfocus', {route: route});
this.props.onDidFocus && this.props.onDidFocus(route);
}
_onNavigate(action: any): void {
switch (action) {
case NavigationCardStackPanResponder.Actions.BACK:
this.pop();
break;
case NavigationPagerPanResponder.Actions.JUMP_BACK:
this.jumpBack();
break;
case NavigationPagerPanResponder.Actions.JUMP_FORWARD:
this.jumpForward();
break;
default:
if (__DEV__) {
console.warn('unsupported gesture action', action);
}
}
}
}
// Legacy static members.
NavigationLegacyNavigator.BreadcrumbNavigationBar = NavigatorBreadcrumbNavigationBar;
NavigationLegacyNavigator.NavigationBar = NavigatorNavigationBar;
NavigationLegacyNavigator.SceneConfigs = NavigatorSceneConfigs;
module.exports = NavigationLegacyNavigator;
@@ -1,381 +0,0 @@
/**
* Copyright (c) 2013-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 NavigationLegacyNavigatorRouteStack
* @flow
*/
'use strict';
const invariant = require('fbjs/lib/invariant');
import type {
NavigationState,
NavigationParentState,
} from 'NavigationTypeDefinition';
type IterationCallback = (route: any, index: number, key: string) => void;
function isRouteEmpty(route: any): boolean {
return (route === undefined || route === null || route === '') || false;
}
function areRouteNodesEqual(
one: Array<RouteNode>,
two: Array<RouteNode>,
): boolean {
if (one === two) {
return true;
}
if (one.length !== two.length) {
return false;
}
for (let ii = 0, jj = one.length; ii < jj; ii++) {
if (one[ii] !== two[ii]) {
return false;
}
}
return true;
}
let _nextRouteNodeID = 0;
/**
* Private struct class that holds the key for a route.
*/
class RouteNode {
key: string;
route: any;
/**
* Cast `navigationState` as `RouteNode`.
* Also see `RouteNode#toNavigationState`.
*/
static fromNavigationState(navigationState: NavigationState): RouteNode {
invariant(
navigationState instanceof RouteNode,
'navigationState should be an instacne of RouteNode'
);
return navigationState;
}
constructor(route: any) {
// Key value gets bigger incrementally. Developer can compare the
// keys of two routes then know which route is added to the stack
// earlier.
const key = String(_nextRouteNodeID++);
if (__DEV__ ) {
// Ensure the immutability of the node.
Object.defineProperty(this, 'key' , {
enumerable: true,
configurable: false,
writable: false,
value: key,
});
Object.defineProperty(this, 'route' , {
enumerable: true,
configurable: false,
writable: false,
value: route,
});
} else {
this.key = key;
this.route = route;
}
}
toNavigationState(): NavigationState {
return this;
}
}
let _nextRouteStackID = 0;
/**
* The data structure that holds a list of routes and the focused index
* of the routes. This data structure is implemented as immutable data
* and mutation (e.g. push, pop...etc) will yields a new instance.
*/
class RouteStack {
_index: number;
_key: string;
_routeNodes: Array<RouteNode>;
static getRouteByNavigationState(navigationState: NavigationState): any {
return RouteNode.fromNavigationState(navigationState).route;
}
constructor(index: number, routes: Array<any>) {
invariant(
routes.length > 0,
'routes must not be an empty array'
);
invariant(
index > -1 && index <= routes.length - 1,
'RouteStack: index out of bound'
);
let routeNodes;
if (routes[0] instanceof RouteNode) {
// The array is already an array of <RouteNode>.
routeNodes = routes;
} else {
// Wrap the route with <RouteNode>.
routeNodes = routes.map((route) => {
invariant(!isRouteEmpty(route), 'route must not be mepty');
return new RouteNode(route);
});
}
this._routeNodes = routeNodes;
this._index = index;
this._key = String(_nextRouteStackID++);
}
/* $FlowFixMe - get/set properties not yet supported */
get size(): number {
return this._routeNodes.length;
}
/* $FlowFixMe - get/set properties not yet supported */
get index(): number {
return this._index;
}
// Export as...
toArray(): Array<any> {
return this._routeNodes.map(node => node.route);
}
toNavigationState(): NavigationParentState {
return {
index: this._index,
key: this._key,
children: this._routeNodes.map(node => node.toNavigationState()),
};
}
get(index: number): any {
if (index < 0 || index > this._routeNodes.length - 1) {
return null;
}
return this._routeNodes[index].route;
}
/**
* Returns the key associated with the route.
* When a route is added to a stack, the stack creates a key for this route.
* The key will persist until the initial stack and its derived stack
* no longer contains this route.
*/
keyOf(route: any): ?string {
if (isRouteEmpty(route)) {
return null;
}
const index = this.indexOf(route);
return index > -1 ?
this._routeNodes[index].key :
null;
}
indexOf(route: any): number {
if (isRouteEmpty(route)) {
return -1;
}
for (let ii = 0, jj = this._routeNodes.length; ii < jj; ii++) {
const node = this._routeNodes[ii];
if (node.route === route) {
return ii;
}
}
return -1;
}
slice(begin: ?number, end: ?number): RouteStack {
// check `begin` and `end` first to keep @flow happy.
const routeNodes = (end === undefined || end === null) ?
this._routeNodes.slice(begin || 0) :
this._routeNodes.slice(begin || 0, end || 0);
const index = Math.min(this._index, routeNodes.length - 1);
return this._update(index, routeNodes);
}
/**
* Returns a new stack with the provided route appended,
* starting at this stack size.
*/
push(route: any): RouteStack {
invariant(
!isRouteEmpty(route),
'Must supply route to push'
);
invariant(this.indexOf(route) === -1, 'route must be unique');
// When pushing, removes the rest of the routes past the current index.
const routeNodes = this._routeNodes.slice(0, this._index + 1);
routeNodes.push(new RouteNode(route));
return this._update(routeNodes.length - 1, routeNodes);
}
/**
* Returns a new stack a size ones less than this stack,
* excluding the last index in this stack.
*/
pop(): RouteStack {
if (this._routeNodes.length <= 1) {
return this;
}
// When popping, removes the rest of the routes past the current index.
const routeNodes = this._routeNodes.slice(0, this._index);
return this._update(routeNodes.length - 1, routeNodes);
}
popToRoute(route: any): RouteStack {
const index = this.indexOf(route);
invariant(
index > -1,
'Calling popToRoute for a route that doesn\'t exist!'
);
return this.slice(0, index + 1);
}
jumpTo(route: any): RouteStack {
const index = this.indexOf(route);
return this.jumpToIndex(index);
}
jumpToIndex(index: number): RouteStack {
invariant(
index > -1 && index < this._routeNodes.length,
'jumpToIndex: index out of bound'
);
return this._update(index, this._routeNodes);
}
jumpForward(): RouteStack {
const index = this._index + 1;
if (index >= this._routeNodes.length) {
return this;
}
return this._update(index, this._routeNodes);
}
jumpBack(): RouteStack {
const index = this._index - 1;
if (index < 0) {
return this;
}
return this._update(index, this._routeNodes);
}
/**
* Replace a route in the navigation stack.
*
* `index` specifies the route in the stack that should be replaced.
* If it's negative, it counts from the back.
*/
replaceAtIndex(index: number, route: any): RouteStack {
invariant(
!isRouteEmpty(route),
'Must supply route to replace'
);
if (this.get(index) === route) {
return this;
}
invariant(this.indexOf(route) === -1, 'route must be unique');
const size = this._routeNodes.length;
if (index < 0) {
index += size;
}
if (index < 0 || index >= size) {
return this;
}
const routeNodes = this._routeNodes.slice(0);
routeNodes[index] = new RouteNode(route);
return this._update(index, routeNodes);
}
replacePreviousAndPop(route: any): RouteStack {
if (this._index < 1) {
// stack is too small.
return this;
}
const index = this.indexOf(route);
invariant(
index === -1 || index === this._index - 1,
'route already exists in the stack'
);
return this.replaceAtIndex(this._index - 1, route).popToRoute(route);
}
// Reset
/**
* Replace the current active route with a new route, and pops out
* the rest routes after it.
*/
resetTo(route: any): RouteStack {
invariant(!isRouteEmpty(route), 'Must supply route');
const index = this.indexOf(route);
if (index === this._index) {
// Already has this active route.
return this;
}
invariant(index === -1, 'route already exists in the stack');
const routeNodes = this._routeNodes.slice(0, this._index);
routeNodes.push(new RouteNode(route));
return this._update(routeNodes.length - 1, routeNodes);
}
resetRoutes(routes: Array<any>): RouteStack {
const index = routes.length - 1;
return new RouteStack(index, routes);
}
// Iterations
forEach(callback: IterationCallback, context: ?Object): void {
this._routeNodes.forEach((node, index) => {
callback.call(context, node.route, index, node.key);
});
}
mapToArray(callback: IterationCallback, context: ?Object): Array<any> {
return this._routeNodes.map((node, index) => {
return callback.call(context, node.route, index, node.key);
});
}
_update(index: number, routeNodes: Array<RouteNode>): RouteStack {
if (
this._index === index &&
areRouteNodesEqual(this._routeNodes, routeNodes)
) {
return this;
}
return new RouteStack(index, routeNodes);
}
}
module.exports = RouteStack;
@@ -1,454 +0,0 @@
/**
* Copyright (c) 2013-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.
*
* Facebook, Inc. ("Facebook") owns all right, title and interest, including
* all intellectual property and other proprietary rights, in and to the React
* Native CustomComponents software (the "Software"). Subject to your
* compliance with these terms, you are hereby granted a non-exclusive,
* worldwide, royalty-free copyright license to (1) use and copy the Software;
* and (2) reproduce and distribute the Software as part of your own software
* ("Your Software"). Facebook reserves all rights not expressly granted to
* you in this license agreement.
*
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
jest.unmock('NavigationLegacyNavigatorRouteStack');
const NavigationLegacyNavigatorRouteStack = require('NavigationLegacyNavigatorRouteStack');
function assetStringNotEmpty(str) {
expect(!!str && typeof str === 'string').toBe(true);
}
describe('NavigationLegacyNavigatorRouteStack:', () => {
// Different types of routes.
const ROUTES = [
'foo',
1,
true,
{foo: 'bar'},
['foo'],
];
// Basic
it('gets index', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.index).toBe(1);
});
it('gets size', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.size).toBe(3);
});
it('gets route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b', 'c']);
expect(stack.get(2)).toBe('c');
});
it('converts to an array', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
expect(stack.toArray()).toEqual(['a', 'b']);
});
it('creates a new stack after mutation', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const stack2 = stack1.push('c');
expect(stack1).not.toBe(stack2);
});
it('throws at index out of bound', () => {
expect(
() => new NavigationLegacyNavigatorRouteStack(-1, ['a', 'b'])
).toThrow();
expect(
() => new NavigationLegacyNavigatorRouteStack(100, ['a', 'b'])
).toThrow();
});
it('finds index', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
expect(stack.indexOf('b')).toBe(1);
expect(stack.indexOf('c')).toBe(-1);
});
// Key
it('gets key for route', () => {
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
const key = stack.push(route).keyOf(route);
expect(typeof key).toBe('string');
expect(!!key).toBe(true);
};
ROUTES.forEach(test);
});
it('gets a key of larger value for route', () => {
let lastKey = '';
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
const key = stack.push(route).keyOf(route);
expect(key > lastKey).toBe(true);
lastKey = key;
};
ROUTES.forEach(test);
});
it('gets an unique key for a different route', () => {
let stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
const keys = {};
const test = (route) => {
stack = stack.push(route);
const key = stack.keyOf(route);
expect(keys[key]).toBe(undefined);
keys[key] = true;
};
ROUTES.forEach(test);
});
it('gets the same unique key for the same route', () => {
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, [route]);
expect(stack.keyOf(route)).toBe(stack.keyOf(route));
};
ROUTES.forEach(test);
});
it('gets the same unique key form the derived stack', () => {
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, [route]);
const derivedStack = stack.push('wow').pop().slice(0, 10).push('blah');
expect(derivedStack.keyOf(route)).toBe(stack.keyOf(route));
};
ROUTES.forEach(test);
});
it('gets a different key from a different stack', () => {
const test = (route) => {
const stack1 = new NavigationLegacyNavigatorRouteStack(0, [route]);
const stack2 = new NavigationLegacyNavigatorRouteStack(0, [route]);
expect(stack1.keyOf(route)).not.toBe(stack2.keyOf(route));
};
ROUTES.forEach(test);
});
it('gets no key for a route that does not contains this route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
expect(stack.keyOf('b')).toBe(null);
});
it('gets a new key for a route that was removed and added again', () => {
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
const key1 = stack.push(route).keyOf(route);
const key2 = stack.push(route).pop().push(route).keyOf(route);
expect(key1).not.toBe(key2);
};
ROUTES.forEach(test);
});
// Slice
it('slices', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c', 'd']);
const stack2 = stack1.slice(1, 3);
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['b', 'c']);
});
it('may update index after slicing', () => {
const stack = new NavigationLegacyNavigatorRouteStack(2, ['a', 'b', 'c']);
expect(stack.slice().index).toBe(2);
expect(stack.slice(0, 1).index).toBe(0);
expect(stack.slice(0, 2).index).toBe(1);
expect(stack.slice(0, 3).index).toBe(2);
expect(stack.slice(0, 100).index).toBe(2);
expect(stack.slice(-2).index).toBe(1);
});
it('slices without specifying params', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
const stack2 = stack1.slice();
expect(stack2).toBe(stack1);
});
it('slices to from the end', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c', 'd']);
const stack2 = stack1.slice(-2);
expect(stack2.toArray()).toEqual(['c', 'd']);
});
it('throws when slicing to empty', () => {
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.slice(100);
}).toThrow();
});
// Push
it('pushes route', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
const stack2 = stack1.push('c');
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a', 'b', 'c']);
expect(stack2.index).toBe(2);
expect(stack2.size).toBe(3);
});
it('throws when pushing empty route', () => {
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.push(null);
}).toThrow();
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.push('');
}).toThrow();
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.push(undefined);
}).toThrow();
});
it('replaces routes on push', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
const stack2 = stack1.push('d');
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a', 'b', 'd']);
expect(stack2.index).toBe(2);
});
// Pop
it('pops route', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(2, ['a', 'b', 'c']);
const stack2 = stack1.pop();
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a', 'b']);
expect(stack2.index).toBe(1);
expect(stack2.size).toBe(2);
});
it('replaces routes on pop', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
const stack2 = stack1.pop();
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a']);
expect(stack2.index).toBe(0);
});
it('does nothing while popping to empty', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
expect(stack.pop()).toBe(stack);
expect(stack.pop().pop()).toBe(stack);
});
it('pops to route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.popToRoute('b').toArray()).toEqual(['a', 'b']);
expect(stack.popToRoute('b').index).toBe(1);
expect(stack.popToRoute('a').toArray()).toEqual(['a']);
expect(stack.popToRoute('a').index).toBe(0);
expect(() => {stack.popToRoute('x');}).toThrow();
});
// Jump
it('jumps to index', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b', 'c']);
const stack2 = stack1.jumpToIndex(2);
expect(stack2).not.toBe(stack1);
expect(stack2.index).toBe(2);
});
it('throws then jumping to index out of bound', () => {
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.jumpToIndex(2);
}).toThrow();
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.jumpToIndex(-1);
}).toThrow();
});
it('jumps to route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
expect(stack.jumpTo('b').index).toBe(1);
});
it('jumps backward', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
expect(stack.jumpBack().index).toBe(0);
expect(stack.jumpBack().jumpBack().jumpBack().index).toBe(0);
});
it('jumps forward', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
expect(stack.jumpForward().index).toBe(1);
expect(stack.jumpForward().jumpForward().jumpForward().index).toBe(1);
});
// Replace
it('replaces route at index', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
const stack2 = stack1.replaceAtIndex(0, 'x');
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['x', 'b']);
expect(stack2.index).toBe(0);
});
it('replaces route at negative index', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
const stack2 = stack1.replaceAtIndex(-1, 'x');
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a', 'x']);
expect(stack2.index).toBe(1);
});
it('throws when replacing empty route', () => {
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.replaceAtIndex(1, null);
}).toThrow();
});
it('does nothing when replacing at index out of bound', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
expect(stack.replaceAtIndex(100, 'x')).toBe(stack);
expect(stack.replaceAtIndex(-100, 'x')).toBe(stack);
});
it('replaces previous and pop route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.replacePreviousAndPop('x').toArray()).toEqual(['x']);
expect(stack.replacePreviousAndPop('x').index).toBe(0);
});
it('does nothing when there is nothing to replace', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b', 'c']);
expect(stack.replacePreviousAndPop('x')).toBe(stack);
});
// Reset
it('resets route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.resetTo('b')).toBe(stack);
expect(stack.resetTo('x').toArray()).toEqual(['a', 'x']);
expect(stack.resetTo('x').index).toBe(1);
expect(() => {stack.resetTo(null);}).toThrow();
expect(() => {stack.resetTo('a');}).toThrow();
});
it('resets routes', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
expect(stack.resetRoutes(['x', 'y']).toArray()).toEqual(['x', 'y']);
expect(stack.resetRoutes(['x', 'y']).index).toBe(1);
});
// Iteration
it('iterates each item', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const logs = [];
const keys = {};
const context = {name: 'yo'};
stack.forEach(function (route, index, key) {
assetStringNotEmpty(key);
if (!keys.hasOwnProperty(key)) {
keys[key] = true;
logs.push([
route,
index,
this.name,
]);
}
}, context);
expect(logs).toEqual([
['a', 0, 'yo'],
['b', 1, 'yo'],
]);
});
it('Maps to an array', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const keys = {};
const context = {name: 'yo'};
const logs = stack.mapToArray(function(route, index, key) {
assetStringNotEmpty(key);
if (!keys.hasOwnProperty(key)) {
keys[key] = true;
return [
route,
index,
this.name,
];
}
}, context);
expect(logs).toEqual([
['a', 0, 'yo'],
['b', 1, 'yo'],
]);
});
// Navigation State
it('coverts to navigation state', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const state = stack.toNavigationState();
expect(state).toEqual({
index: 0,
key: '0',
children:[
{key: '0', route: 'a'},
{key: '1', route: 'b'},
],
});
});
it('coverts from navigation state', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const state = stack.toNavigationState().children[0];
const route = NavigationLegacyNavigatorRouteStack.getRouteByNavigationState(state);
expect(route).toBe('a');
});
});
@@ -117,7 +117,7 @@ class RouteStack {
return this._routeNodes.findIndex(finder, this);
}
slice(begin: ?number, end: ?number): RouteStack {
slice(begin?: number, end?: number): RouteStack {
var routeNodes = this._routeNodes.slice(begin, end);
var index = Math.min(this._index, routeNodes.size - 1);
return this._update(index, routeNodes);
@@ -1,5 +1,10 @@
/**
* Copyright (c) 2015, Facebook, Inc. All rights reserved.
* Copyright (c) 2013-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.
*
* Facebook, Inc. ("Facebook") owns all right, title and interest, including
* all intellectual property and other proprietary rights, in and to the React
@@ -446,7 +451,7 @@ var Navigator = React.createClass({
this._onAnimationEnd();
var presentedIndex = this.state.presentedIndex;
var didFocusRoute = this._subRouteFocus[presentedIndex] || this.state.routeStack[presentedIndex];
this._emitDidFocus(didFocusRoute);
if (AnimationsDebugModule) {
AnimationsDebugModule.stopRecordingFps(Date.now());
}
@@ -457,6 +462,9 @@ var Navigator = React.createClass({
this.state.transitionCb();
this.state.transitionCb = null;
}
this._emitDidFocus(didFocusRoute);
if (this._interactionHandle) {
this.clearInteractionHandle(this._interactionHandle);
this._interactionHandle = null;
@@ -917,16 +925,15 @@ var Navigator = React.createClass({
var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
var nextStack = activeStack.concat([route]);
var destIndex = nextStack.length - 1;
var nextAnimationConfigStack = activeAnimationConfigStack.concat([
this.props.configureScene(route, nextStack),
]);
var nextSceneConfig = this.props.configureScene(route, nextStack);
var nextAnimationConfigStack = activeAnimationConfigStack.concat([nextSceneConfig]);
this._emitWillFocus(nextStack[destIndex]);
this.setState({
routeStack: nextStack,
sceneConfigStack: nextAnimationConfigStack,
}, () => {
this._enableScene(destIndex);
this._transitionTo(destIndex);
this._transitionTo(destIndex, nextSceneConfig.defaultTransitionVelocity);
});
},
@@ -939,11 +946,13 @@ var Navigator = React.createClass({
'Cannot pop below zero'
);
var popIndex = this.state.presentedIndex - n;
var presentedRoute = this.state.routeStack[this.state.presentedIndex];
var popSceneConfig = this.props.configureScene(presentedRoute); // using the scene config of the currently presented view
this._enableScene(popIndex);
this._emitWillFocus(this.state.routeStack[popIndex]);
this._transitionTo(
popIndex,
null, // default velocity
popSceneConfig.defaultTransitionVelocity,
null, // no spring jumping
() => {
this._cleanScenesPastIndex(popIndex);
@@ -0,0 +1,62 @@
/**
* 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 EmitterSubscription
* @noflow
* @typechecks
*/
'use strict';
const EventSubscription = require('EventSubscription');
import type EventEmitter from 'EventEmitter';
import type EventSubscriptionVendor from 'EventSubscriptionVendor';
/**
* EmitterSubscription represents a subscription with listener and context data.
*/
class EmitterSubscription extends EventSubscription {
emitter: EventEmitter;
listener: Function;
context: ?Object;
/**
* @param {EventEmitter} emitter - The event emitter that registered this
* subscription
* @param {EventSubscriptionVendor} subscriber - The subscriber that controls
* this subscription
* @param {function} listener - Function to invoke when the specified event is
* emitted
* @param {*} context - Optional context object to use when invoking the
* listener
*/
constructor(
emitter: EventEmitter,
subscriber: EventSubscriptionVendor,
listener: Function,
context: ?Object
) {
super(subscriber);
this.emitter = emitter;
this.listener = listener;
this.context = context;
}
/**
* Removes this subscription from the emitter that registered it.
* Note: we're overriding the `remove()` method of EventSubscription here
* but deliberately not calling `super.remove()` as the responsibility
* for removing the subscription lies with the EventEmitter.
*/
remove() {
this.emitter.removeSubscription(this);
}
}
module.exports = EmitterSubscription;
@@ -8,14 +8,13 @@
*
* @providesModule EventEmitter
* @noflow
* @typechecks
* @typecheck
*/
var EmitterSubscription = require('EmitterSubscription');
var ErrorUtils = require('ErrorUtils');
var EventSubscriptionVendor = require('EventSubscriptionVendor');
var emptyFunction = require('fbjs/lib/emptyFunction');
var invariant = require('fbjs/lib/invariant');
const EmitterSubscription = require('EmitterSubscription');
const EventSubscriptionVendor = require('EventSubscriptionVendor');
const emptyFunction = require('fbjs/lib/emptyFunction');
const invariant = require('fbjs/lib/invariant');
/**
* @class EventEmitter
@@ -31,11 +30,18 @@ var invariant = require('fbjs/lib/invariant');
* more advanced emitter may use an EventHolder and EventFactory.
*/
class EventEmitter {
_subscriber: EventSubscriptionVendor;
_currentSubscription: ?EmitterSubscription;
/**
* @constructor
*
* @param {EventSubscriptionVendor} subscriber - Optional subscriber instance
* to use. If omitted, a new subscriber will be created for the emitter.
*/
constructor() {
this._subscriber = new EventSubscriptionVendor();
constructor(subscriber: ?EventSubscriptionVendor) {
this._subscriber = subscriber || new EventSubscriptionVendor();
}
/**
@@ -53,10 +59,12 @@ class EventEmitter {
* listener
*/
addListener(
eventType: String, listener, context: ?Object): EmitterSubscription {
return this._subscriber.addSubscription(
eventType: string, listener: Function, context: ?Object): EmitterSubscription {
return (this._subscriber.addSubscription(
eventType,
new EmitterSubscription(this._subscriber, listener, context));
new EmitterSubscription(this, this._subscriber, listener, context)
) : any);
}
/**
@@ -69,10 +77,9 @@ class EventEmitter {
* @param {*} context - Optional context object to use when invoking the
* listener
*/
once(eventType: String, listener, context: ?Object): EmitterSubscription {
var emitter = this;
return this.addListener(eventType, function() {
emitter.removeCurrentListener();
once(eventType: string, listener: Function, context: ?Object): EmitterSubscription {
return this.addListener(eventType, () => {
this.removeCurrentListener();
listener.apply(context, arguments);
});
}
@@ -84,7 +91,7 @@ class EventEmitter {
* @param {?string} eventType - Optional name of the event whose registered
* listeners to remove
*/
removeAllListeners(eventType: ?String) {
removeAllListeners(eventType: ?string) {
this._subscriber.removeAllSubscriptions(eventType);
}
@@ -114,7 +121,19 @@ class EventEmitter {
!!this._currentSubscription,
'Not in an emitting cycle; there is no current subscription'
);
this._subscriber.removeSubscription(this._currentSubscription);
this.removeSubscription(this._currentSubscription);
}
/**
* Removes a specific subscription. Called by the `remove()` method of the
* subscription itself to ensure any necessary cleanup is performed.
*/
removeSubscription(subscription: EmitterSubscription) {
invariant(
subscription.emitter === this,
'Subscription does not belong to this emitter.'
);
this._subscriber.removeSubscription(subscription);
}
/**
@@ -124,8 +143,8 @@ class EventEmitter {
* @param {string} eventType - Name of the event to query
* @returns {array}
*/
listeners(eventType: String): Array /* TODO: Array<EventSubscription> */ {
var subscriptions = this._subscriber.getSubscriptionsForType(eventType);
listeners(eventType: string): [EmitterSubscription] {
const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
return subscriptions
? subscriptions.filter(emptyFunction.thatReturnsTrue).map(
function(subscription) {
@@ -148,13 +167,11 @@ class EventEmitter {
*
* emitter.emit('someEvent', 'abc'); // logs 'abc'
*/
emit(eventType: String) {
var subscriptions = this._subscriber.getSubscriptionsForType(eventType);
emit(eventType: string) {
const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
if (subscriptions) {
var keys = Object.keys(subscriptions);
for (var ii = 0; ii < keys.length; ii++) {
var key = keys[ii];
var subscription = subscriptions[key];
for (let i = 0, l = subscriptions.length; i < l; i++) {
const subscription = subscriptions[i];
// The subscription may have been removed during this event loop.
if (subscription) {
@@ -1,22 +1,20 @@
/**
* @generated SignedSource<<fb2bb5c1c402a097a7e1da7413526629>>
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in of a static_upstream project! !!
* !! !!
* !! You should not modify this file directly. Instead: !!
* !! 1) Use `fjs use-upstream` to temporarily replace this with !!
* !! the latest version from upstream. !!
* !! 2) Make your changes, test them, etc. !!
* !! 3) Use `fjs push-upstream` to copy your changes back to !!
* !! static_upstream. !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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 EventEmitterWithHolding
* @typechecks
* @flow
*/
'use strict';
import type EmitterSubscription from 'EmitterSubscription';
import type EventEmitter from 'EventEmitter';
import type EventHolder from 'EventHolder';
/**
* @class EventEmitterWithHolding
* @description
@@ -30,6 +28,12 @@
* that uses an emitter.
*/
class EventEmitterWithHolding {
_emitter: EventEmitter;
_eventHolder: EventHolder;
_currentEventToken: ?Object;
_emittingHeldEvents: boolean;
/**
* @constructor
* @param {object} emitter - The object responsible for emitting the actual
@@ -37,7 +41,7 @@ class EventEmitterWithHolding {
* @param {object} holder - The event holder that is responsible for holding
* and then emitting held events.
*/
constructor(emitter, holder) {
constructor(emitter: EventEmitter, holder: EventHolder) {
this._emitter = emitter;
this._eventHolder = holder;
this._currentEventToken = null;
@@ -47,14 +51,14 @@ class EventEmitterWithHolding {
/**
* @see EventEmitter#addListener
*/
addListener(eventType: String, listener, context: ?Object) {
addListener(eventType: string, listener: Function, context: ?Object) {
return this._emitter.addListener(eventType, listener, context);
}
/**
* @see EventEmitter#once
*/
once(eventType: String, listener, context: ?Object) {
once(eventType: string, listener: Function, context: ?Object) {
return this._emitter.once(eventType, listener, context);
}
@@ -79,8 +83,8 @@ class EventEmitterWithHolding {
* }); // logs 'abc'
*/
addRetroactiveListener(
eventType: String, listener, context: ?Object): EmitterSubscription {
var subscription = this._emitter.addListener(eventType, listener, context);
eventType: string, listener: Function, context: ?Object): EmitterSubscription {
const subscription = this._emitter.addListener(eventType, listener, context);
this._emittingHeldEvents = true;
this._eventHolder.emitToListener(eventType, listener, context);
@@ -92,7 +96,7 @@ class EventEmitterWithHolding {
/**
* @see EventEmitter#removeAllListeners
*/
removeAllListeners(eventType: String) {
removeAllListeners(eventType: string) {
this._emitter.removeAllListeners(eventType);
}
@@ -106,15 +110,15 @@ class EventEmitterWithHolding {
/**
* @see EventEmitter#listeners
*/
listeners(eventType: String) /* TODO: Annotate return type here */ {
listeners(eventType: string) /* TODO: Annotate return type here */ {
return this._emitter.listeners(eventType);
}
/**
* @see EventEmitter#emit
*/
emit(eventType: String, a, b, c, d, e, _) {
this._emitter.emit(eventType, a, b, c, d, e, _);
emit(eventType: string, ...args: any) {
this._emitter.emit(eventType, ...args);
}
/**
@@ -132,12 +136,9 @@ class EventEmitterWithHolding {
* console.log(message);
* }); // logs 'abc'
*/
emitAndHold(eventType: String, a, b, c, d, e, _) {
this._currentEventToken = this._eventHolder.holdEvent(
eventType,
a, b, c, d, e, _
);
this._emitter.emit(eventType, a, b, c, d, e, _);
emitAndHold(eventType: string, ...args: any) {
this._currentEventToken = this._eventHolder.holdEvent(eventType, ...args);
this._emitter.emit(eventType, ...args);
this._currentEventToken = null;
}
@@ -145,7 +146,7 @@ class EventEmitterWithHolding {
* @see EventHolder#releaseCurrentEvent
*/
releaseCurrentEvent() {
if (this._currentEventToken !== null) {
if (this._currentEventToken) {
this._eventHolder.releaseEvent(this._currentEventToken);
} else if (this._emittingHeldEvents) {
this._eventHolder.releaseCurrentEvent();
@@ -156,7 +157,7 @@ class EventEmitterWithHolding {
* @see EventHolder#releaseEventType
* @param {string} eventType
*/
releaseHeldEventType(eventType: String) {
releaseHeldEventType(eventType: string) {
this._eventHolder.releaseEventType(eventType);
}
}
@@ -1,25 +1,23 @@
/**
* @generated SignedSource<<0591836c443c735d24e61782320d3d16>>
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in of a static_upstream project! !!
* !! !!
* !! You should not modify this file directly. Instead: !!
* !! 1) Use `fjs use-upstream` to temporarily replace this with !!
* !! the latest version from upstream. !!
* !! 2) Make your changes, test them, etc. !!
* !! 3) Use `fjs push-upstream` to copy your changes back to !!
* !! static_upstream. !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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 EventHolder
* @typechecks
* @flow
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
const invariant = require('fbjs/lib/invariant');
class EventHolder {
_heldEvents: Object;
_currentEventKey: ?Object;
constructor() {
this._heldEvents = {};
this._currentEventKey = null;
@@ -46,14 +44,14 @@ class EventHolder {
* }); //logs 'abc'
*
*/
holdEvent(eventType: String, a, b, c, d, e, _) {
holdEvent(eventType: string, ...args: any) {
this._heldEvents[eventType] = this._heldEvents[eventType] || [];
var eventsOfType = this._heldEvents[eventType];
var key = {
const eventsOfType = this._heldEvents[eventType];
const key = {
eventType: eventType,
index: eventsOfType.length
};
eventsOfType.push([a, b, c, d, e, _]);
eventsOfType.push(args);
return key;
}
@@ -65,12 +63,12 @@ class EventHolder {
* @param {?object} context - Optional context object to use when invoking
* the listener
*/
emitToListener(eventType: ?String , listener, context: ?Object) {
var eventsOfType = this._heldEvents[eventType];
emitToListener(eventType: ?string , listener: Function, context: ?Object) {
const eventsOfType = this._heldEvents[eventType];
if (!eventsOfType) {
return;
}
var origEventKey = this._currentEventKey;
const origEventKey = this._currentEventKey;
eventsOfType.forEach((/*?array*/ eventHeld, /*number*/ index) => {
if (!eventHeld) {
return;
@@ -97,7 +95,7 @@ class EventHolder {
this._currentEventKey !== null,
'Not in an emitting cycle; there is no current event'
);
this.releaseEvent(this._currentEventKey);
this._currentEventKey && this.releaseEvent(this._currentEventKey);
}
/**
@@ -115,7 +113,7 @@ class EventHolder {
*
* @param {string} type
*/
releaseEventType(type: String) {
releaseEventType(type: string) {
this._heldEvents[type] = [];
}
}
@@ -0,0 +1,42 @@
/**
* 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 EventSubscription
* @flow
*/
'use strict';
import type EventSubscriptionVendor from 'EventSubscriptionVendor';
/**
* EventSubscription represents a subscription to a particular event. It can
* remove its own subscription.
*/
class EventSubscription {
eventType: string;
key: number;
subscriber: EventSubscriptionVendor;
/**
* @param {EventSubscriptionVendor} subscriber the subscriber that controls
* this subscription.
*/
constructor(subscriber: EventSubscriptionVendor) {
this.subscriber = subscriber;
}
/**
* Removes this subscription from the subscriber that controls it.
*/
remove() {
this.subscriber.removeSubscription(this);
}
}
module.exports = EventSubscription;
@@ -1,23 +1,19 @@
/**
* @generated SignedSource<<24d5cc1cdd24704296686faf89dd36cf>>
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in of a static_upstream project! !!
* !! !!
* !! You should not modify this file directly. Instead: !!
* !! 1) Use `fjs use-upstream` to temporarily replace this with !!
* !! the latest version from upstream. !!
* !! 2) Make your changes, test them, etc. !!
* !! 3) Use `fjs push-upstream` to copy your changes back to !!
* !! static_upstream. !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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 EventSubscriptionVendor
* @typechecks
* @flow
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
const invariant = require('fbjs/lib/invariant');
import type EventSubscription from 'EventSubscription';
/**
* EventSubscriptionVendor stores a set of EventSubscriptions that are
@@ -25,6 +21,9 @@ var invariant = require('fbjs/lib/invariant');
*/
class EventSubscriptionVendor {
_subscriptionsForType: Object;
_currentSubscription: ?EventSubscription;
constructor() {
this._subscriptionsForType = {};
this._currentSubscription = null;
@@ -37,14 +36,14 @@ class EventSubscriptionVendor {
* @param {EventSubscription} subscription
*/
addSubscription(
eventType: String, subscription: EventSubscription): EventSubscription {
eventType: string, subscription: EventSubscription): EventSubscription {
invariant(
subscription.subscriber === this,
'The subscriber of the subscription is incorrectly set.');
if (!this._subscriptionsForType[eventType]) {
this._subscriptionsForType[eventType] = [];
}
var key = this._subscriptionsForType[eventType].length;
const key = this._subscriptionsForType[eventType].length;
this._subscriptionsForType[eventType].push(subscription);
subscription.eventType = eventType;
subscription.key = key;
@@ -57,7 +56,7 @@ class EventSubscriptionVendor {
* @param {?string} eventType - Optional name of the event type whose
* registered supscriptions to remove, if null remove all subscriptions.
*/
removeAllSubscriptions(eventType: ?String) {
removeAllSubscriptions(eventType: ?string) {
if (eventType === undefined) {
this._subscriptionsForType = {};
} else {
@@ -72,10 +71,10 @@ class EventSubscriptionVendor {
* @param {object} subscription
*/
removeSubscription(subscription: Object) {
var eventType = subscription.eventType;
var key = subscription.key;
const eventType = subscription.eventType;
const key = subscription.key;
var subscriptionsForType = this._subscriptionsForType[eventType];
const subscriptionsForType = this._subscriptionsForType[eventType];
if (subscriptionsForType) {
delete subscriptionsForType[key];
}
@@ -93,7 +92,7 @@ class EventSubscriptionVendor {
* @param {string} eventType
* @returns {?array}
*/
getSubscriptionsForType(eventType: String): ?Array {
getSubscriptionsForType(eventType: string): ?[EventSubscription] {
return this._subscriptionsForType[eventType];
}
}
@@ -1,22 +1,17 @@
/**
* @generated SignedSource<<7149bdac6fb48595f245ad6e76938e44>>
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in of a static_upstream project! !!
* !! !!
* !! You should not modify this file directly. Instead: !!
* !! 1) Use `fjs use-upstream` to temporarily replace this with !!
* !! the latest version from upstream. !!
* !! 2) Make your changes, test them, etc. !!
* !! 3) Use `fjs push-upstream` to copy your changes back to !!
* !! static_upstream. !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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 EventValidator
* @flow
*/
'use strict';
var copyProperties = require('copyProperties');
const copyProperties = require('copyProperties');
/**
* EventValidator is designed to validate event types to make it easier to catch
@@ -27,7 +22,7 @@ var copyProperties = require('copyProperties');
* mistyped the event name it will suggest what you might have meant to type in
* the error message.
*/
var EventValidator = {
const EventValidator = {
/**
* @param {Object} emitter - The object responsible for emitting the actual
* events
@@ -35,12 +30,12 @@ var EventValidator = {
* check for errors
* @return {Object} A new emitter with event type validation
* @example
* var types = {someEvent: true, anotherEvent: true};
* var emitter = EventValidator.addValidation(emitter, types);
* const types = {someEvent: true, anotherEvent: true};
* const emitter = EventValidator.addValidation(emitter, types);
*/
addValidation: function(emitter: Object, types: Object) {
var eventTypes = Object.keys(types);
var emitterWithValidation = Object.create(emitter);
const eventTypes = Object.keys(types);
const emitterWithValidation = Object.create(emitter);
copyProperties(emitterWithValidation, {
emit: function emit(type, a, b, c, d, e, _) {
@@ -60,7 +55,7 @@ function assertAllowsEventType(type, allowedTypes) {
}
function errorMessageFor(type, allowedTypes) {
var message = 'Unknown event type "' + type + '". ';
let message = 'Unknown event type "' + type + '". ';
if (__DEV__) {
message += recommendationFor(type, allowedTypes);
}
@@ -71,7 +66,7 @@ function errorMessageFor(type, allowedTypes) {
// Allow for good error messages
if (__DEV__) {
var recommendationFor = function (type, allowedTypes) {
var closestTypeRecommendation = closestTypeFor(type, allowedTypes);
const closestTypeRecommendation = closestTypeFor(type, allowedTypes);
if (isCloseEnough(closestTypeRecommendation, type)) {
return 'Did you mean "' + closestTypeRecommendation.type + '"? ';
} else {
@@ -80,7 +75,7 @@ if (__DEV__) {
};
var closestTypeFor = function (type, allowedTypes) {
var typeRecommendations = allowedTypes.map(
const typeRecommendations = allowedTypes.map(
typeRecommendationFor.bind(this, type)
);
return typeRecommendations.sort(recommendationSort)[0];
@@ -108,8 +103,8 @@ if (__DEV__) {
};
var damerauLevenshteinDistance = function (a, b) {
var i, j;
var d = [];
let i, j;
const d = [];
for (i = 0; i <= a.length; i++) {
d[i] = [i];
@@ -121,7 +116,7 @@ if (__DEV__) {
for (i = 1; i <= a.length; i++) {
for (j = 1; j <= b.length; j++) {
var cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
const cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
d[i][j] = Math.min(
d[i - 1][j] + 1,
@@ -130,8 +125,8 @@ if (__DEV__) {
);
if (i > 1 && j > 1 &&
a.charAt(i - 1) == b.charAt(j - 2) &&
a.charAt(i - 2) == b.charAt(j - 1)) {
a.charAt(i - 1) === b.charAt(j - 2) &&
a.charAt(i - 2) === b.charAt(j - 1)) {
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
}
}
@@ -0,0 +1,61 @@
/**
* 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 NativeEventEmitter
* @flow
*/
'use strict';
const EventEmitter = require('EventEmitter');
const Platform = require('Platform');
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
const invariant = require('fbjs/lib/invariant');
import type EmitterSubscription from 'EmitterSubscription';
/**
* Abstract base class for implementing event-emitting modules. This implements
* a subset of the standard EventEmitter node module API.
*/
class NativeEventEmitter extends EventEmitter {
_nativeModule: Object;
constructor(nativeModule: Object) {
super(RCTDeviceEventEmitter.sharedSubscriber);
if (Platform.OS === 'ios') {
invariant(nativeModule, 'Native module cannot be null.');
this._nativeModule = nativeModule;
}
}
addListener(eventType: string, listener: Function, context: ?Object): EmitterSubscription {
if (Platform.OS === 'ios') {
this._nativeModule.addListener(eventType);
}
return super.addListener(eventType, listener, context);
}
removeAllListeners(eventType: string) {
invariant(eventType, 'eventType argument is required.');
if (Platform.OS === 'ios') {
const count = this.listeners(eventType).length;
this._nativeModule.removeListeners(count);
}
super.removeAllListeners(eventType);
}
removeSubscription(subscription: EmitterSubscription) {
if (Platform.OS === 'ios') {
this._nativeModule.removeListeners(1);
}
super.removeSubscription(subscription);
}
}
module.exports = NativeEventEmitter;
@@ -0,0 +1,76 @@
/**
* 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 RCTDeviceEventEmitter
* @flow
*/
'use strict';
const EventEmitter = require('EventEmitter');
const EventSubscriptionVendor = require('EventSubscriptionVendor');
const BatchedBridge = require('BatchedBridge');
import type EmitterSubscription from 'EmitterSubscription';
/**
* Deprecated - subclass NativeEventEmitter to create granular event modules instead of
* adding all event listeners directly to RCTDeviceEventEmitter.
*/
class RCTDeviceEventEmitter extends EventEmitter {
sharedSubscriber: EventSubscriptionVendor;
constructor() {
const sharedSubscriber = new EventSubscriptionVendor();
super(sharedSubscriber);
this.sharedSubscriber = sharedSubscriber;
}
addListener(eventType: string, listener: Function, context: ?Object): EmitterSubscription {
if (eventType.lastIndexOf('statusBar', 0) === 0) {
console.warn('`%s` event should be registered via the StatusBarIOS module', eventType);
return require('StatusBarIOS').addListener(eventType, listener, context);
}
if (eventType.lastIndexOf('keyboard', 0) === 0) {
console.warn('`%s` event should be registered via the Keyboard module', eventType);
return require('Keyboard').addListener(eventType, listener, context);
}
return super.addListener(eventType, listener, context);
}
removeAllListeners(eventType: ?string) {
if (eventType) {
if (eventType.lastIndexOf('statusBar', 0) === 0) {
console.warn('statusBar events should be unregistered via the StatusBarIOS module');
return require('StatusBarIOS').removeAllListeners(eventType);
}
if (eventType.lastIndexOf('keyboard', 0) === 0) {
console.warn('keyboard events should be unregistered via the Keyboard module');
return require('Keyboard').removeAllListeners(eventType);
}
}
super.removeAllListeners(eventType);
}
removeSubscription(subscription: EmitterSubscription) {
if (subscription.emitter !== this) {
subscription.emitter.removeSubscription(subscription);
} else {
super.removeSubscription(subscription);
}
}
}
RCTDeviceEventEmitter = new RCTDeviceEventEmitter();
BatchedBridge.registerCallableModule(
'RCTDeviceEventEmitter',
RCTDeviceEventEmitter
);
module.exports = RCTDeviceEventEmitter;
@@ -11,10 +11,14 @@
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var EventEmitter = require('EventEmitter');
const BatchedBridge = require('BatchedBridge');
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var RCTNativeAppEventEmitter = new EventEmitter();
/**
* Deprecated - subclass NativeEventEmitter to create granular event modules instead of
* adding all event listeners directly to RCTNativeAppEventEmitter.
*/
const RCTNativeAppEventEmitter = RCTDeviceEventEmitter;
BatchedBridge.registerCallableModule(
'RCTNativeAppEventEmitter',
@@ -1,30 +1,25 @@
/**
* @generated SignedSource<<c735038726af2daf584b3e7fb3950e8b>>
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in of a static_upstream project! !!
* !! !!
* !! You should not modify this file directly. Instead: !!
* !! 1) Use `fjs use-upstream` to temporarily replace this with !!
* !! the latest version from upstream. !!
* !! 2) Make your changes, test them, etc. !!
* !! 3) Use `fjs push-upstream` to copy your changes back to !!
* !! static_upstream. !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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 mixInEventEmitter
* @flow
*/
var EventEmitter = require('EventEmitter');
var EventEmitterWithHolding = require('EventEmitterWithHolding');
var EventHolder = require('EventHolder');
var EventValidator = require('EventValidator');
const EventEmitter = require('EventEmitter');
const EventEmitterWithHolding = require('EventEmitterWithHolding');
const EventHolder = require('EventHolder');
const EventValidator = require('EventValidator');
var copyProperties = require('copyProperties');
var invariant = require('fbjs/lib/invariant');
var keyOf = require('fbjs/lib/keyOf');
const copyProperties = require('copyProperties');
const invariant = require('fbjs/lib/invariant');
const keyOf = require('fbjs/lib/keyOf');
var TYPES_KEY = keyOf({__types: true});
const TYPES_KEY = keyOf({__types: true});
/**
* API to setup an object or constructor to be able to emit data events.
@@ -47,16 +42,16 @@ var TYPES_KEY = keyOf({__types: true});
* mixInEventEmitter(Singleton, {lonely: true});
* Singleton.emit('lonely', true);
*/
function mixInEventEmitter(klass, types) {
function mixInEventEmitter(cls: Function | Object, types: Object) {
invariant(types, 'Must supply set of valid event types');
// If this is a constructor, write to the prototype, otherwise write to the
// singleton object.
var target = klass.prototype || klass;
const target = cls.prototype || cls;
invariant(!target.__eventEmitter, 'An active emitter is already mixed in');
var ctor = klass.constructor;
const ctor = cls.constructor;
if (ctor) {
invariant(
ctor === Object || ctor === Function,
@@ -76,7 +71,7 @@ function mixInEventEmitter(klass, types) {
copyProperties(target, EventEmitterMixin);
}
var EventEmitterMixin = {
const EventEmitterMixin = {
emit: function(eventType, a, b, c, d, e, _) {
return this.__getEventEmitter().emit(eventType, a, b, c, d, e, _);
},
@@ -123,10 +118,10 @@ var EventEmitterMixin = {
__getEventEmitter: function() {
if (!this.__eventEmitter) {
var emitter = new EventEmitter();
let emitter = new EventEmitter();
emitter = EventValidator.addValidation(emitter, this.__types);
var holder = new EventHolder();
const holder = new EventHolder();
this.__eventEmitter = new EventEmitterWithHolding(emitter, holder);
}
return this.__eventEmitter;
+8 -6
View File
@@ -14,6 +14,8 @@
const InteractionManager = require('InteractionManager');
const React = require('React');
const infoLog = require('infoLog');
const DEBUG = false;
/**
@@ -121,12 +123,12 @@ class Incremental extends React.Component<DefaultProps, Props, State> {
}
getName(): string {
var ctx = this.context.incrementalGroup || {};
const ctx = this.context.incrementalGroup || {};
return ctx.groupId + ':' + this._incrementId + '-' + this.props.name;
}
componentWillMount() {
var ctx = this.context.incrementalGroup;
const ctx = this.context.incrementalGroup;
if (!ctx) {
return;
}
@@ -134,15 +136,15 @@ class Incremental extends React.Component<DefaultProps, Props, State> {
InteractionManager.runAfterInteractions({
name: 'Incremental:' + this.getName(),
gen: () => new Promise(resolve => {
if (!this._mounted) {
if (!this._mounted || this._rendered) {
resolve();
return;
}
DEBUG && console.log('set doIncrementalRender for ' + this.getName());
DEBUG && infoLog('set doIncrementalRender for ' + this.getName());
this.setState({doIncrementalRender: true}, resolve);
}),
}).then(() => {
DEBUG && console.log('call onDone for ' + this.getName());
DEBUG && infoLog('call onDone for ' + this.getName());
this._mounted && this.props.onDone && this.props.onDone();
}).catch((ex) => {
ex.message = `Incremental render failed for ${this.getName()}: ${ex.message}`;
@@ -154,7 +156,7 @@ class Incremental extends React.Component<DefaultProps, Props, State> {
if (this._rendered || // Make sure that once we render once, we stay rendered even if incrementalGroupEnabled gets flipped.
!this.context.incrementalGroupEnabled ||
this.state.doIncrementalRender) {
DEBUG && console.log('render ' + this.getName());
DEBUG && infoLog('render ' + this.getName());
this._rendered = true;
return this.props.children;
}
+4 -2
View File
@@ -14,6 +14,8 @@
const Incremental = require('Incremental');
const React = require('React');
const infoLog = require('infoLog');
let _groupCounter = -1;
const DEBUG = false;
@@ -31,12 +33,12 @@ import type {Props, Context} from 'Incremental';
* See Incremental.js for more info.
*/
class IncrementalGroup extends React.Component {
props: Props;
props: Props & {disabled?: boolean};
context: Context;
_groupInc: string;
componentWillMount() {
this._groupInc = `g${++_groupCounter}-`;
DEBUG && console.log(
DEBUG && infoLog(
'create IncrementalGroup with id ' + this.getGroupId()
);
}
@@ -0,0 +1,146 @@
/**
* Copyright (c) 2013-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 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. *
*
* @providesModule SwipeableListView
* @flow
*/
'use strict';
const ListView = require('ListView');
const React = require('React');
const SwipeableListViewDataSource = require('SwipeableListViewDataSource');
const SwipeableRow = require('SwipeableRow');
const {PropTypes} = React;
/**
* A container component that renders multiple SwipeableRow's in a ListView
* implementation.
*/
const SwipeableListView = React.createClass({
statics: {
getNewDataSource(): Object {
return new SwipeableListViewDataSource({
getRowData: (data, sectionID, rowID) => data[rowID],
getSectionHeaderData: (data, sectionID) => data[sectionID],
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
rowHasChanged: (row1, row2) => row1 !== row2,
});
},
},
_listViewRef: (null: ?string),
propTypes: {
dataSource: PropTypes.instanceOf(SwipeableListViewDataSource).isRequired,
maxSwipeDistance: PropTypes.number,
// Callback method to render the swipeable view
renderRow: PropTypes.func.isRequired,
// Callback method to render the view that will be unveiled on swipe
renderQuickActions: PropTypes.func.isRequired,
},
getDefaultProps(): Object {
return {
renderQuickActions: () => null,
};
},
getInitialState(): Object {
return {
dataSource: this.props.dataSource,
};
},
componentWillReceiveProps(nextProps: Object): void {
if (
this.state.dataSource.getDataSource() !== nextProps.dataSource.getDataSource()
) {
this.setState({
dataSource: nextProps.dataSource,
});
}
},
render(): ReactElement {
return (
<ListView
{...this.props}
ref={(ref) => {
this._listViewRef = ref;
}}
dataSource={this.state.dataSource.getDataSource()}
renderRow={this._renderRow}
scrollEnabled={this.state.scrollEnabled}
/>
);
},
/**
* This is a work-around to lock vertical `ListView` scrolling on iOS and
* mimic Android behaviour. Locking vertical scrolling when horizontal
* scrolling is active allows us to significantly improve framerates
* (from high 20s to almost consistently 60 fps)
*/
_setListViewScrollable(value: boolean): void {
if (this._listViewRef && this._listViewRef.setNativeProps) {
this._listViewRef.setNativeProps({
scrollEnabled: value,
});
}
},
// Passing through ListView's getScrollResponder() function
getScrollResponder(): ?Object {
if (this._listViewRef && this._listViewRef.getScrollResponder) {
return this._listViewRef.getScrollResponder();
}
},
_renderRow(rowData: Object, sectionID: string, rowID: string): ReactElement {
const slideoutView = this.props.renderQuickActions(rowData, sectionID, rowID);
// If renderRowSlideout is unspecified or returns falsey, don't allow swipe
if (!slideoutView) {
return this.props.renderRow(rowData, sectionID, rowID);
}
return (
<SwipeableRow
slideoutView={slideoutView}
isOpen={rowData.id === this.props.dataSource.getOpenRowID()}
maxSwipeDistance={this.props.maxSwipeDistance}
key={rowID}
onOpen={() => this._onOpen(rowData.id)}
onSwipeEnd={() => this._setListViewScrollable(true)}
onSwipeStart={() => this._setListViewScrollable(false)}>
{this.props.renderRow(rowData, sectionID, rowID)}
</SwipeableRow>
);
},
_onOpen(rowID: string): void {
this.setState({
dataSource: this.state.dataSource.setOpenRowID(rowID),
});
},
});
module.exports = SwipeableListView;
@@ -0,0 +1,105 @@
/**
* Copyright (c) 2013-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 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. *
*
* @providesModule SwipeableListViewDataSource
*/
'use strict';
const ListViewDataSource = require('ListViewDataSource');
/**
* Data source wrapper around ListViewDataSource to allow for tracking of
* which row is swiped open and close opened row(s) when another row is swiped
* open.
*
* See https://github.com/facebook/react-native/pull/5602 for why
* ListViewDataSource is not subclassed.
*/
class SwipeableListViewDataSource {
_previousOpenRowID: string;
_openRowID: string;
_dataBlob: any;
_dataSource: ListViewDataSource;
rowIdentities: Array<Array<string>>;
sectionIdentities: Array<string>;
constructor(params: Object) {
this._dataSource = new ListViewDataSource({
getRowData: params.getRowData,
getSectionHeaderData: params.getSectionHeaderData,
rowHasChanged: (row1, row2) => {
/**
* Row needs to be re-rendered if its swiped open/close status is
* changed, or its data blob changed.
*/
return (
(row1.id !== this._previousOpenRowID && row2.id === this._openRowID) ||
(row1.id === this._previousOpenRowID && row2.id !== this._openRowID) ||
params.rowHasChanged(row1, row2)
);
},
sectionHeaderHasChanged: params.sectionHeaderHasChanged,
});
}
cloneWithRowsAndSections(
dataBlob: any,
sectionIdentities: ?Array<string>,
rowIdentities: ?Array<Array<string>>
): SwipeableListViewDataSource {
this._dataSource = this._dataSource.cloneWithRowsAndSections(
dataBlob,
sectionIdentities,
rowIdentities
);
this._dataBlob = dataBlob;
this.rowIdentities = this._dataSource.rowIdentities;
this.sectionIdentities = this._dataSource.sectionIdentities;
return this;
}
// For the actual ListView to use
getDataSource(): ListViewDataSource {
return this._dataSource;
}
getOpenRowID(): ?string {
return this._openRowID;
}
setOpenRowID(rowID: string): SwipeableListViewDataSource {
this._previousOpenRowID = this._openRowID;
this._openRowID = rowID;
this._dataSource = this._dataSource.cloneWithRowsAndSections(
this._dataBlob,
this.sectionIdentities,
this.rowIdentities
);
return this;
}
}
module.exports = SwipeableListViewDataSource;
@@ -0,0 +1,71 @@
/**
* Copyright (c) 2013-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 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. *
*
* @providesModule SwipeableQuickActionButton
* @flow
*/
'use strict';
const Image = require('Image');
const React = require('React');
const Text = require('Text');
const TouchableHighlight = require('TouchableHighlight');
const View = require('View');
const {PropTypes} = React;
const SwipeableQuickActionButton = React.createClass({
propTypes: {
accessibilityLabel: PropTypes.string,
imageSource: Image.propTypes.source.isRequired,
imageStyle: Image.propTypes.style,
onPress: PropTypes.func,
style: View.propTypes.style,
testID: PropTypes.string,
text: PropTypes.string,
textStyle: Text.propTypes.style,
},
render(): ?ReactElement {
if (!this.props.imageSource && !this.props.text) {
return null;
}
return (
<TouchableHighlight
onPress={this.props.onPress}
testID={this.props.testID}
underlayColor="transparent">
<View style={this.props.style}>
<Image
accessibilityLabel={this.props.accessibilityLabel}
source={this.props.imageSource}
style={this.props.imageStyle}
/>
<Text style={this.props.textStyle}>
{this.props.text}
</Text>
</View>
</TouchableHighlight>
);
},
});
module.exports = SwipeableQuickActionButton;
@@ -0,0 +1,73 @@
/**
* Copyright (c) 2013-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 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. *
*
* @providesModule SwipeableQuickActions
* @flow
*/
'use strict';
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
const MAX_QUICK_ACTIONS = 2;
const SwipeableQuickActions = React.createClass({
propTypes: {
style: View.propTypes.style,
},
render(): ReactElement {
const children = this.props.children;
let buttons = [];
// Multiple children
if (children instanceof Array) {
for (let i = 0; i < children.length && i < MAX_QUICK_ACTIONS; i++) {
buttons.push(children[i]);
if (i < this.props.children.length - 1) { // Not last button
buttons.push(<View key={i} style={styles.divider} />);
}
}
} else { // 1 child
buttons = children;
}
return (
<View style={[styles.background, this.props.style]}>
{buttons}
</View>
);
},
});
const styles = StyleSheet.create({
background: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
},
divider: {
width: 4,
},
});
module.exports = SwipeableQuickActions;
@@ -0,0 +1,244 @@
/**
* Copyright (c) 2013-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 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. *
*
* @providesModule SwipeableRow
* @flow
*/
'use strict';
const Animated = require('Animated');
const PanResponder = require('PanResponder');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
const {PropTypes} = React;
const emptyFunction = require('emptyFunction');
// Position of the left of the swipable item when closed
const CLOSED_LEFT_POSITION = 0;
// Minimum swipe distance before we recognize it as such
const HORIZONTAL_SWIPE_DISTANCE_THRESHOLD = 15;
/**
* Creates a swipable row that allows taps on the main item and a custom View
* on the item hidden behind the row
*/
const SwipeableRow = React.createClass({
_panResponder: {},
_previousLeft: CLOSED_LEFT_POSITION,
propTypes: {
isOpen: PropTypes.bool,
maxSwipeDistance: PropTypes.number.isRequired,
onOpen: PropTypes.func,
onSwipeEnd: PropTypes.func.isRequired,
onSwipeStart: PropTypes.func.isRequired,
/**
* A ReactElement that is unveiled when the user swipes
*/
slideoutView: PropTypes.node.isRequired,
/**
* The minimum swipe distance required before fully animating the swipe. If
* the user swipes less than this distance, the item will return to its
* previous (open/close) position.
*/
swipeThreshold: PropTypes.number.isRequired,
},
getInitialState(): Object {
return {
currentLeft: new Animated.Value(this._previousLeft),
/**
* In order to render component A beneath component B, A must be rendered
* before B. However, this will cause "flickering", aka we see A briefly
* then B. To counter this, _isSwipeableViewRendered flag is used to set
* component A to be transparent until component B is loaded.
*/
isSwipeableViewRendered: false,
rowHeight: (null: ?number),
};
},
getDefaultProps(): Object {
return {
isOpen: false,
maxSwipeDistance: 0,
onSwipeEnd: emptyFunction,
onSwipeStart: emptyFunction,
swipeThreshold: 30,
};
},
componentWillMount(): void {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (event, gestureState) => true,
// Don't capture child's start events
onStartShouldSetPanResponderCapture: (event, gestureState) => false,
onMoveShouldSetPanResponder: (event, gestureState) => false,
onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminationRequest: this._onPanResponderTerminationRequest,
onPanResponderTerminate: this._handlePanResponderEnd,
});
},
componentWillReceiveProps(nextProps: Object): void {
/**
* We do not need an "animateOpen(noCallback)" because this animation is
* handled internally by this component.
*/
if (this.props.isOpen && !nextProps.isOpen) {
this._animateToClosedPosition();
}
},
render(): ReactElement {
// The view hidden behind the main view
let slideOutView;
if (this.state.isSwipeableViewRendered) {
slideOutView = (
<View style={[
styles.slideOutContainer,
{height: this.state.rowHeight},
]}>
{this.props.slideoutView}
</View>
);
}
// The swipeable item
const swipeableView = (
<Animated.View
onLayout={this._onSwipeableViewLayout}
style={[
styles.swipeableContainer,
{
transform: [{translateX: this.state.currentLeft}],
},
]}>
{this.props.children}
</Animated.View>
);
return (
<View
{...this._panResponder.panHandlers}>
{slideOutView}
{swipeableView}
</View>
);
},
_onSwipeableViewLayout(event: Object): void {
if (!this.state.isSwipeableViewRendered) {
this.setState({
isSwipeableViewRendered: true,
rowHeight: event.nativeEvent.layout.height,
});
}
},
_handleMoveShouldSetPanResponderCapture(
event: Object,
gestureState: Object,
): boolean {
// Decides whether a swipe is responded to by this component or its child
return gestureState.dy < 10 && this._isValidSwipe(gestureState);
},
_handlePanResponderGrant(event: Object, gestureState: Object): void {
},
_handlePanResponderMove(event: Object, gestureState: Object): void {
this.props.onSwipeStart();
this.state.currentLeft.setValue(this._previousLeft + gestureState.dx);
},
_onPanResponderTerminationRequest(event: Object, gestureState: Object): boolean {
return false;
},
_animateTo(toValue: number): void {
Animated.timing(
this.state.currentLeft,
{
toValue: toValue,
},
).start(() => {
this._previousLeft = toValue;
});
},
_animateToOpenPosition(): void {
this._animateTo(-this.props.maxSwipeDistance);
},
_animateToClosedPosition(): void {
this._animateTo(CLOSED_LEFT_POSITION);
},
// Ignore swipes due to user's finger moving slightly when tapping
_isValidSwipe(gestureState: Object): boolean {
return Math.abs(gestureState.dx) > HORIZONTAL_SWIPE_DISTANCE_THRESHOLD;
},
_handlePanResponderEnd(event: Object, gestureState: Object): void {
const horizontalDistance = gestureState.dx;
if (Math.abs(horizontalDistance) > this.props.swipeThreshold) {
if (horizontalDistance < 0) {
// Swiped left
this.props.onOpen && this.props.onOpen();
this._animateToOpenPosition();
} else {
// Swiped right
this._animateToClosedPosition();
}
} else {
if (this._previousLeft === CLOSED_LEFT_POSITION) {
this._animateToClosedPosition();
} else {
this._animateToOpenPosition();
}
}
this.props.onSwipeEnd();
},
});
const styles = StyleSheet.create({
slideOutContainer: {
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
top: 0,
},
swipeableContainer: {
flex: 1,
},
});
module.exports = SwipeableRow;
+9 -8
View File
@@ -20,19 +20,20 @@
const ViewabilityHelper = {
computeViewableRows(
viewablePercentThreshold: number,
rowFrames: Array<Object>,
rowFrames: {[key: string]: Object},
data: Array<{rowKey: string, rowData: any}>,
scrollOffsetY: number,
viewportHeight: number
): Array<number> {
var viewableRows = [];
var firstVisible = -1;
for (var idx = 0; idx < rowFrames.length; idx++) {
var frame = rowFrames[idx];
const viewableRows = [];
let firstVisible = -1;
for (let idx = 0; idx < data.length; idx++) {
const frame = rowFrames[data[idx].rowKey];
if (!frame) {
continue;
}
var top = frame.y - scrollOffsetY;
var bottom = top + frame.height;
const top = frame.y - scrollOffsetY;
const bottom = top + frame.height;
if ((top < viewportHeight) && (bottom > 0)) {
firstVisible = idx;
if (_isViewable(
@@ -67,7 +68,7 @@ function _getPercentOccupied(
bottom: number,
viewportHeight: number
): number {
var visibleHeight = Math.min(bottom, viewportHeight) - Math.max(top, 0);
let visibleHeight = Math.min(bottom, viewportHeight) - Math.max(top, 0);
visibleHeight = Math.max(0, visibleHeight);
return Math.max(0, visibleHeight * 100 / viewportHeight);
}
+157 -104
View File
@@ -43,6 +43,7 @@ const ViewabilityHelper = require('ViewabilityHelper');
const clamp = require('clamp');
const deepDiffer = require('deepDiffer');
const infoLog = require('infoLog');
const invariant = require('invariant');
const nullthrows = require('nullthrows');
@@ -83,13 +84,13 @@ type Props = {
* A simple array of data blobs that are passed to the renderRow function in
* order. Note there is no dataSource like in the standard `ListView`.
*/
data: Array<any>;
data: Array<{rowKey: string, rowData: any}>;
/**
* Takes a data blob from the `data` array prop plus some meta info and should
* return a row.
*/
renderRow: (
data: any, sectionIdx: number, rowIdx: number, key?: string
rowData: any, sectionIdx: number, rowIdx: number, rowKey: string
) => ?ReactElement;
/**
* Rendered when the list is scrolled faster than rows can be rendered.
@@ -130,13 +131,6 @@ type Props = {
* determine how many rows to render above the viewport.
*/
numToRenderAhead: number;
/**
* Super dangerous and experimental - rows and all their decendents must be
* fully stateless otherwise recycling their instances may introduce nasty
* bugs. Some apps may see an improvement in perf, but sometimes perf and
* memory usage can actually get worse with this.
*/
enableDangerousRecycling: boolean;
/**
* Used to log perf events for async row rendering.
*/
@@ -177,26 +171,27 @@ class WindowedListView extends React.Component {
_firstVisible: number = -1;
_lastVisible: number = -1;
_scrollOffsetY: number = 0;
_isScrolling: boolean = false;
_frameHeight: number = 0;
_rowFrames: Array<Object> = [];
_rowFrames: {[key: string]: Object} = {};
_rowRenderMode: {[key: string]: null | 'async' | 'sync'} = {};
_rowFramesDirty: boolean = false;
_hasCalledOnEndReached: bool = false;
_willComputeRowsToRender: bool = false;
_hasCalledOnEndReached: boolean = false;
_willComputeRowsToRender: boolean = false;
_timeoutHandle: number = 0;
_incrementPending: bool = false;
_incrementPending: boolean = false;
_viewableRows: Array<number> = [];
_cellsInProgress: Set<number> = new Set();
_cellsInProgress: Set<string> = new Set();
_scrollRef: ?Object;
static defaultProps = {
enableDangerousRecycling: false,
initialNumToRender: 10,
maxNumToRender: 30,
numToRenderAhead: 10,
viewablePercentThreshold: 50,
renderScrollComponent: (props) => <ScrollView {...props} />,
disableIncrementalRendering: false,
recomputeRowsBatchingPeriod: 100,
recomputeRowsBatchingPeriod: 10, // This should capture most events that will happen in one frame
};
constructor(props: Props) {
@@ -239,23 +234,26 @@ class WindowedListView extends React.Component {
componentWillReceiveProps(newProps: Object) {
// This has to happen immediately otherwise we could crash, e.g. if the data
// array has gotten shorter.
if (newProps.data.length < this._rowFrames.length) {
this._rowFrames = this._rowFrames.splice(0, newProps.data.length);
}
this._computeRowsToRender(newProps);
}
_onMomentumScrollEnd = (e: Object) => {
this._onScroll(e);
};
_onScroll = (e: Object) => {
this._scrollOffsetY = e.nativeEvent.contentOffset.y;
const newScrollY = e.nativeEvent.contentOffset.y;
this._isScrolling = this._scrollOffsetY !== newScrollY;
this._scrollOffsetY = newScrollY;
this._frameHeight = e.nativeEvent.layoutMeasurement.height;
// We don't want to enqueue any updates if any cells are in the middle of an incremental render,
// because it would just be wasted work.
if (this._cellsInProgress.size === 0) {
this._enqueueComputeRowsToRender();
}
if (this.props.onViewableRowsChanged && this._rowFrames.length) {
if (this.props.onViewableRowsChanged && Object.keys(this._rowFrames).length) {
const viewableRows = ViewabilityHelper.computeViewableRows(
this.props.viewablePercentThreshold,
this._rowFrames,
this.props.data,
e.nativeEvent.contentOffset.y,
e.nativeEvent.layoutMeasurement.height
);
@@ -267,35 +265,44 @@ class WindowedListView extends React.Component {
this.props.onScroll && this.props.onScroll(e);
};
// Caller does the diffing so we don't have to.
_onNewLayout = (params: {rowIndex: number, layout: Object}) => {
const {rowIndex, layout} = params;
_onNewLayout = (params: {rowKey: string, layout: Object}) => {
const {rowKey, layout} = params;
if (DEBUG) {
const layoutPrev = this._rowFrames[rowIndex] || {};
console.log(
const layoutPrev = this._rowFrames[rowKey] || {};
infoLog(
'record layout for row: ',
{i: rowIndex, h: layout.height, y: layout.y, x: layout.x, hp: layoutPrev.height, yp: layoutPrev.y}
{k: rowKey, h: layout.height, y: layout.y, x: layout.x, hp: layoutPrev.height, yp: layoutPrev.y}
);
}
this._rowFrames[rowIndex] = {...layout, offscreenLayoutDone: true};
if (this._rowFrames[rowKey]) {
const deltaY = Math.abs(this._rowFrames[rowKey].y - layout.y);
const deltaH = Math.abs(this._rowFrames[rowKey].height - layout.height);
if (deltaY > 2 || deltaH > 2) {
const dataEntry = this.props.data.find((datum) => datum.rowKey === rowKey);
console.warn('layout jump: ', {dataEntry, prevLayout: this._rowFrames[rowKey], newLayout: layout});
}
}
this._rowFrames[rowKey] = {...layout, offscreenLayoutDone: true};
this._rowFramesDirty = true;
if (this._cellsInProgress.size === 0) {
this._enqueueComputeRowsToRender();
}
};
_onWillUnmountCell = (rowIndex: number) => {
if (this._rowFrames[rowIndex]) {
this._rowFrames[rowIndex].offscreenLayoutDone = false;
_onWillUnmountCell = (rowKey: string) => {
if (this._rowFrames[rowKey]) {
this._rowFrames[rowKey].offscreenLayoutDone = false;
this._rowRenderMode[rowKey] = null;
}
};
/**
* This is used to keep track of cells that are in the process of rendering. If any cells are in progress, then
* other updates are skipped because they will just be wasted work.
*/
_onProgressChange = ({rowIndex, inProgress}: {rowIndex: number, inProgress: boolean}) => {
_onProgressChange = ({rowKey, inProgress}: {rowKey: string, inProgress: boolean}) => {
if (inProgress) {
this._cellsInProgress.add(rowIndex);
this._cellsInProgress.add(rowKey);
} else {
this._cellsInProgress.delete(rowIndex);
this._cellsInProgress.delete(rowKey);
}
};
/**
@@ -332,10 +339,11 @@ class WindowedListView extends React.Component {
const rowFrames = this._rowFrames;
let firstVisible = -1;
let lastVisible = 0;
let lastRow = this.state.lastRow;
const top = this._scrollOffsetY;
const bottom = top + this._frameHeight;
for (let idx = 0; idx < rowFrames.length; idx++) {
const frame = rowFrames[idx];
for (let idx = 0; idx < lastRow; idx++) {
const frame = rowFrames[props.data[idx].rowKey];
if (!frame) {
// No frame - sometimes happens when they come out of order, so just wait for the rest.
return;
@@ -357,14 +365,13 @@ class WindowedListView extends React.Component {
// Unfortuantely, we can't use <Incremental> to simplify our increment logic in this function because we need to
// make sure that cells are rendered in the right order one at a time when scrolling back up.
const numRendered = this.state.lastRow - this.state.firstRow + 1;
const numRendered = lastRow - this.state.firstRow + 1;
// Our last row target that we will approach incrementally
const targetLastRow = clamp(
numRendered - 1, // Don't reduce numRendered when scrolling back up
lastVisible + props.numToRenderAhead, // Primary goal
totalRows - 1, // Don't render past the end
);
let lastRow = this.state.lastRow;
// Increment the last row one at a time per JS event loop
if (!this._incrementPending) {
if (targetLastRow > this.state.lastRow) {
@@ -403,8 +410,11 @@ class WindowedListView extends React.Component {
const rowsShouldChange = firstRow !== this.state.firstRow || lastRow !== this.state.lastRow;
if (this._rowFramesDirty || rowsShouldChange) {
if (rowsShouldChange) {
this.props.onMountedRowsWillChange && this.props.onMountedRowsWillChange(firstRow, lastRow - firstRow + 1);
console.log('WLV: row render range will change:', {firstRow, lastRow});
props.onMountedRowsWillChange && props.onMountedRowsWillChange(firstRow, lastRow - firstRow + 1);
infoLog(
'WLV: row render range will change:',
{firstRow, firstVis: this._firstVisible, lastVis: this._lastVisible, lastRow},
);
}
this._rowFramesDirty = false;
this.setState({firstRow, lastRow});
@@ -425,15 +435,40 @@ class WindowedListView extends React.Component {
const rowFrames = this._rowFrames;
const rows = [];
let spacerHeight = 0;
// Incremental rendering is a tradeoff between throughput and responsiveness. When we have plenty of buffer (say 50%
// of the target), we render incrementally to keep the app responsive. If we are dangerously low on buffer (say
// below 25%) we always disable incremental to try to catch up as fast as possible. In the middle, we only disable
// incremental while scrolling since it's unlikely the user will try to press a button while scrolling. We also
// ignore the "buffer" size when we are bumped up against the edge of the available data.
const firstBuffer = firstRow === 0 ? Infinity : this._firstVisible - firstRow;
const lastBuffer = lastRow === this.props.data.length - 1 ? Infinity : lastRow - this._lastVisible;
const minBuffer = Math.min(firstBuffer, lastBuffer);
const disableIncrementalRendering = this.props.disableIncrementalRendering ||
(this._isScrolling && minBuffer < this.props.numToRenderAhead * 0.5) ||
(minBuffer < this.props.numToRenderAhead * 0.25);
// Render mode is sticky while the component is mounted.
for (let ii = firstRow; ii <= lastRow; ii++) {
if (!rowFrames[ii]) {
const rowKey = this.props.data[ii].rowKey;
if (this._rowRenderMode[rowKey] === 'sync' || (disableIncrementalRendering && this._rowRenderMode[rowKey] !== 'async')) {
this._rowRenderMode[rowKey] = 'sync';
} else {
this._rowRenderMode[rowKey] = 'async';
}
}
for (let ii = firstRow; ii <= lastRow; ii++) {
const rowKey = this.props.data[ii].rowKey;
if (!rowFrames[rowKey]) {
break; // if rowFrame missing, no following ones will exist so quit early
}
// Look for the first row where offscreen layout is done (only true for mounted rows) and set the spacer height
// such that it will offset all the unmounted rows before that one using the saved frame data.
if (rowFrames[ii].offscreenLayoutDone) {
const frame = rowFrames[ii - 1];
spacerHeight = frame ? frame.y + frame.height : 0;
// Look for the first row where offscreen layout is done (only true for mounted rows) or it will be rendered
// synchronously and set the spacer height such that it will offset all the unmounted rows before that one using
// the saved frame data.
if (rowFrames[rowKey].offscreenLayoutDone || this._rowRenderMode[rowKey] === 'sync') {
if (ii > 0) {
const prevRowKey = this.props.data[ii - 1].rowKey;
const frame = rowFrames[prevRowKey];
spacerHeight = frame ? frame.y + frame.height : 0;
}
break;
}
}
@@ -442,7 +477,7 @@ class WindowedListView extends React.Component {
showIndicator = true;
spacerHeight -= this.state.boundaryIndicatorHeight || 0;
}
DEBUG && console.log('render top spacer with height ', spacerHeight);
DEBUG && infoLog('render top spacer with height ', spacerHeight);
rows.push(<View key="sp-top" style={{height: spacerHeight}} />);
if (this.props.renderWindowBoundaryIndicator) {
// Always render it, even if removed, so that we can get the height right away and don't waste time creating/
@@ -462,25 +497,27 @@ class WindowedListView extends React.Component {
);
}
for (let idx = firstRow; idx <= lastRow; idx++) {
const key = '' + (this.props.enableDangerousRecycling ? (idx % this.props.maxNumToRender) : idx);
const rowKey = this.props.data[idx].rowKey;
const includeInLayout = this._rowRenderMode[rowKey] === 'sync' ||
(this._rowFrames[rowKey] && this._rowFrames[rowKey].offscreenLayoutDone);
rows.push(
<CellRenderer
key={key}
recyclingKey={key}
key={rowKey}
rowKey={rowKey}
rowIndex={idx}
onNewLayout={this._onNewLayout}
onWillUnmount={this._onWillUnmountCell}
includeInLayout={this.props.disableIncrementalRendering ||
(this._rowFrames[idx] && this._rowFrames[idx].offscreenLayoutDone)}
includeInLayout={includeInLayout}
onProgressChange={this._onProgressChange}
asyncRowPerfEventName={this.props.asyncRowPerfEventName}
data={this.props.data[idx]}
rowData={this.props.data[idx].rowData}
renderRow={this.props.renderRow}
/>
);
}
const showFooter = this._rowFrames[lastRow] &&
this._rowFrames[lastRow].offscreenLayoutDone &&
const lastRowKey = this.props.data[lastRow].rowKey;
const showFooter = this._rowFrames[lastRowKey] &&
this._rowFrames[lastRowKey].offscreenLayoutDone &&
lastRow === this.props.data.length - 1;
if (this.props.renderFooter) {
rows.push(
@@ -509,17 +546,16 @@ class WindowedListView extends React.Component {
// Prevent user from scrolling into empty space of unmounted rows.
const contentInset = {top: firstRow === 0 ? 0 : -spacerHeight};
return (
<IncrementalGroup name="WLV" disabled={this.props.disableIncrementalRendering}>
{this.props.renderScrollComponent({
scrollEventThrottle: 50,
removeClippedSubviews: true,
...this.props,
contentInset,
ref: (ref) => { this._scrollRef = ref; },
onScroll: this._onScroll,
children: rows,
})}
</IncrementalGroup>
this.props.renderScrollComponent({
scrollEventThrottle: 50,
removeClippedSubviews: true,
...this.props,
contentInset,
ref: (ref) => { this._scrollRef = ref; },
onScroll: this._onScroll,
onMomentumScrollEnd: this._onMomentumScrollEnd,
children: rows,
})
);
}
}
@@ -531,11 +567,14 @@ type CellProps = {
/**
* Row-specific data passed to renderRow and used in shouldComponentUpdate with ===
*/
data: mixed;
rowData: mixed;
rowKey: string;
/**
* Renders the actual row contents.
*/
renderRow: (data: mixed, sectionIdx: number, rowIdx: number) => ?ReactElement;
renderRow: (
rowData: mixed, sectionIdx: number, rowIdx: number, rowKey: string
) => ?ReactElement;
/**
* Index of the row, passed through to other callbacks.
*/
@@ -557,21 +596,22 @@ type CellProps = {
* Updates the parent with the latest layout. Only called when incremental rendering is done and triggers the parent
* to re-render this row with includeInLayout true.
*/
onNewLayout: (params: {rowIndex: number, layout: Object}) => void;
onNewLayout: (params: {rowKey: string, layout: Object}) => void;
/**
* Used to track when rendering is in progress so the parent can avoid wastedful re-renders that are just going to be
* invalidated once the cell finishes.
*/
onProgressChange: (progress: {rowIndex: number; inProgress: boolean}) => void;
onProgressChange: (progress: {rowKey: string; inProgress: boolean}) => void;
/**
* Used to invalidate the layout so the parent knows it needs to compensate for the height in the placeholder size.
*/
onWillUnmount: (rowIndex: number) => void;
onWillUnmount: (rowKey: string) => void;
};
class CellRenderer extends React.Component {
props: CellProps;
_containerRef: View;
_offscreenRenderDone = false;
_timer = 0;
_timeout = 0;
_lastLayout: ?Object = null;
_perfUpdateID: number = 0;
_asyncCookie: any;
@@ -580,12 +620,12 @@ class CellRenderer extends React.Component {
if (this.props.asyncRowPerfEventName) {
this._perfUpdateID = g_perf_update_id++;
this._asyncCookie = Systrace.beginAsyncEvent(this.props.asyncRowPerfEventName + this._perfUpdateID);
console.log(`perf_asynctest_${this.props.asyncRowPerfEventName}_start ${this._perfUpdateID} ${Date.now()}`);
infoLog(`perf_asynctest_${this.props.asyncRowPerfEventName}_start ${this._perfUpdateID} ${Date.now()}`);
}
if (this.props.includeInLayout) {
this._includeInLayoutLatch = true;
}
this.props.onProgressChange({rowIndex: this.props.rowIndex, inProgress: true});
this.props.onProgressChange({rowKey: this.props.rowKey, inProgress: true});
}
_onLayout = (e) => {
const layout = e.nativeEvent.layout;
@@ -595,64 +635,77 @@ class CellRenderer extends React.Component {
return; // Don't send premature or duplicate updates
}
this.props.onNewLayout({
rowIndex: this.props.rowIndex,
rowKey: this.props.rowKey,
layout,
});
};
_updateParent() {
invariant(!this._offscreenRenderDone, 'should only finish rendering once');
this._offscreenRenderDone = true;
// If this is not called before calling onNewLayout, the number of inProgress cells will remain non-zero,
// and thus the onNewLayout call will not fire the needed state change update.
this.props.onProgressChange({rowKey: this.props.rowKey, inProgress: false});
// If an onLayout event hasn't come in yet, then we skip here and assume it will come in later. This happens
// when Incremental is disabled and _onOffscreenRenderDone is called faster than layout can happen.
this._lastLayout && this.props.onNewLayout({rowKey: this.props.rowKey, layout: this._lastLayout});
DEBUG && infoLog('\n >>>>> display row ' + this.props.rowIndex + '\n\n\n');
if (this.props.asyncRowPerfEventName) {
// Note this doesn't include the native render time but is more accurate than also including the JS render
// time of anything that has been queued up.
Systrace.endAsyncEvent(this.props.asyncRowPerfEventName + this._perfUpdateID, this._asyncCookie);
infoLog(`perf_asynctest_${this.props.asyncRowPerfEventName}_end ${this._perfUpdateID} ${Date.now()}`);
}
}
_onOffscreenRenderDone = () => {
DEBUG && console.log('_onOffscreenRenderDone for row ' + this.props.rowIndex);
this._timer = setTimeout(() => { // Flush any pending layout events.
invariant(!this._offscreenRenderDone, 'should only finish rendering once');
this._offscreenRenderDone = true;
// If this is not called before calling onNewLayout, the number of inProgress cells will remain non-zero,
// and thus the onNewLayout call will not fire the needed state change update.
this.props.onProgressChange({rowIndex: this.props.rowIndex, inProgress: false});
// If an onLayout event hasn't come in yet, then we skip here and assume it will come in later. This happens
// when Incremental is disabled and _onOffscreenRenderDone is called faster than layout can happen.
this._lastLayout && this.props.onNewLayout({rowIndex: this.props.rowIndex, layout: this._lastLayout});
DEBUG && console.log('\n >>>>> display row ' + this.props.rowIndex + '\n\n\n');
if (this.props.asyncRowPerfEventName) {
Systrace.endAsyncEvent(this.props.asyncRowPerfEventName + this._perfUpdateID, this._asyncCookie);
console.log(`perf_asynctest_${this.props.asyncRowPerfEventName}_end ${this._perfUpdateID} ${Date.now()}`);
}
}, 1);
DEBUG && infoLog('_onOffscreenRenderDone for row ' + this.props.rowIndex);
if (this._includeInLayoutLatch) {
this._updateParent(); // rendered straight into layout, so no need to flush
} else {
this._timeout = setTimeout(() => this._updateParent(), 1); // Flush any pending layout events.
}
};
componentWillUnmount() {
clearTimeout(this._timer);
this.props.onProgressChange({rowIndex: this.props.rowIndex, inProgress: false});
this.props.onWillUnmount(this.props.rowIndex);
clearTimeout(this._timeout);
this.props.onProgressChange({rowKey: this.props.rowKey, inProgress: false});
this.props.onWillUnmount(this.props.rowKey);
}
componentWillReceiveProps(newProps) {
if (newProps.includeInLayout && !this.props.includeInLayout) {
invariant(this._offscreenRenderDone, 'Should never try to add to layout before render done');
this._includeInLayoutLatch = true; // Once we render in layout, make sure it sticks.
this.refs.container.setNativeProps({style: styles.include});
this._containerRef.setNativeProps({style: styles.include});
}
}
shouldComponentUpdate(newProps) {
return newProps.data !== this.props.data;
shouldComponentUpdate(newProps: CellProps) {
return newProps.rowData !== this.props.rowData;
}
_setRef = (ref) => {
this._containerRef = ref;
};
render() {
let debug;
if (DEBUG) {
console.log('render cell ' + this.props.rowIndex);
infoLog('render cell ' + this.props.rowIndex);
const Text = require('Text');
debug = <Text style={{backgroundColor: 'lightblue'}}>
Row: {this.props.rowIndex}
</Text>;
}
const style = (this._includeInLayoutLatch || this.props.includeInLayout) ? styles.include : styles.remove;
const style = this._includeInLayoutLatch ? styles.include : styles.remove;
return (
<IncrementalGroup onDone={this._onOffscreenRenderDone} name={`CellRenderer_${this.props.rowIndex}`}>
<IncrementalGroup
disabled={this._includeInLayoutLatch}
onDone={this._onOffscreenRenderDone}
name={`WLVCell_${this.props.rowIndex}`}>
<View
ref="container"
ref={this._setRef}
style={style}
onLayout={this._onLayout}>
{debug}
{this.props.renderRow(this.props.data, 0, this.props.rowIndex)}
{this.props.renderRow(this.props.rowData, 0, this.props.rowIndex, this.props.rowKey)}
{debug}
</View>
</IncrementalGroup>
+5 -4
View File
@@ -130,11 +130,11 @@ RCT_EXPORT_MODULE()
#pragma mark - Private API
- (void)beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy
- (void)beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy distanceFilter:(CLLocationDistance)distanceFilter
{
if (!_locationManager) {
_locationManager = [CLLocationManager new];
_locationManager.distanceFilter = _observerOptions.distanceFilter;
_locationManager.delegate = self;
}
@@ -147,6 +147,7 @@ RCT_EXPORT_MODULE()
[_locationManager requestWhenInUseAuthorization];
}
_locationManager.distanceFilter = distanceFilter;
_locationManager.desiredAccuracy = desiredAccuracy;
// Start observing location
[_locationManager startUpdatingLocation];
@@ -179,7 +180,7 @@ RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options)
_observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy);
}
[self beginLocationUpdatesWithDesiredAccuracy:_observerOptions.accuracy];
[self beginLocationUpdatesWithDesiredAccuracy:_observerOptions.accuracy distanceFilter:_observerOptions.distanceFilter];
_observingLocation = YES;
}
@@ -253,7 +254,7 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
if (_locationManager) {
accuracy = MIN(_locationManager.desiredAccuracy, accuracy);
}
[self beginLocationUpdatesWithDesiredAccuracy:accuracy];
[self beginLocationUpdatesWithDesiredAccuracy:accuracy distanceFilter:options.distanceFilter];
}
#pragma mark - CLLocationManagerDelegate
-12
View File
@@ -201,10 +201,6 @@ var Image = React.createClass({
validAttributes: ReactNativeViewAttributes.UIView
},
contextTypes: {
isInAParentText: React.PropTypes.bool
},
render: function() {
var source = resolveAssetSource(this.props.source) || {};
var {width, height, uri} = source;
@@ -225,13 +221,6 @@ var Image = React.createClass({
console.warn('The <Image> component requires a `source` property rather than `src`.');
}
if (this.context.isInAParentText) {
RawImage = RCTVirtualImage;
if (!width || !height) {
console.warn('You must specify a width and height for the image %s', uri);
}
}
return (
<RawImage
{...this.props}
@@ -252,7 +241,6 @@ var styles = StyleSheet.create({
var RCTImageView = requireNativeComponent('RCTImageView', Image);
var RCTNetworkImageView = NetworkImageViewManager ? requireNativeComponent('RCTNetworkImageView', Image) : RCTImageView;
var RCTVirtualImage = requireNativeComponent('RCTVirtualImage', Image);
module.exports = Image;
-2
View File
@@ -31,9 +31,7 @@ var ImageStylePropTypes = {
overflow: ReactPropTypes.oneOf(['visible', 'hidden']),
/**
* iOS-Specific style to "tint" an image.
* Changes the color of all the non-transparent pixels to the tintColor.
* @platform ios
*/
tintColor: ColorPropType,
opacity: ReactPropTypes.number,
@@ -44,10 +44,6 @@
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = "<group>"; };
139A38821C4D57AD00862840 /* RCTResizeMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTResizeMode.h; sourceTree = "<group>"; };
139A38831C4D587C00862840 /* RCTResizeMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTResizeMode.m; sourceTree = "<group>"; };
13EF7F071BC42D4E003F47DD /* RCTShadowVirtualImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowVirtualImage.h; sourceTree = "<group>"; };
13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowVirtualImage.m; sourceTree = "<group>"; };
13EF7F091BC42D4E003F47DD /* RCTVirtualImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualImageManager.h; sourceTree = "<group>"; };
13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVirtualImageManager.m; sourceTree = "<group>"; };
13EF7F7D1BC825B1003F47DD /* RCTXCAssetImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTXCAssetImageLoader.h; sourceTree = "<group>"; };
13EF7F7E1BC825B1003F47DD /* RCTXCAssetImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTXCAssetImageLoader.m; sourceTree = "<group>"; };
143879361AAD32A300F088A5 /* RCTImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageLoader.h; sourceTree = "<group>"; };
@@ -95,10 +91,6 @@
35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */,
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
13EF7F071BC42D4E003F47DD /* RCTShadowVirtualImage.h */,
13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */,
13EF7F091BC42D4E003F47DD /* RCTVirtualImageManager.h */,
13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */,
58B5115E1A9E6B3D00147676 /* Products */,
);
indentWidth = 2;
@@ -169,7 +161,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */,
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
@@ -178,7 +169,6 @@
139A38841C4D587C00862840 /* RCTResizeMode.m in Sources */,
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
EEF314721C9B0DD30049118E /* RCTImageBlurUtils.m in Sources */,
13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */,
13EF7F7F1BC825B1003F47DD /* RCTXCAssetImageLoader.m in Sources */,
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */,
);
+6 -4
View File
@@ -425,10 +425,12 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
if (!_pendingTasks) {
_pendingTasks = [NSMutableArray new];
}
[_pendingTasks addObject:task];
if (MAX(_activeTasks, _scheduledDecodes) < _maxConcurrentLoadingTasks) {
[task start];
_activeTasks++;
if (task) {
[_pendingTasks addObject:task];
if (MAX(_activeTasks, _scheduledDecodes) < _maxConcurrentLoadingTasks) {
[task start];
_activeTasks++;
}
}
cancelLoad = ^{
+1 -2
View File
@@ -8,13 +8,12 @@
*/
#import <UIKit/UIKit.h>
#import "RCTImageComponent.h"
#import "RCTResizeMode.h"
@class RCTBridge;
@class RCTImageSource;
@interface RCTImageView : UIImageView <RCTImageComponent>
@interface RCTImageView : UIImageView
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
+26 -8
View File
@@ -207,13 +207,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
scale:imageScale
resizeMode:(RCTResizeMode)self.contentMode
progressBlock:progressHandler
completionBlock:^(NSError *error, UIImage *image) {
completionBlock:^(NSError *error, UIImage *loadedImage) {
RCTImageView *strongSelf = weakSelf;
if (blurRadius > __FLT_EPSILON__) {
// Do this on the background thread to avoid blocking interaction
image = RCTBlurredImageWithRadius(image, blurRadius);
}
dispatch_async(dispatch_get_main_queue(), ^{
void (^setImageBlock)(UIImage *) = ^(UIImage *image) {
if (![source isEqual:strongSelf.source]) {
// Bail out if source has changed since we started loading
return;
@@ -234,9 +230,31 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
}
}
if (strongSelf->_onLoadEnd) {
strongSelf->_onLoadEnd(nil);
strongSelf->_onLoadEnd(nil);
}
});
};
if (blurRadius > __FLT_EPSILON__) {
// Blur on a background thread to avoid blocking interaction
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = RCTBlurredImageWithRadius(loadedImage, blurRadius);
RCTExecuteOnMainThread(^{
setImageBlock(image);
}, NO);
});
} else {
// No blur, so try to set the image on the main thread synchronously to minimize image
// flashing. (For instance, if this view gets attached to a window, then -didMoveToWindow
// calls -reloadImage, and we want to set the image synchronously if possible so that the
// image property is set in the same CATransaction that attaches this view to the window.)
if ([NSThread isMainThread]) {
setImageBlock(loadedImage);
} else {
RCTExecuteOnMainThread(^{
setImageBlock(loadedImage);
}, NO);
}
}
}];
} else {
[self clearImage];
-28
View File
@@ -1,28 +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 "RCTShadowView.h"
#import "RCTImageComponent.h"
#import "RCTImageSource.h"
#import "RCTResizeMode.h"
@class RCTBridge;
/**
* Shadow image component, used for embedding images in non-view contexts such
* as text. This is NOT used for ordinary <Image> views.
*/
@interface RCTShadowVirtualImage : RCTShadowView <RCTImageComponent>
- (instancetype)initWithBridge:(RCTBridge *)bridge;
@property (nonatomic, strong) RCTImageSource *source;
@property (nonatomic, assign) RCTResizeMode resizeMode;
@end
-82
View File
@@ -1,82 +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 "RCTShadowVirtualImage.h"
#import "RCTImageLoader.h"
#import "RCTImageUtils.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
@implementation RCTShadowVirtualImage
{
RCTBridge *_bridge;
RCTImageLoaderCancellationBlock _cancellationBlock;
}
@synthesize image = _image;
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if ((self = [super init])) {
_bridge = bridge;
}
return self;
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
- (void)didSetProps:(NSArray<NSString *> *)changedProps
{
[super didSetProps:changedProps];
if (changedProps.count == 0) {
// No need to reload image
return;
}
// Cancel previous request
if (_cancellationBlock) {
_cancellationBlock();
}
CGSize imageSize = {
RCTZeroIfNaN(self.width),
RCTZeroIfNaN(self.height),
};
__weak RCTShadowVirtualImage *weakSelf = self;
_cancellationBlock = [_bridge.imageLoader loadImageWithTag:_source.imageURL.absoluteString
size:imageSize
scale:RCTScreenScale()
resizeMode:_resizeMode
progressBlock:nil
completionBlock:^(NSError *error, UIImage *image) {
dispatch_async(_bridge.uiManager.methodQueue, ^{
RCTShadowVirtualImage *strongSelf = weakSelf;
if (![_source isEqual:strongSelf.source]) {
// Bail out if source has changed since we started loading
return;
}
strongSelf->_image = image;
[strongSelf dirtyText];
});
}];
}
- (void)dealloc
{
if (_cancellationBlock) {
_cancellationBlock();
}
}
@end
-25
View File
@@ -1,25 +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 "RCTVirtualImageManager.h"
#import "RCTShadowVirtualImage.h"
@implementation RCTVirtualImageManager
RCT_EXPORT_MODULE()
- (RCTShadowView *)shadowView
{
return [[RCTShadowVirtualImage alloc] initWithBridge:self.bridge];
}
RCT_EXPORT_SHADOW_PROPERTY(source, RCTImageSource)
RCT_EXPORT_SHADOW_PROPERTY(resizeMode, UIViewContentMode)
@end
+51 -2
View File
@@ -20,6 +20,8 @@ var Text = require('Text');
var TouchableHighlight = require('TouchableHighlight');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
var View = require('View');
var {SourceCode} = require('NativeModules');
var {fetch} = require('fetch');
var flattenStyle = require('flattenStyle');
var mapWithSeparator = require('mapWithSeparator');
@@ -32,11 +34,31 @@ var ElementProperties = React.createClass({
PropTypes.array,
PropTypes.number,
]),
source: PropTypes.shape({
fileName: PropTypes.string,
lineNumber: PropTypes.number,
}),
},
render: function() {
var style = flattenStyle(this.props.style);
var selection = this.props.selection;
var openFileButton;
var source = this.props.source;
var {fileName, lineNumber} = source || {};
if (fileName && lineNumber) {
var parts = fileName.split('/');
var fileNameShort = parts[parts.length - 1];
openFileButton = (
<TouchableHighlight
style={styles.openButton}
onPress={this._openFile.bind(null, fileName, lineNumber)}>
<Text style={styles.openButtonTitle} numberOfLines={1}>
{fileNameShort}:{lineNumber}
</Text>
</TouchableHighlight>
);
}
// Without the `TouchableWithoutFeedback`, taps on this inspector pane
// would change the inspected element to whatever is under the inspector
return (
@@ -63,13 +85,26 @@ var ElementProperties = React.createClass({
)}
</View>
<View style={styles.row}>
<StyleInspector style={style} />
<View style={styles.col}>
<StyleInspector style={style} />
{openFileButton}
</View>
<BoxInspector style={style} frame={this.props.frame} />
</View>
</View>
</TouchableWithoutFeedback>
);
}
},
_openFile: function(fileName: string, lineNumber: number) {
var match = SourceCode.scriptURL && SourceCode.scriptURL.match(/^https?:\/\/.*?\//);
var baseURL = match ? match[0] : 'http://localhost:8081/';
fetch(baseURL + 'open-stack-frame', {
method: 'POST',
body: JSON.stringify({file: fileName, lineNumber}),
});
},
});
var styles = StyleSheet.create({
@@ -101,6 +136,9 @@ var styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'space-between',
},
col: {
flex: 1,
},
info: {
padding: 10,
},
@@ -108,6 +146,17 @@ var styles = StyleSheet.create({
color: 'white',
fontSize: 9,
},
openButton: {
padding: 10,
backgroundColor: '#000',
marginVertical: 5,
marginRight: 5,
borderRadius: 2,
},
openButtonTitle: {
color: 'white',
fontSize: 8,
}
});
module.exports = ElementProperties;
+5 -1
View File
@@ -21,7 +21,7 @@ var InspectorUtils = require('InspectorUtils');
var React = require('React');
var StyleSheet = require('StyleSheet');
var Touchable = require('Touchable');
var UIManager = require('NativeModules').UIManager;
var UIManager = require('UIManager');
var View = require('View');
if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
@@ -129,11 +129,13 @@ class Inspector extends React.Component {
// if we inspect a stateless component we can't use the getPublicInstance method
// therefore we use the internal _instance property directly.
var publicInstance = instance['_instance'] || {};
var source = instance._currentElement && instance._currentElement._source;
UIManager.measure(instance.getNativeNode(), (x, y, width, height, left, top) => {
this.setState({
inspected: {
frame: {left, top, width, height},
style: publicInstance.props ? publicInstance.props.style : {},
source,
},
selection: i,
});
@@ -149,6 +151,7 @@ class Inspector extends React.Component {
// therefore we use the internal _instance property directly.
var publicInstance = instance._instance || {};
var props = publicInstance.props || {};
var source = instance._currentElement && instance._currentElement._source;
this.setState({
panelPos: pointerY > Dimensions.get('window').height / 2 ? 'top' : 'bottom',
selection: hierarchy.length - 1,
@@ -156,6 +159,7 @@ class Inspector extends React.Component {
inspected: {
style: props.style || {},
frame,
source,
},
});
}
+1 -1
View File
@@ -15,7 +15,7 @@ var Dimensions = require('Dimensions');
var InspectorUtils = require('InspectorUtils');
var React = require('React');
var StyleSheet = require('StyleSheet');
var UIManager = require('NativeModules').UIManager;
var UIManager = require('UIManager');
var View = require('View');
var ElementBox = require('ElementBox');
+1
View File
@@ -41,6 +41,7 @@ class InspectorPanel extends React.Component {
<ElementProperties
style={this.props.inspected.style}
frame={this.props.inspected.frame}
source={this.props.inspected.source}
hierarchy={this.props.hierarchy}
selection={this.props.selection}
setSelection={this.props.setSelection}

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