Compare commits

...

280 Commits

Author SHA1 Message Date
Nicolas Gallagher
a41af0f65f 0.4.0 2018-02-06 16:35:20 -08:00
Nicolas Gallagher
96eecc0da3 Replace lerna with custom script
Each package version is now updated with each release.

Fix #783
2018-02-06 16:30:03 -08:00
Nicolas Gallagher
69d5373222 [fix] Firefox rendering of TextInput
Removes UA controls from numeric inputs in Firefox, and makes the
default border solid in all browsers.

Fix #789
2018-02-06 11:17:38 -08:00
Nicolas Gallagher
538ab88eda Separate modules for StyleSheet and related side-effects 2018-02-05 12:18:43 -08:00
Nicolas Gallagher
21b3f39c0b [fix] babel-plugin require call for compiled modules
require('module') => require('module').default

Fix #786
2018-02-04 10:42:21 -08:00
Nicolas Gallagher
998e275e65 [add] CSS keyframes support via 'animationName' style
Keyframes can be defined using an array of objects as the value of
'animationName'. Each keyframe is transformed into a CSS keyframe rule.
2018-02-03 11:48:57 -08:00
Nicolas Gallagher
31d428a649 [fix] add support for TextInput placeholderTextColor prop
This change also ensures that potential object-styles (user-provided or
placeholderTextColor) are group together at the end of the style array.

Fix #560
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
240cf7e05f [change] AppRegistry and StyleSheet APIs to fix SSR
SSR was not working correctly. Styles would accumulate in the style
cache and the styles returned for each 'getApplication' call would not
represent the styles needed to render a given tree, but rather all the
styles needed to render every tree that has been rendered by that server
instance.

This is now fixed by reinitializing the style resolver after a call to
'getStyleSheet' on the server. The return type of
'AppRegistry.getApplication' is changed – { element, getStyleElement }.
The 'getStyleElement' function must be called after the component tree
has been rendered.  Note that if 'getStyleElement' is not called for a
response, then its styles may leak into the next response's styles (but
will not affect the UX).

This patch also removes the 'StyleSheet.getStyleSheets' (web-only) API
and requires SSR to be done using 'AppRegistry.getApplication'.

Fix #778
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
2ad710d83a [change] StyleSheet does not eagerly resolve and inject styles
Resolving styles at the same time as they are registered is problematic.
This work doesn't need to be complete until the moment a component is
rendered, rather than being done at startup.
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
dcce72b66e Reorganize style resolver
The style resolver is reorganized to remove React Native style
registration, and contain all the stateful resolver logic (previously
partially found in NativeMethodsMixin).

Style registration is done in 'StyleSheet.create', and will subsequently
be decoupled from eager style resolution.

The stateful resolver is fully contained in the
ReactNativeStyleResolver. The options are removed from the resolver as
they aren't needed to implement the correct i18n-ified styles. This
functionality is also covered by unit tests now.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
083769d642 [fix] resolve inline-style pointerEvents to classNames
pointerEvents styles are now always resolved to classNames, even if
working with dynamic styles. In practice, inline-style pointerEvents
have always resolved to classNames as an accident of 'createElement'
registering each pointerEvent style. However, this patch ensures that
the style resolver produces correct output, and introduces the basic
mechanism by which 'placeholderTextColor' (for which classNames cannot
be precomputed) could function similarly.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
a53dba8c62 [change] simplify CSS rule injection
WebStyleSheet handles injecting CSS rules and returning the style
sheet's text content.

All CSS, including the reset, is now added using WebStyleSheet and the DOM API 'insertRule'.
Browsers will throw a parse error on unrecognized vendor-prefixes in
selectors, so rules with unsafe selectors are wrapped in '@media all {}'
before being passed to 'insertRule'. (This will be relevant when
attempting to support 'placeholderTextColor'. And it is a way to
batch-insert rules with a single call to 'insertRule'.)

The html tag 'font-family' CSS reset is removed as it's not needed.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
670d43ba04 Refactor how pointerEvents styles are managed 2018-01-31 10:55:44 -08:00
Nicolas Gallagher
73a731f2da Rename StyleManager and StyleRegistry 2018-01-31 10:55:44 -08:00
Nicolas Gallagher
1542f1f369 0.3.4 2018-01-31 10:31:50 -08:00
Nicolas Gallagher
6f58d7abe7 [fix] TextInput support for spellCheck prop
Fix #501
2018-01-31 10:27:08 -08:00
Nicolas Gallagher
7e0fbf9691 Fix yarn.lock
Yarn didn't make these changes to yarn.lock after updating packages.
2018-01-29 12:46:12 -08:00
Maximilian Stoiber
865034e8f7 Update benchmark instructions 2018-01-29 12:40:37 -08:00
Nicolas Gallagher
6e96ee4f3c Update benchmarked libraries 2018-01-29 08:59:18 -08:00
Maxime Thirouin
16d98b49f0 [fix] ignore native-only Image props
Close #788
2018-01-26 14:59:59 -08:00
Nicolas Gallagher
d04721c75a Update yarn.lock 2018-01-23 13:01:48 -08:00
Nicolas Gallagher
8512709251 Fix benchmark picker touch target size in Safari 2018-01-23 12:24:56 -08:00
Nicolas Gallagher
efeaea70a9 Benchmarks include forced layout time
This change to 'benchmarks' reports the time taken to perform a forced
layout after mounting the tree. Adding a forced layout to the stress
tests can surface how different approaches to styling may affect browser
render timings.

The total time displayed is now the sum of "scripting time" (previously
total time) and "layout time". The layout time is a reflection of the
time the browser takes to perform a style recalculation and relayout of
the document.

The Benchmark component now has a 'forceLayout' prop. When it is 'true'
a forced layout is triggered on componentDidUpdate. The time taken is
added to the sample's timing data.
2018-01-23 12:24:18 -08:00
Nicolas Gallagher
a403244e67 Update benchmarks library dependencies 2018-01-23 09:49:48 -08:00
Oleg Slobodskoi
985c1d63b6 Add benchmarks update test for react-jss
Close #785
2018-01-23 09:11:38 -08:00
Nicolas Gallagher
9d8d4057f6 Benchmark app component optimizations 2018-01-22 15:52:37 -08:00
Nicolas Gallagher
ec8843fe90 Compute benchmark props before each iteration
Fix a bug in the logic that was meant to perform component prop
computation in-between cycles.
2018-01-22 13:24:41 -08:00
Nicolas Gallagher
935970156c Benchmarks report total run time 2018-01-21 17:02:21 -08:00
Nicolas Gallagher
e4e6147081 0.3.3 2018-01-19 14:05:46 -08:00
Nicolas Gallagher
3e1b68d801 Add Moto G4 example benchmark results 2018-01-19 13:59:13 -08:00
Giuseppe Gurgone
1b493c9914 Add styled-jsx to benchmarks
Close #782
2018-01-19 12:51:21 -08:00
hushicai
6ecdc1a517 [fix] babel-plugin VariableDeclaration case
Convert VariableDeclarations, e.g.,

var ReactNative = require('react-native');

Close #781
2018-01-19 00:09:43 -08:00
Nicolas Gallagher
619079cedf Move babel-plugin npm badge to correct README 2018-01-18 10:24:02 -08:00
Javi Velasco
bbf7674b43 Add react-fela to benchmarks
Close #779
2018-01-18 10:03:13 -08:00
Nicolas Gallagher
f163e4f16f Use full URLs for README links 2018-01-18 09:50:14 -08:00
Nicolas Gallagher
bd8c2d6f24 Add npm badge to babel-plugin-react-native-web README 2018-01-18 09:49:54 -08:00
Nicolas Gallagher
3906b6b41b Add benchmarks app link to README 2018-01-17 19:38:58 -08:00
Nicolas Gallagher
753ef963f6 0.3.2 2018-01-17 19:18:50 -08:00
Nicolas Gallagher
0721245b3e Patch release script
Disable husky on version commit. Current publish script doesn't account
for attempts to republish the same version.
2018-01-17 19:15:23 -08:00
Nicolas Gallagher
b7adfd5f32 [fix] Picker.Item label text
Render the label as a child of 'option' rather than using the 'label'
attribute.

Fix #774
2018-01-17 17:59:05 -08:00
Nicolas Gallagher
a9342daee2 Add release script for benchmarks 2018-01-17 17:40:13 -08:00
Nicolas Gallagher
ed0cafac7c Rewrite benchmarks app
Reorganizes and rewrites the benchmarks. Each implementation is now
self-contained and the benchmarks can be run using a GUI. The benchmarks
themselves have been changed so that individual tests render over a
shorter time frame and more samples are taken.
2018-01-17 17:21:53 -08:00
Nicolas Gallagher
6e6fd4b5d0 Add note about Object.assign polyfill to docs 2018-01-16 17:14:17 -08:00
Nicolas Gallagher
5cd533e6cc 0.3.1 2018-01-16 11:15:45 -08:00
Nicolas Gallagher
d5e8d85ce9 Fix example in babel plugin README 2018-01-16 11:11:50 -08:00
Nicolas Gallagher
e234568a34 Fix source links in documentation 2018-01-11 12:59:44 -08:00
Nicolas Gallagher
19cf0711bc [add] StyleSheet support for 'overscrollBehavior'
An experimental CSS property to control the behavior when the scroll
position of a scroll container reaches the edge of the scrollport. This
allows web apps to get closer to native scrolling behaviour and
performance.

https://wicg.github.io/overscroll-behavior/
https://developers.google.com/web/updates/2017/11/overscroll-behavior

Fix #765
2018-01-11 12:58:43 -08:00
Johannes
067e3f346f [fix] KeyboardAvoidingView missing 'this' binding
Close #762
2018-01-11 12:53:54 -08:00
Nicolas Gallagher
2117e44e9d [fix] limit Image loader deferral to 1000ms
This patch introduces a limit on how long image loading is deferred, and
mitigates an issue with lengthy delays to 'requestIdleCallback' in the
Chrome browser.

Fix #759
2018-01-11 12:08:30 -08:00
Nicolas Gallagher
902ba22877 Update to flow-bin@0.63.1 2018-01-09 17:49:52 -08:00
Nicolas Gallagher
60c2cd65df Update to lint-staged@6.0.0 2018-01-09 17:36:19 -08:00
Nicolas Gallagher
fde29326f1 Update to lerna@2.6.0 2018-01-09 17:35:53 -08:00
Nicolas Gallagher
44d795437e Update to storybook@3.3.6 2018-01-09 17:34:28 -08:00
Nicolas Gallagher
03598d869b Update to babel-plugin-tester@5.0.0 2018-01-09 17:31:52 -08:00
Nicolas Gallagher
a3e44a5c60 Update to enzyme@3.3.0 2018-01-09 17:27:38 -08:00
Nicolas Gallagher
02b124eceb Update yarn.lock 2018-01-08 18:53:05 -08:00
Nicolas Gallagher
91472bc3d6 0.3.0 2018-01-08 18:42:16 -08:00
Nicolas Gallagher
7f45c52ce7 Update to inline-style-prefixer@4.0.0 2018-01-08 18:31:36 -08:00
Nicolas Gallagher
b6ef1d3a36 [fix] handle "monospace" font-family on web
This hack corrects the inheritance and scaling of font size when the
font-family is "monospace".
2018-01-08 12:19:24 -08:00
Maxime Thirouin
fd6ccbcfb3 [fix] ignore more native-only View/Text props
Fix #735
Close #753
2018-01-08 12:10:46 -08:00
Maxime Thirouin
17614e348b Use prettier config instead of CLI args
This allows IDE plugins that rely on prettier config (introduced in
[1.6.0](https://github.com/prettier/prettier/pull/2434)) to detect
prettier and run it automatically with the correct config.

Close #757
2018-01-08 12:06:58 -08:00
Maxime Thirouin
c26ef0eb3b Run precommit hook automatically
Help to ensure that code is formatted and linted before commits and PRs.

Fix #755
Close #756
Close #754
2018-01-08 11:59:26 -08:00
Nicolas Gallagher
b78206d2f4 Remove 'transform-runtime' from webpack example 2018-01-08 11:49:28 -08:00
Nicolas Gallagher
69e0396fb1 Minor README edit 2018-01-05 16:35:39 -08:00
Nicolas Gallagher
6d9154196e [fix] StyleSheet.hairlineWidth guard against missing document.body 2018-01-04 14:30:24 -08:00
Nicolas Gallagher
87fdd6c73b [change] 'react-native-web' module organization and exports
The patch reorganizes the top-level module division of the
'react-native-web' project.

Previously, the package's exported modules were found in:

    apis/*/index.js
    components/*/index.js
    components/*/*.js
    modules/*/index.js
    propTypes/*.js

Now, each part of the exported API is found in:

    exports/*/index.js

And anything not directly part of the exported API is found in:

    modules/*/index.js
    vendor/*/index.js

Close #748
2018-01-04 14:30:24 -08:00
Nicolas Gallagher
209bd3aee1 [fix] babel-plugin support for 'react-native-web' module name
Now rewrites import/export/require statements from 'react-native-web'.
Install the plugin in the 'benchmarks' package.
2018-01-01 12:01:22 -08:00
Nicolas Gallagher
46e77d0b00 Change babel presets
Tune the compiled output to reduce file size.
2017-12-31 15:47:16 -08:00
Nicolas Gallagher
6f10f6be9c [fix] AppRegistry.unmountApplicationComponentAtRootTag
Fixes the bad import of a named export rather than the default export.
2017-12-30 19:06:03 -08:00
Nicolas Gallagher
0d0fdc15ac Minor docs fixes 2017-12-30 18:43:05 -08:00
Nicolas Gallagher
bff3f50ae0 Fix release script 2017-12-30 18:42:35 -08:00
Liron Yahdav
85aaa39206 [fix] i18n of styles when using 'setNativeProps'
Close #732
2017-12-30 14:31:22 -08:00
Nicolas Gallagher
b85a7062be Fix formatting of benchmarks table 2017-12-30 14:11:54 -08:00
Nicolas Gallagher
af47d5f414 [fix] React warning when using hitSlop prop
Make sure the hitSlop element has a 'key'.

Fix #743
2017-12-30 14:06:33 -08:00
Nicolas Gallagher
41d90e0238 [fix] ReactDOM hydration warnings in development
Don't try to hydrate from SSR HTML during development.

Fix #745
2017-12-30 14:02:01 -08:00
Nicolas Gallagher
86263a2fa0 Reorganize and add to benchmarks
Rearrange the benchmark code so that each implementation is
self-contained. Adds the SierpinskiTriangle case that 'emotion'
introduced in their fork of the 'react-native-web' benchmarks. And make
it possible to run benchmarks on a per-library basis.
2017-12-30 13:53:04 -08:00
Nicolas Gallagher
f6d1caab9d Document web-only Switch props 2017-12-26 09:19:04 +00:00
Nicolas Gallagher
1776891736 Improve project introduction and guides
Adopt the structure of the React README and improve the contribution
guidelines to include Facebook's CoC. Fix various links following the
move to a monorepo.
2017-12-26 09:12:15 +00:00
Nicolas Gallagher
f52a851972 Use an .eslintignore file 2017-12-24 12:50:44 +00:00
Nicolas Gallagher
3026465ae3 Monorepo
Introduces a monorepo structure, relies on yarn workspaces to share
dependencies, and lerna for syncing versions across the monorepo.

* Create 2 workspaces:
    'packages' and 'website'
* Create 2 public packages:
    'babel-plugin-react-native-web' and 'react-native-web'
* Create 1 private package:
    'benchmarks'

A simple release script runs the tests, builds the package assets,
increments the package version numbers, git commits and tags, publishes
the package to npm, pushes the changes to github, and releases the
website update.

Close #657
2017-12-24 12:33:41 +00:00
Nicolas Gallagher
14d87f4b30 Fix tests not running in CI
Accidentally stopped running tests after this patch:
9a5b932139
2017-12-23 13:16:53 +00:00
Nicolas Gallagher
5881f07323 Fix typo in benchmark results 2017-12-20 23:23:24 +00:00
Nicolas Gallagher
b545fe47a7 0.2.2 2017-12-20 23:07:21 +00:00
Mo Kouli
4da4dd57c4 [fix] AppContainer initial state
Regression introduced in:
217ad97bfd

Close #738
2017-12-20 23:05:00 +00:00
Nicolas Gallagher
3e12ddfb2b 0.2.1 2017-12-20 17:31:25 +00:00
Johannes
3ecf5d2ed2 [fix] corrupt hydrate import 2017-12-20 17:45:45 +01:00
Nicolas Gallagher
0a5acdb996 Update version in benchmark results table 2017-12-20 15:05:58 +00:00
Nicolas Gallagher
a712a58eba 0.2.0 2017-12-20 15:01:12 +00:00
Nicolas Gallagher
6de892c92b [add] CheckBox component
Implements the CheckBox component and adds a web-only 'color' prop to
allow the color of the checkbox to be customized.
2017-12-20 14:51:44 +00:00
Nicolas Gallagher
495defd69b [fix] StyleSheet.hairlineWidth on retina screens 2017-12-20 11:54:13 +00:00
Nicolas Gallagher
1a20fcfce6 [add] StyleSheet.compose
As per the recent addition to React Native.
2017-12-20 11:27:57 +00:00
Nicolas Gallagher
ed1e45a43d Link to yarnpkg website instead of npmjs 2017-12-20 11:13:17 +00:00
Nicolas Gallagher
556dc8926e [fix] ScrollView animated scrollTo
Rely on web's native smooth scrolling mechanism when implemented in the
browser: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior

Fix #593
2017-12-19 15:52:30 +00:00
Nicolas Gallagher
66cf45b90b Update benchmark libraries and results
* Update all packages.
* Remove 'react-native-stylesheet'; since React 16 it is equivalent in
  performance to using the full 'react-native-web' View implementation.
* Remove 'styled-components/primitives'; it's as slow as
  'styled-components'.
* Record latest benchmark results and hardware.
2017-12-19 15:27:05 +00:00
Nicolas Gallagher
d1e49e06e6 Remove unmaintained starter kit 2017-12-18 17:49:51 +00:00
Nicolas Gallagher
8bf28dbe43 Document more instance methods in Direct Manipulation guide
Fix #704
2017-12-18 17:45:23 +00:00
Nicolas Gallagher
9ae95d0797 Add further details to Getting Started guide
Close #728
2017-12-18 17:33:44 +00:00
Nicolas Gallagher
321051b723 [add] ART export
Improve API compatibility with React Native by exporting 'react-art' as
'ART'.

Fix #602
2017-12-18 17:16:09 +00:00
Nicolas Gallagher
5f68542529 [fix] Touchable use with react-test-renderer
The object returned by 'ReactDOM.findDOMNode' when rendered by
'react-test-renderer' doesn't match the DOM API for an element. Only
attempt to bind the listener if 'addEventListener' is present on the
object.

Fix #720
2017-12-18 16:35:45 +00:00
Nicolas Gallagher
82c044ee33 [fix] use ReactDOM.hydrate in AppRegistry.runApplication
Allows AppRegistry to hydrate server-side rendered apps.

Fix #733
2017-12-18 16:15:47 +00:00
Nicolas Gallagher
9bcc67e73a [fix] top-level API exports
Also fixes importing these APIs from 'react-native' when used with the
Babel plugin.
2017-12-18 16:15:03 +00:00
Nicolas Gallagher
f1ce6c2acb [fix] AppRegistry.getApplication style element keys
Fix #734
2017-12-18 15:26:18 +00:00
Nicolas Gallagher
034108a2a0 [add] SafeAreaView component 2017-12-06 14:34:22 -08:00
Nicolas Gallagher
f96d7b868f [change] update PanResponder implementation
Fix #171
2017-12-06 14:23:35 -08:00
Nicolas Gallagher
0dfe319d41 [change] update the Animated implementation
Replaces the 'animated' package with the latest implementation from
React Native. Requires a few imports to be replaced.

Close #716
Fix #714
Fix #688
2017-12-06 14:01:36 -08:00
Kenneth Kufluk
b7e970f4e6 [add] Picker and Picker.Item components
Close #705
2017-12-04 16:15:23 -08:00
Nicolas Gallagher
02e62ad5d6 Lint fixes 2017-12-02 16:08:56 -08:00
Nicolas Gallagher
541d2458fb [change] Image no longer accepts children
Align with recent changes to the React Native API.
2017-12-02 16:04:27 -08:00
Nicolas Gallagher
b1e860ab40 Add ImageBackground docs 2017-12-02 16:04:27 -08:00
Louis Lagrange
e8eab9b3ec [add] ImageBackground component
Close #696
2017-12-02 16:04:19 -08:00
Nicolas Gallagher
6bc76c3c92 Update yarn script syntax 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
2acd8e477c Update raf and debounce modules 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
ff2b0c9bdc Update to React@16.2 dev dependencies 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
79208720d1 Update webpack tools 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
fca04c4125 Update enzyme 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
5b5b72cc19 Update eslint and prettier 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
217ad97bfd [change] Update Flow and types 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
3e3cfc5325 0.1.16 2017-12-02 14:48:35 -08:00
Nicolas Gallagher
da86ea98fc [fix] NetInfo event listeners and types
* Fix 'addEventListener' handler registration.
* Fix event object provided to handlers.
* Fix event object type - always include 'type' and 'effectiveType'.
* Fix unit test semantics.
* Fix documented NetInfo types.

Close #724
2017-12-02 12:47:12 -08:00
Nicolas Gallagher
5f3e422b5c 0.1.15 2017-12-01 17:55:17 -08:00
Nicolas Gallagher
1f1f89b062 [fix] Image 'onLoad' callback on update
'onLoad' should not be called when a component updates, if the 'uri' is
unchanged.

Fixes a regression introduced by
92952ee746
2017-12-01 17:52:47 -08:00
Nicolas Gallagher
0f79960b85 0.1.14 2017-11-15 15:21:45 -08:00
Nicolas Gallagher
117ce59f27 [fix] TextInput focus/blur management
1. Focusing/blurring a TextInput should update TextInputState.
2. Using the focus/blur instance methods should trigger related events.

Close #715
2017-11-15 15:20:21 -08:00
Louis Lagrange
214121480e [fix] stub for Picker.Item
Add stub function for API compatibility.

Close #690
2017-11-15 14:45:33 -08:00
Li Jie
6261536f57 Fix benchmarks documentation
Close #706
2017-11-15 14:43:13 -08:00
Nicolas Gallagher
a748b7e606 [fix] ScrollView 'setNativeProps'
Fix #709
Close #710
2017-11-15 14:39:58 -08:00
Zero Cho
92952ee746 [fix] call Image 'onLoad' when image is loaded from cache
Fix #452
Close #712
2017-11-15 13:33:13 -08:00
Nicolas Gallagher
c22a9aff7d 0.1.13 2017-10-31 10:56:36 -07:00
Nicolas Gallagher
dd8a3c8d59 [fix] StyleSheet handling of default 'borderWidth'
Problem:

The default border width should be '0px', but setting a border width
value to 'null' would override the default and result in no border width
being applied to the element. This could cause unwanted user agent
styles to be applied.

Solution:

createReactDOMStyle now replaces border width values of 'null' with
'0px'.

Fix #697
2017-10-30 22:42:52 -07:00
Nicolas Gallagher
4a1abee1df Update benchmarks packages 2017-10-30 14:45:13 -07:00
Nicolas Gallagher
92ef3ffbb8 0.1.12 2017-10-20 11:29:45 -07:00
Nicolas Gallagher
899763bc34 [fix] zIndex stacking
React Native simplifies zIndex stacking by making every 'View' a new
stacking context.

Fix #689
2017-10-19 13:16:36 -07:00
Nicolas Gallagher
c69ad3c2d6 Update presentation of Image docs 2017-10-19 12:42:45 -07:00
Nicolas Gallagher
9a5b932139 Flatten styles in test snapshots 2017-10-19 12:42:05 -07:00
Nicolas Gallagher
ba96e457b4 0.1.11 2017-10-18 11:10:59 -07:00
Nicolas Gallagher
bdfe943bd5 0.1.10 2017-10-18 11:08:15 -07:00
Nicolas Gallagher
3870445b7e [add] jest snapshot serializer
Flatten style objects in snapshots
2017-10-17 17:36:34 -07:00
Nicolas Gallagher
5395a3e8bc 0.1.9 2017-10-13 13:25:52 -07:00
Nicolas Gallagher
45b3d8b0df [fix] style warnings for text and SVG
Allow 'fill' as a valid style property.
Allow 'textAlignVertical' to be any string.
2017-10-13 13:17:27 -07:00
Jaco Bovenschen
f1ee3c003a Fix typo in Dimensions doc
Close #682
2017-10-13 10:53:35 -07:00
Nicolas Gallagher
606181406c 0.1.8 2017-10-11 15:14:43 -07:00
Nicolas Gallagher
b537400f38 [fix] remove 'module' from package.json
This is properly supported and there are no ES6 modules in the package at
the moment.
2017-10-11 15:02:58 -07:00
Nicolas Gallagher
0a4fdc155e [fix] babel plugin 'source' check 2017-10-11 15:02:12 -07:00
Nicolas Gallagher
0b8e59974b 0.1.7 2017-10-09 13:14:01 -07:00
Jirat Kijlerdpornpailoj
22eebea633 [fix] babel plugin support for "export" statements
Close #677
2017-10-06 14:22:00 -07:00
Nicolas Gallagher
5dd414f9aa 0.1.6 2017-10-05 19:05:13 -07:00
Louis Lagrange
72c72f6530 [fix] AsyncStorage.flushGetRequests
Add stub function for API compatibility.

Close #676
2017-10-05 19:03:43 -07:00
Nicolas Gallagher
de970f9dbb Add more tests for Switch 2017-10-05 13:59:59 -07:00
Nicolas Gallagher
e8d6c5b4dd Helper function for ActivityIndicator SVGs 2017-10-05 13:59:42 -07:00
Tiaan
e91a5ae13e Add missing comma to example webpack config
Close #671
2017-10-04 11:54:22 -07:00
Nicolas Gallagher
b08bfb9ad5 0.1.5 2017-10-04 11:49:22 -07:00
Nicolas Gallagher
5353011ee4 [fix] BackHandler export; remove BackAndroid
Fix #673
2017-10-04 11:45:28 -07:00
Nicolas Gallagher
5faa3af19a 0.1.4 2017-10-03 10:58:32 -07:00
Jhen
c8461c9c11 [fix] Babel plugin import paths for ViewPropTypes and TextPropTypes
Close #668
2017-10-03 10:53:55 -07:00
Nicolas Gallagher
3112e2ba56 0.1.3 2017-10-02 10:14:18 -07:00
Nicolas Gallagher
fee03e101c [fix] exports from module.js
Fix #666
2017-10-02 10:13:45 -07:00
Nicolas Gallagher
c730a20a26 Update formatter 2017-10-01 17:01:27 -07:00
Nicolas Gallagher
f7ed60ac67 Update test tools 2017-10-01 17:00:04 -07:00
Nicolas Gallagher
417716391a Update build tools 2017-10-01 16:59:00 -07:00
Nicolas Gallagher
ee5e3cb3ca Update eslint packages 2017-10-01 16:54:29 -07:00
Nicolas Gallagher
2298c5d6e3 0.1.2 2017-10-01 16:40:41 -07:00
Jirat Kijlerdpornpailoj
ca9f870ff6 [fix] Image.getSize failure callback
Fix #561
Close #665
2017-10-01 16:33:47 -07:00
Nicolas Gallagher
e9b2fd8bc4 0.1.1 2017-09-29 16:45:48 -07:00
Nicolas Gallagher
09a4985fd2 Remove markdown component and api docs 2017-09-28 14:49:51 -07:00
Nicolas Gallagher
a9e61b4cd5 Docs: allow AppText to inherit font-size 2017-09-28 14:49:51 -07:00
Charlie Croom
6ef19c3ccd [fix] latest NetInfo API
Update the NetInfo API to conform to the latest React Native
implementation. Web-only properties are also included now.

Fix #662
Close #663
2017-09-28 14:49:50 -07:00
Nicolas Gallagher
0d29458874 Remove links to unmaintained starter kits 2017-09-28 13:04:21 -07:00
Amogh Banta
1795bae8b5 Add re-start to starter kits 2017-09-27 09:06:45 -07:00
Nicolas Gallagher
c1152ee09a Update README install instructions 2017-09-26 10:30:48 -07:00
Nicolas Gallagher
182149aac7 0.1.0 2017-09-26 10:21:04 -07:00
Nicolas Gallagher
d6df440987 [change] React 16 support
Fix #364
2017-09-26 10:14:34 -07:00
Nicolas Gallagher
df16c24d68 0.0.130 2017-09-25 14:47:38 -07:00
Nicolas Gallagher
8a2c259235 Update inline-style-prefixer 2017-09-21 14:55:52 -07:00
Nicolas Gallagher
a7f265de11 Update test tools 2017-09-21 14:52:07 -07:00
Nicolas Gallagher
581529540a Update build tools 2017-09-21 14:50:42 -07:00
Nicolas Gallagher
8ad61d9061 Update formatter 2017-09-21 14:49:31 -07:00
Nicolas Gallagher
3d79861970 0.0.129 2017-09-21 14:20:37 -07:00
Nicolas Gallagher
0a84ccb299 [fix] IE10 flexbox support
Add prefixes for flexbox (and other) support in IE10.

Ref #650
2017-09-21 14:18:11 -07:00
Nicolas Gallagher
3aa37450a0 Improve error message for text nodes in View 2017-09-21 14:04:41 -07:00
Nicolas Gallagher
450722153d 0.0.128 2017-09-20 09:45:25 -07:00
Nicolas Gallagher
fbba32defb [fix] cross-browser flex styles
Fix #616
Close #648
2017-09-19 15:44:10 -07:00
Nicolas Gallagher
d762d64b49 Revert SSR Image change
Reverts 3c660e2ad7

See #543
2017-09-19 15:23:09 -07:00
Nicolas Gallagher
a5b1dda62d 0.0.127 2017-09-19 15:12:13 -07:00
Nicolas Gallagher
aad904b550 [fix] remove 'use strict' from warnValidStyle 2017-09-19 15:07:54 -07:00
Unknown
a01e895e30 [change] RefreshControl placeholder
Fix #638
Close #644
2017-09-19 13:44:31 -07:00
Nicolas Gallagher
52e5d41518 [fix] keyboard interaction with Touchable as link
Fix #643
Close #645
2017-09-19 13:40:57 -07:00
Nicolas Gallagher
77a40b6237 [fix] Easing import when using babel plugin
Fix #649
2017-09-19 13:37:12 -07:00
Nicolas Gallagher
4bbe1a40aa 0.0.126 2017-09-18 21:08:39 -07:00
Nicolas Gallagher
6942e4e417 Shallow render ActivityIndicator tests
These snapshots should be improved by flattening the React Native style
object.
2017-09-18 19:47:03 -07:00
Nicolas Gallagher
63daff7f80 Simplify Switch tests 2017-09-18 19:32:02 -07:00
Nicolas Gallagher
662b7c3d6e Simplify Image snapshot testing 2017-09-18 19:26:48 -07:00
Nicolas Gallagher
12fb588596 Simplify View snapshot testing 2017-09-18 19:26:11 -07:00
Nicolas Gallagher
5cb09b1a9e [fix] disallow text node children of View
Catch text nodes that are not the only child of View. Fixes a case
missing from 0ad6ab948b.
2017-09-18 13:28:51 -07:00
Nicolas Gallagher
c03cfdf8bd Add compile task to build task 2017-09-17 11:25:50 -07:00
Nicolas Gallagher
997b598de8 [fix] event normalization
Work better with simulated events, and avoid crashing if
'nativeEvent.target' isn't an element node.

Close #597
2017-09-15 15:16:40 -07:00
Nicolas Gallagher
6fe796f9da [add] accessibilityRole 'label' to Text 2017-09-15 11:29:44 -07:00
Nicolas Gallagher
0ad6ab948b [fix] disallow text node children of View
Fix #627
2017-09-15 11:29:41 -07:00
Nicolas Gallagher
32a23136af 0.0.125 2017-09-14 15:16:52 -07:00
Nicolas Gallagher
96f48226cb [fix] ColorPropType validation
Check color is of type string before using `indexOf`.

Fix #630
2017-09-14 13:19:13 -07:00
Peter Schussheim
f1ef0f21af Fix typo in NetInfo docs 2017-09-14 11:31:18 -07:00
Unknown
d42e8907ca [fix] return value of Keyboard.addListener
Close #631
Fix #632
2017-09-14 11:26:46 -07:00
Nicolas Gallagher
90724b2cef [fix] filter unsupported ScrollView props
Fix #633
2017-09-14 11:20:18 -07:00
Nicolas Gallagher
dd0f1de3d1 [fix] avoid prop types check on UnimplementedView
Fix #635
2017-09-14 11:08:36 -07:00
Nicolas Gallagher
fc751ed715 [fix] remove TouchalbeWithoutFeedback children prop type
Fix #634
2017-09-14 10:49:49 -07:00
Nicolas Gallagher
917b06a690 Update README 2017-09-13 11:27:36 -07:00
Nicolas Gallagher
80cb7baf82 0.0.124 2017-09-12 15:58:28 -07:00
Nicolas Gallagher
f08515b1f1 Avoid compiling tests 2017-09-12 15:58:14 -07:00
Nicolas Gallagher
8591bf7ce5 [fix] add AppState to Babel plugin list 2017-09-12 15:46:27 -07:00
Nicolas Gallagher
b7c8f00fcc 0.0.123 2017-09-12 11:31:49 -07:00
Nicolas Gallagher
ed81b985a9 Include the babel dir in published package 2017-09-12 11:31:38 -07:00
Nicolas Gallagher
441dc8efff 0.0.122 2017-09-12 10:54:01 -07:00
Paul Armstrong
7e8ef5b72c Add babel plugin to rewrite import paths
Replace 'react-native' imports with direct imports to the relevant
module within 'react-native-web'. Follow up task is to also rewrite
'react-native-web' imports from the entry file to become direct imports
(reduces bundle size).

Close #608
2017-09-12 10:45:42 -07:00
Nicolas Gallagher
641c8c47e0 [change] Button text style to match Android 2017-09-11 15:52:44 -07:00
Nicolas Gallagher
87ead7f64e Fix doc styles 2017-09-11 15:52:41 -07:00
Nicolas Gallagher
5ed2127175 [fix] default Text styles
Match React Native's default font-size for Text, and use the system font
stack by default. Nested Text elements will inherit font styles from
their parent Text.
2017-09-11 15:47:49 -07:00
Nicolas Gallagher
e802026298 Benchmarks: add emotion and latest results 2017-09-10 12:52:07 -07:00
Nicolas Gallagher
5d24f4c8fa Update jest 2017-09-10 10:36:39 -07:00
Nicolas Gallagher
a9f1afc07c Update formatter and linter tools 2017-09-10 10:35:07 -07:00
Nicolas Gallagher
d185d81560 Update build tools 2017-09-10 10:32:11 -07:00
Nicolas Gallagher
66fa09da8e Move modality initialization to StyleSheet 2017-09-10 10:28:22 -07:00
Nicolas Gallagher
bae4dd806a [change] rename createDOMElement to createElement
Allow 'createElement' to be used as a drop-in replacement for
'ReactDOM.createElement'.
2017-09-10 10:28:21 -07:00
Unknown
4a680fd9b6 [add] basic support for KeyboardAvoidingView
Close #624
2017-09-08 10:57:44 -07:00
Nicolas Gallagher
e34e4e38d3 0.0.121 2017-09-07 13:31:47 -07:00
Unknown
b23a4f55dc [add] support for Keyboard API
Close #625
2017-09-07 13:07:33 -07:00
Nicolas Gallagher
26bdf44a4c [fix] TextInput keyPress events when child of Touchable
Fix #612
2017-09-07 11:40:00 -07:00
Nicolas Gallagher
5e98107617 [fix] support "System" as a font-family on web
Fix #622
2017-09-02 12:26:36 -07:00
Nicolas Gallagher
86ea0e5eff Fix source code URLs in docs 2017-08-31 17:42:03 -07:00
Nicolas Gallagher
6e02c5690c [add] support for AssetRegistry
Applies changes from @fmoo to help with Metro packager support. Also
fixed a Flow exposed by this change.
2017-08-31 12:32:24 -07:00
Nicolas Gallagher
c50d808a2b [change] remove 'core' module export 2017-08-31 12:06:48 -07:00
Nicolas Gallagher
130b10c3f7 0.0.120 2017-08-16 16:02:33 -07:00
Nicolas Gallagher
03ddf074ea Update fbjs and inline-style-prefixer 2017-08-16 14:09:31 -07:00
Nicolas Gallagher
f201a0347d Update prettier 2017-08-16 14:07:25 -07:00
Nicolas Gallagher
fffbdff6ca Update eslint packages 2017-08-16 14:04:32 -07:00
Nicolas Gallagher
23e5c8479c Update webpack packages 2017-08-16 14:03:38 -07:00
Nicolas Gallagher
17e5e374ee Update babel packages 2017-08-16 14:01:58 -07:00
Nicolas Gallagher
ef907dce22 [add] StyleSheet validation allows table styles
Fix #605
2017-08-16 11:27:47 -07:00
Nicolas Gallagher
586f134f76 [fix] Text's CSS box-sizing default
Fix #606
2017-08-16 11:01:26 -07:00
Nicolas Gallagher
70e2a75b43 [add] don't warn about use of position:sticky 2017-08-10 17:00:21 -07:00
Nicolas Gallagher
8756c20ade Minor changes and fixes to docs 2017-08-09 09:44:36 -07:00
Nicolas Gallagher
4081d17f25 0.0.119 2017-08-01 12:08:44 -07:00
Sam
4fa6f77d25 [fix] TouchableMixin async race condition
When a Touchable component returns 0 for
`touchableGetHighlightDelayMS()` the mixin may occasionally try to
detect if the press event occurred on the hit area before the hit area
has been determined. Clearing out this value ensures that
`positionOnActivate` is recalculated before that takes place.

Close #586
2017-08-01 11:58:46 -07:00
Andrew Kennedy
17d723559d Add tests for 'Button' component
Close #583
2017-08-01 11:55:37 -07:00
Nicolas Gallagher
8d80885f5d [fix] Dimensions.get when called on the server 2017-08-01 11:55:23 -07:00
Nicolas Gallagher
3ca4becc41 0.0.118 2017-07-31 16:32:28 -07:00
Nicolas Gallagher
cc077e9019 [fix] allow Dimensions.set to be called on the server
Fix #566
2017-07-31 16:31:42 -07:00
Nicolas Gallagher
1225b00cdb Fix publishing of documentation 2017-07-30 20:25:26 -07:00
Nicolas Gallagher
ed70617e91 0.0.117 2017-07-30 20:13:29 -07:00
Nicolas Gallagher
134114de83 [fix] TextInput: call on onKeyPress for Tab and Cmd+Enter
Add support for more key combinations that only fire 'keydown' React DOM
events. And allow users to call 'preventDefault', etc., on the event.

Fix #567
Close #582
2017-07-30 20:11:19 -07:00
Nicolas Gallagher
08ee7c83bb Minor starting documentation improvement 2017-07-30 18:45:08 -07:00
Nicolas Gallagher
5fad78dcad [fix] remove unsupported TextInput props
For compatibility with React Native, do not pass on unsupported
TextInput props to avoid ReactDOM warnings.

Close #571
2017-07-29 17:30:31 -07:00
Nicolas Gallagher
e04343e48e Benchmarks: use glamor@2 and fix yarn.lock paths 2017-07-28 19:08:49 -07:00
Nicolas Gallagher
5e3a946f8b Upgrade docs to use @storybook/react 2017-07-28 15:29:39 -07:00
Nicolas Gallagher
4e3d8dbb02 [change] support CSS custom properties
Update 'setValueForStyles' and style validation to support defining and
using custom properties.

Fix #516
2017-07-28 15:29:07 -07:00
Nicolas Gallagher
fee909d26a [fix] AppState on the server
Fix #578
2017-07-27 16:30:28 -07:00
Nicolas Gallagher
9e58a7b5f1 Update GitHub files 2017-07-27 11:38:50 -07:00
Nicolas Gallagher
20e1febe21 0.0.116 2017-07-26 19:58:34 -07:00
Nicolas Gallagher
ef209ca281 Fix caniuse-api install, again 2017-07-26 19:55:43 -07:00
Nicolas Gallagher
a35949fa71 0.0.115 2017-07-26 19:25:31 -07:00
Nicolas Gallagher
f5ac856c2d Revert unrelated changes to normalizeNativeEvent 2017-07-26 19:25:18 -07:00
Nicolas Gallagher
4b557b1e0b Fix canuse-api installation 2017-07-26 19:20:34 -07:00
Nicolas Gallagher
d4e9d9d256 0.0.114 2017-07-26 16:17:28 -07:00
Nicolas Gallagher
0ff3e91592 [fix] AppState support for Android browser 4.4 2017-07-26 15:52:11 -07:00
Nicolas Gallagher
092d5d12f7 [fix] unitless values for vendor prefixed properties
Problem:

Numeric values are suffixed with 'px', unless the property supports
unitless values. However, vendor prefixed properties were ignored
resulting in invalid CSS values for properties like
'-webkit-flex-shrink'.

Solution:

Apply the upstream solution from React, which includes vendor prefixed
properties in the "unitless number" map. Also build a custom vendor
prefixer to ensure adequate browser support (i.e., Safari 7 and older
Chrome).
2017-07-26 15:51:46 -07:00
Nicolas Gallagher
507e0d41f5 Move event modules into directories 2017-07-26 15:43:10 -07:00
Nicolas Gallagher
9e863d5402 Install docs dependencies separately 2017-07-22 10:47:40 -07:00
Nicolas Gallagher
1364b1dfdf Rename benchmark script 2017-07-22 10:45:04 -07:00
Nicolas Gallagher
7f81e313ed Add link to Glitch playground 2017-07-22 10:43:25 -07:00
Nicolas Gallagher
a1892ec8b8 Update benchmark subjects and results 2017-07-15 11:04:38 -07:00
Nicolas Gallagher
e6232d5980 0.0.113 2017-07-14 10:02:38 -07:00
Nicolas Gallagher
e93a2eb478 Minor edits to View docs 2017-07-13 17:26:32 -07:00
Nicolas Gallagher
44ecf1fe87 [fix] Touchable to avoid touch delay
According to MDN, "touch-action:manipulation" enables "…panning and
pinch zoom gestures, but disables additional non-standard gestures such
as double-tap to zoom. Disabling double-tap to zoom removes the need for
browsers to delay the generation of click events when the user taps the
screen."
2017-07-13 17:13:01 -07:00
Nicolas Gallagher
faec2b4a83 [fix] ScrollView to use 'touch-action' to disable scroll
The recommendation from the Chrome team is to use 'touch-action' to
disable scrolling (via touch modality) when passive event listeners are
in use.

Close #563
2017-07-13 17:10:38 -07:00
Nicolas Gallagher
3677f0dd57 Use more components to build docs 2017-07-11 20:18:52 -07:00
Nicolas Gallagher
36d161a959 0.0.112 2017-07-11 15:39:20 -07:00
Nicolas Gallagher
1ca18ab056 [fix] TouchableWithoutFeedback keyboard support 2017-07-11 09:56:18 -07:00
Nicolas Gallagher
b43717e797 [fix] ensure 'heading' role styles are reset 2017-07-11 09:53:17 -07:00
Nicolas Gallagher
801d5f8c68 0.0.111 2017-07-10 13:14:47 -07:00
Nicolas Gallagher
30f2ec9bf5 [fix] improve Image accessibility 2017-07-10 13:13:11 -07:00
Nicolas Gallagher
6f3e29f630 [change] better Touchable support for keyboards
Problem:

Although 'Touchable' supports basic keyboard usage, it doesn't support
delays or interaction via the Space key.

Solution:

Extend the 'Touchable' mixin to better support keyboard interactions.
All touchable callbacks and delays are now supported when interacted
with via a keyboard's Enter and Space keys (as would be expected of
native 'button' elements). However, events are not normalized to mimic
touch events.

Minor upstream changes to the Touchables in React Native are also
included.
2017-07-10 12:57:16 -07:00
Nicolas Gallagher
2607cb25ab Clean up benchmark exports and PropTypes import 2017-07-09 18:43:46 -07:00
615 changed files with 25573 additions and 12095 deletions

5
.babelrc Normal file
View File

@@ -0,0 +1,5 @@
{
"presets": [
"./scripts/babel/preset"
]
}

4
.eslintignore Normal file
View File

@@ -0,0 +1,4 @@
coverage
dist
node_modules
packages/**/vendor/*

View File

@@ -1,14 +1,16 @@
[version]
^0.63.0
[ignore]
.*/__tests__/.*
.*/benchmarks/.*
.*/docs/.*
.*/node_modules/animated/*
<PROJECT_ROOT>/.*/__tests__/.*
<PROJECT_ROOT>/packages/.*/dist/.*
<PROJECT_ROOT>/website/.*
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
[include]
[libs]
types
<PROJECT_ROOT>/types
[options]
unsafe.enable_getters_and_setters=true

View File

@@ -17,7 +17,7 @@ Fork, then clone the repo:
git clone https://github.com/your-username/react-native-web.git
```
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install):
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install)):
```
yarn
@@ -25,6 +25,12 @@ yarn
## Automated tests
To run the linter:
```
yarn lint
```
To run flow:
```
@@ -40,62 +46,57 @@ yarn jest
…in watch mode:
```
yarn jest:watch
yarn jest --watch
```
To run all automated tests:
To run all these automated tests:
```
yarn test
```
## Visual tests
To run the interactive storybook:
```
yarn docs:start
```
To generate a static build of the storybook:
```
yarn docs:build
```
To run the performance benchmarks in a browser (opening `./benchmarks/index.html`):
```
yarn benchmarks
```
## Compile and build
To compile the source code to `dist`:
To compile the `react-native-web` source code:
```
yarn compile
```
To create a UMD bundle of the library:
…in watch mode:
```
yarn build
yarn compile --watch
```
### Pre-commit
## Website and visual tests
To format and lint code before commit:
To run the interactive storybook:
```
yarn precommit
yarn website
```
To format and lint the entire project:
When you're also making changes to the 'react-native-web' source files, run this command in another process:
```
yarn fmt
yarn lint
yarn compile --watch
```
## Benchmarks
To run the benchmarks locally:
```
yarn benchmarks
open ./packages/benchmarks/dist/index.html
```
To develop against these benchmarks:
```
yarn compile --watch
yarn benchmarks --watch
```
### New Features
@@ -104,13 +105,16 @@ Please open an issue with a proposal for a new feature or refactoring before
starting on the work. We don't want you to waste your efforts on a pull request
that we won't want to accept.
## Submitting Changes
## Pull requests
* Open a new issue in the [Issue tracker](https://github.com/necolas/react-native-web/issues).
* Fork the repo.
* Create a new feature branch based off the `master` branch.
* Make sure all tests pass and there are no linting errors.
* Submit a pull request, referencing any issues it addresses.
**Before submitting a pull request**, please make sure the following is done:
1. Fork the repository and create your branch from `master`.
2. If you've added code that should be tested, add tests!
3. If you've changed APIs, update the documentation.
4. Ensure the tests pass (`yarn test`).
You can now submit a pull request, referencing any issues it addresses.
Please try to keep your pull request focused in scope and avoid including
unrelated commits.
@@ -119,3 +123,18 @@ After you have submitted your pull request, we'll try to get back to you as
soon as possible. We may suggest some changes or improvements.
Thank you for contributing!
## Releases
To commit, publish, and push a final version:
```
yarn release <version>
```
Release candidates or versions that you'd like to publish to npm, but do not
want to produce a commit and push it to GitHub:
```
yarn release <version> --skip-git
```

View File

@@ -1,30 +1,20 @@
<!--
React Native for Web is an implementation of React Native. If you have feature
requests, you should post them to https://productpains.com/product/react-native/.
GitHub issues should only be used for bugs or Web-specific features you believe
React Native requires.
Make sure to add ALL the information needed to understand the bug so that
someone can help. If the info is missing we'll add the 'needs more information'
label and close the issue until there is enough information.
-->
**Do you want to request a *feature* or report a *bug*?**
**What is the current behavior?**
Link to minimal test case: (template: [codepen](https://codepen.io/necolas/pen/PZzwBR?editors=0010))
**What is the expected behaviour?**
**Steps to reproduce**
**If the current behavior is a bug, please provide the steps to reproduce and
if a minimal demo of the problem via Glitch or similar (template:
https://glitch.com/edit/#!/react-native-web-playground).**
1.
2.
**Environment (include versions)**
**What is the expected behavior?**
OS:
Device:
Browser:
React Native for Web (version):
React (version):
**Environment (include versions). Did this work in previous versions?**
* OS:
* Device:
* Browser:
* React Native for Web (version):
* React (version):

View File

@@ -1,18 +1 @@
<!--
Thanks for submitting a pull request! Make sure the PR does only one thing.
Please provide enough information so that others can review your pull
request. Make sure you have read the Contributing Guidelines -
https://github.com/necolas/react-native-web/CONTRIBUTING.md
-->
**This patch solves the following problem**
**Test plan**
**This pull request**
- [ ] includes documentation
- [ ] includes tests
- [ ] includes benchmark reports
- [ ] includes an interactive example
- [ ] includes screenshots/videos
**Before submitting a pull request,** please make sure you have followed the steps the CONTRIBUTING guide.

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
coverage
node_modules
dist

View File

@@ -1,9 +1,17 @@
language: node_js
node_js:
- "6"
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- "8"
before_install:
# Install Yarn
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH=$HOME/.yarn/bin:$PATH
cache:
yarn: true
directories:
- node_modules
script:
- npm run lint
- npm test
- yarn test

198
README.md
View File

@@ -1,103 +1,139 @@
# React Native for Web
[![Build Status][travis-image]][travis-url]
[![npm version][npm-image]][npm-url]
[![npm version][package-badge]][package-url] [![Build Status][ci-badge]][ci-url] [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
[React Native][react-native-url] components and APIs for the Web.
"React Native for Web" brings the platform-agnostic Components and APIs of
[React Native][react-native-url] to the Web.
* **High-quality user interfaces**: React Native for Web makes it easy to
create [fast](https://github.com/necolas/react-native-web/blob/master/packages/benchmarks/README.md),
adaptive web UIs in JavaScript. It provides native-like interactions, support
for multiple input modes (touch, mouse, keyboard), optimized vendor-prefixed
styles, built-in support for RTL layout, built-in accessibility, and integrates
with React Dev Tools.
* **Write once, render anywhere**: React Native for Web interoperates with
existing React DOM components and is compatible with the majority of the
React Native API. You can develop new components for native and web without
rewriting existing code. React Native for Web can also render to HTML and
critical CSS on the server using Node.js.
Who is using React Native for Web? [Twitter](https://mobile.twitter.com),
[Major League Soccer](https://matchcenter.mlssoccer.com), [The
Times](https://github.com/newsuk/times-components), [React Native's
documentation](http://facebook.github.io/react-native/).
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
[npm-image]: https://badge.fury.io/js/react-native-web.svg
[npm-url]: https://npmjs.org/package/react-native-web
[react-native-url]: https://facebook.github.io/react-native/
[travis-image]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[travis-url]: https://travis-ci.org/necolas/react-native-web
## Overview
"React Native for Web" is a project to bring React Native's building blocks and
touch handling to the Web.
Browse the [UI Explorer](https://necolas.github.io/react-native-web/storybook/)
to see React Native examples running on Web. Or try it out online with [React
Native for Web: Playground](https://www.webpackbin.com/bins/-KgucwxRbn7HRU-V-3Bc).
## Quick start
To install in your app:
The easiest way to get started with React Native for Web is to use this
[ready-to-go project on Glitch](https://glitch.com/edit/#!/react-native-web-playground).
You dont need to install anything to try it out.
```
npm install --save react@15.6 react-dom@15.6 react-native-web
```
NOTE: React Native for Web supports React/ReactDOM 15.4, 15.5, or 15.6.
Then read the [Getting Started](docs/guides/getting-started.md) guide.
If you are unfamiliar with setting up a React web project, please follow the
recommendations in the [React documentation](https://reactjs.org/).
## Documentation
The [UI Explorer](https://necolas.github.io/react-native-web/storybook/)
interactively documents all the APIs and Components.
You can find the React Native for Web API documentation [on the
website][website-url].
Guides:
Please refer to the [React Native documentation][react-native-url] for more
design details, and for information about the [Gesture Responder
system](https://facebook.github.io/react-native/docs/gesture-responder-system.html)
and [animations](https://facebook.github.io/react-native/docs/animations.html).
* [Getting started](docs/guides/getting-started.md)
* [Style](docs/guides/style.md)
* [Accessibility](docs/guides/accessibility.md)
* [Direct manipulation](docs/guides/direct-manipulation.md)
* [Internationalization](docs/guides/internationalization.md)
* [Advanced use](docs/guides/advanced.md)
* [Known issues](docs/guides/known-issues.md)
### Installation
## Example code
Install using `yarn` or `npm`:
```js
import React from 'react'
import { AppRegistry, Image, StyleSheet, Text, View } from 'react-native'
// Components
const Card = ({ children }) => <View style={styles.card}>{children}</View>
const Title = ({ children }) => <Text style={styles.title}>{children}</Text>
const Photo = ({ uri }) => <Image source={{ uri }} style={styles.image} />
const App = () => (
<Card>
<Title>App Card</Title>
<Photo uri="/some-photo.jpg" />
</Card>
)
// Styles
const styles = StyleSheet.create({
card: {
flexGrow: 1,
justifyContent: 'center'
},
title: {
fontSize: '1.25rem',
fontWeight: 'bold'
},
image: {
height: 40,
marginVertical: 10,
width: 40
}
})
// App registration and rendering
AppRegistry.registerComponent('MyApp', () => App)
AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-root') })
```
yarn add react react-dom react-native-web
yarn add --dev babel-plugin-react-native-web
```
## Related projects
### Guides
* [react-primitives](https://github.com/lelandrichardson/react-primitives/)
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
* [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack)
* [react-sketchapp](https://github.com/airbnb/react-sketchapp)
* [react-web](https://github.com/taobaofed/react-web)
* [reactxp](https://github.com/microsoft/reactxp)
* [Getting started](https://github.com/necolas/react-native-web/blob/master/website/guides/getting-started.md)
* [Style](https://github.com/necolas/react-native-web/blob/master/website/guides/style.md)
* [Accessibility](https://github.com/necolas/react-native-web/blob/master/website/guides/accessibility.md)
* [Internationalization](https://github.com/necolas/react-native-web/blob/master/website/guides/internationalization.md)
* [Direct manipulation](https://github.com/necolas/react-native-web/blob/master/website/guides/direct-manipulation.md)
* [Advanced use](https://github.com/necolas/react-native-web/blob/master/website/guides/advanced.md)
## Examples
There are several examples [on the website][website-url] and in the [website's
source code](https://github.com/necolas/react-native-web/blob/master/website).
Here is an example to get you started:
```js
import React from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';
class App extends React.Component {
render() {
return (
<View style={styles.box}>
<Text style={styles.text}>Hello, world!</Text>
</View>
);
}
}
const styles = StyleSheet.create({
box: { padding: 10 },
text: { fontWeight: 'bold' }
});
AppRegistry.registerComponent('App', () => App);
AppRegistry.runApplication('App', { rootTag: document.getElementById('react-root') });
```
This example will render the `App` into a container on the page.
You'll notice that there is no reference to `react-dom`; the `App` component is
defined using the platform-agnostic APIs and Components introduced by React
Native. This allows the app to be rendered to web and native platforms.
## Contributing
The main purpose of this repository is to help evolve React web and native
development towards the platform-agnostic design of React Native, and in the
process make it faster and easier to build high-quality experiences for the web
with React. Development happens in the open on GitHub, and we are grateful for
contributing bugfixes and improvements. Read below to learn how you can take
part in improving React Native for Web.
### Code of conduct
Facebook has adopted a [Code of Conduct][code-of-conduct] that this project
expects all participants to adhere to. Please read the full text so that you
can understand what actions will and will not be tolerated.
### Contributing guide
Read the [contributing guide][contributing-url] to learn about the
development process, how to propose bugfixes and improvements, and how to build
and test your changes to React Native for Web.
### Good first issues
To help you get you familiar with the contribution process, there is a list of
[good first issues][good-first-issue-url] that contain bugs which have a
relatively limited scope. This is a great place to get started.
## License
React Native for Web is [BSD licensed](LICENSE).
React Native for Web is [BSD licensed](./LICENSE).
[package-badge]: https://img.shields.io/npm/v/react-native-web.svg?style=flat
[package-url]: https://yarnpkg.com/en/package/react-native-web
[ci-badge]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[ci-url]: https://travis-ci.org/necolas/react-native-web
[website-url]: https://necolas.github.io/react-native-web/storybook/
[react-native-url]: https://facebook.github.io/react-native/
[contributing-url]: https://github.com/necolas/react-native-web/blob/master/.github/CONTRIBUTING.md
[good-first-issue-url]: https://github.com/necolas/react-native-web/labels/good%20first%20issue
[code-of-conduct]: https://code.facebook.com/codeofconduct

View File

@@ -1,47 +0,0 @@
# Performance
To run these benchmarks:
```
npm run build:performance
open ./performance/index.html
```
## Notes
The components used in the render benchmarks are simple enough to be
implemented by multiple UI or style libraries. The implementations are not
equivalent in functionality. For example, React Native for Web's stylesheet is
unique in that it also converts React Native styles to DOM styles, has
deterministic resolution, and supports RTL layout.
`react-native-web/stylesheet` is a comparative baseline that implements a
simple `View` without much of React Native's functionality.
## Benchmark results
Typical render timings*: mean ± two standard deviations.
| Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `94.96` `±31.01` | `200.43` `±38.90` | |
| `react-native-web/stylesheet@0.0.107` | `98.58` `±10.83` | `218.59` `±36.52` | |
| `react-native-web@0.0.107` | `117.45` `±18.76` | `288.27` `±33.50` | `15.10` `±5.45ms` |
Other libraries
| Implementation | Deep tree (ms) | Wide tree (ms) |
| :--- | ---: | ---: |
| `styletron@2.5.1` | `90.38` `±15.15` | `197.40` `±29.02` |
| `aphrodite@1.2.0` | `88.65` `±19.62` | `187.35` `±24.60` |
| `glamor@3.0.0-1` | `145.64` `±21.93` | `283.60` `±23.26` |
| `react-jss@5.4.1` | `143.17` `±19.14` | `361.80` `±33.39` |
| `reactxp@0.34.3` | `227.18` `±28.75` | `496.08` `±59.96` |
| `styled-components@2.1.0` | `262.85` `±46.12` | `578.43` `±35.86` |
| `styled-components/primitives@2.1.0` | `261.43` `±44.14` | `569.65` `±22.19` |
These results indicate that render performance is not a significant
differentiating factor between `aphrodite`, `styletron`, and
`react-native-web/stylesheet`.
*MacBook Pro (13-inch, Early 2015); 3.1 GHz Intel Core i7; 16 GB 1867 MHz DDR3. Google Chrome 58 (2x CPU slowdown).

View File

@@ -1,97 +0,0 @@
import * as marky from 'marky';
const fmt = time => `${Math.round(time * 100) / 100}ms`;
const measure = (name, fn) => {
marky.mark(name);
fn();
const performanceMeasure = marky.stop(name);
return performanceMeasure.duration;
};
const mean = values => {
const sum = values.reduce((sum, value) => sum + value, 0);
return sum / values.length;
};
const median = values => {
if (!Array.isArray(values)) {
return 0;
}
if (values.length === 1) {
return values[0];
}
const numbers = [...values].sort((a, b) => a - b);
return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2;
};
const standardDeviation = values => {
const avg = mean(values);
const squareDiffs = values.map(value => {
const diff = value - avg;
return diff * diff;
});
const meanSquareDiff = mean(squareDiffs);
return Math.sqrt(meanSquareDiff);
};
const benchmark = ({ name, description, setup, teardown, task, runs }) => {
return new Promise(resolve => {
const durations = [];
let i = 0;
setup();
const first = measure('first', task);
teardown();
const done = () => {
const stdDev = standardDeviation(durations);
const formattedFirst = fmt(first);
const formattedMean = fmt(mean(durations));
const formattedMedian = fmt(median(durations));
const formattedStdDev = fmt(stdDev);
console.groupCollapsed(`${name}\n${formattedMean} ±${fmt(2 * stdDev)}`);
description && console.log(description);
console.log(`First: ${formattedFirst}`);
console.log(`Median: ${formattedMedian}`);
console.log(`Mean: ${formattedMean}`);
console.log(`Standard deviation: ${formattedStdDev}`);
console.log(durations);
console.groupEnd();
resolve();
};
const a = () => {
setup();
window.requestAnimationFrame(b);
};
const b = () => {
const duration = measure('mean', task);
durations.push(duration);
window.requestAnimationFrame(c);
};
const c = () => {
teardown();
window.requestAnimationFrame(d);
};
const d = () => {
i += 1;
if (i < runs) {
window.requestAnimationFrame(a);
} else {
window.requestAnimationFrame(done);
}
};
window.requestAnimationFrame(a);
});
};
export default benchmark;

View File

@@ -1,20 +0,0 @@
import benchmark from './benchmark';
import ReactDOM from 'react-dom';
const node = document.querySelector('.root');
const createRenderBenchmark = ({ description, getElement, name, runs }) => () => {
const setup = () => {};
const teardown = () => ReactDOM.unmountComponentAtNode(node);
return benchmark({
name,
description,
runs,
setup,
teardown,
task: () => ReactDOM.render(getElement(), node)
});
};
export default createRenderBenchmark;

View File

@@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Performance tests</title>
</head>
<body>
<div class="root"></div>
<script src="dist/performance.bundle.js"></script>
</body>
</html>

View File

@@ -1,52 +0,0 @@
import aphrodite from './src/aphrodite';
import cssModules from './src/css-modules';
import glamor from './src/glamor';
import jss from './src/jss';
import reactNative from './src/react-native';
import reactNativeStyleSheet from './src/react-native-stylesheet';
import styledComponents from './src/styled-components';
import styledComponentsPrimitives from './src/styled-components-primitives';
import styletron from './src/styletron';
import xp from './src/reactxp';
import renderDeepTree from './tests/renderDeepTree';
import renderTweet from './tests/renderTweet';
import renderWideTree from './tests/renderWideTree';
const testAll = window.location.search === '?all';
const coreTests = [
() => renderTweet('react-native-web', reactNative),
() => renderDeepTree('css-modules', cssModules),
() => renderWideTree('css-modules', cssModules),
() => renderDeepTree('react-native-web/stylesheet', reactNativeStyleSheet),
() => renderWideTree('react-native-web/stylesheet', reactNativeStyleSheet),
() => renderDeepTree('react-native-web', reactNative),
() => renderWideTree('react-native-web', reactNative)
];
/**
* Optionally run tests using other libraries
*/
const extraTests = [
() => renderDeepTree('styletron', styletron),
() => renderWideTree('styletron', styletron),
() => renderDeepTree('aphrodite', aphrodite),
() => renderWideTree('aphrodite', aphrodite),
() => renderDeepTree('glamor', glamor),
() => renderWideTree('glamor', glamor),
() => renderDeepTree('react-jss', jss),
() => renderWideTree('react-jss', jss),
() => renderDeepTree('reactxp', xp),
() => renderWideTree('reactxp', xp),
() => renderDeepTree('styled-components', styledComponents),
() => renderWideTree('styled-components', styledComponents),
() => renderDeepTree('styled-components/primitives', styledComponentsPrimitives),
() => renderWideTree('styled-components/primitives', styledComponentsPrimitives)
];
const tests = testAll ? coreTests.concat(extraTests) : coreTests;
// run benchmarks
tests.reduce((promise, test) => promise.then(test()), Promise.resolve());

View File

@@ -1,21 +0,0 @@
{
"name": "performance",
"private": true,
"dependencies": {
"aphrodite": "^1.2.1",
"classnames": "^2.2.5",
"glamor": "^3.0.0-2",
"marky": "^1.2.0",
"react-jss": "^7.0.0-pre.1",
"react-primitives": "^0.4.3",
"reactxp": "0.34.3",
"styled-components": "^2.1.1",
"styletron-client": "^2.5.7",
"styletron-utils": "^2.5.4"
},
"devDependencies": {
"css-loader": "^0.28.4",
"react-addons-perf": "^15.4.2",
"style-loader": "^0.18.2"
}
}

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/aphrodite';
import View from './components/View/aphrodite';
export default {
Box,
View
};

View File

@@ -1,30 +0,0 @@
import styled from 'styled-components/primitives';
const getColor = color => {
switch (color) {
case 0:
return '#222';
case 1:
return '#666';
case 2:
return '#999';
case 3:
return 'blue';
case 4:
return 'orange';
case 5:
return 'red';
default:
return 'transparent';
}
};
const Box = styled.View`
flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')};
padding: ${props => (props.outer ? '4px' : '0')};
height: ${props => (props.fixed ? '20px' : 'auto')};
width: ${props => (props.fixed ? '20px' : 'auto')};
background-color: ${props => getColor(props.color)};
`;
module.exports = Box;

View File

@@ -1,36 +0,0 @@
.outer {
padding: 4px;
}
.row {
flex-direction: row;
}
.color0 {
background-color: #222;
}
.color1 {
background-color: #666;
}
.color2 {
background-color: #999;
}
.color3 {
background-color: blue;
}
.color4 {
background-color: orange;
}
.color5 {
background-color: red;
}
.fixed {
width: 20px;
height: 20px;
}

View File

@@ -1,48 +0,0 @@
/* eslint-disable react/prop-types */
import { injectStylePrefixed } from 'styletron-utils';
import React from 'react';
import View, { styletron } from '../View/styletron';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
<View
{...other}
style={[
styles[`color${color}`],
fixed && styles.fixed,
layout === 'row' && styles.row,
outer && styles.outer
]}
/>;
const styles = {
outer: injectStylePrefixed(styletron, {
padding: '4px'
}),
row: injectStylePrefixed(styletron, {
flexDirection: 'row'
}),
color0: injectStylePrefixed(styletron, {
backgroundColor: '#222'
}),
color1: injectStylePrefixed(styletron, {
backgroundColor: '#666'
}),
color2: injectStylePrefixed(styletron, {
backgroundColor: '#999'
}),
color3: injectStylePrefixed(styletron, {
backgroundColor: 'blue'
}),
color4: injectStylePrefixed(styletron, {
backgroundColor: 'orange'
}),
color5: injectStylePrefixed(styletron, {
backgroundColor: 'red'
}),
fixed: injectStylePrefixed(styletron, {
width: '20px',
height: '20px'
})
};
module.exports = Box;

View File

@@ -1,43 +0,0 @@
import React, { Component, PropTypes } from 'react';
class DeepTree extends Component {
static propTypes = {
breadth: PropTypes.number.isRequired,
components: PropTypes.object,
depth: PropTypes.number.isRequired,
id: PropTypes.number.isRequired,
wrap: PropTypes.number.isRequired
};
render() {
const { breadth, components, depth, id, wrap } = this.props;
const { Box } = components;
let result = (
<Box color={id % 3} components={components} layout={depth % 2 === 0 ? 'column' : 'row'} outer>
{depth === 0 && <Box color={id % 3 + 3} components={components} fixed />}
{depth !== 0 &&
Array.from({ length: breadth }).map((el, i) =>
<DeepTree
breadth={breadth}
components={components}
depth={depth - 1}
id={i}
key={i}
wrap={wrap}
/>
)}
</Box>
);
for (let i = 0; i < wrap; i++) {
result = (
<Box components={components}>
{result}
</Box>
);
}
return result;
}
}
module.exports = DeepTree;

View File

@@ -1,33 +0,0 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import Styletron from 'styletron-client';
import { injectStylePrefixed } from 'styletron-utils';
import React from 'react';
export const styletron = new Styletron();
class View extends React.Component {
render() {
const { style, ...other } = this.props;
return <div {...other} className={classnames(viewStyle, ...style)} />;
}
}
const viewStyle = injectStylePrefixed(styletron, {
alignItems: 'stretch',
borderWidth: '0px',
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: '0',
margin: '0px',
padding: '0px',
position: 'relative',
// fix flexbox bugs
minHeight: '0px',
minWidth: '0px'
});
export default View;

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/css-modules';
import View from './components/View/css-modules';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/glamor';
import View from './components/View/glamor';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/jss';
import View from './components/View/jss';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/react-native-stylesheet';
import View from './components/View/react-native-stylesheet';
export default {
Box,
View
};

View File

@@ -1,9 +0,0 @@
import Box from './components/Box/react-native';
import Tweet from './components/Tweet';
import { View } from 'react-native';
export default {
Box,
Tweet,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/reactxp';
import { View } from 'reactxp';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/styled-components';
import styled from 'styled-components/primitives';
export default {
Box,
View: styled.View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/styled-components';
import View from './components/View/styled-components';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/styletron';
import View from './components/View/styletron';
export default {
Box,
View
};

View File

@@ -1,14 +0,0 @@
import createRenderBenchmark from '../createRenderBenchmark';
import NestedTree from '../src/components/NestedTree';
import React from 'react';
const renderDeepTree = (label, components) =>
createRenderBenchmark({
name: `Deep tree [${label}]`,
runs: 20,
getElement() {
return <NestedTree breadth={3} components={components} depth={6} id={0} wrap={1} />;
}
});
export default renderDeepTree;

View File

@@ -1,113 +0,0 @@
import createRenderBenchmark from '../createRenderBenchmark';
import Tweet from '../src/components/Tweet';
import React from 'react';
const tweet1 = {
favorite_count: 30,
favorited: true,
id: '834889712556875776',
lang: 'en',
retweet_count: 6,
retweeted: false,
textParts: [
{
prefix: '',
text: 'Living burrito to burrito '
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
}
],
timestamp: 'Feb 23',
user: {
fullName: 'Nicolas',
screenName: 'necolas',
profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg'
}
};
const tweet2 = {
favorite_count: 84,
favorited: false,
id: '730896800060579840',
lang: 'en',
media: {
source: {
uri: 'https://pbs.twimg.com/media/CiSqvsJVEAAtLZ1.jpg',
width: 600,
height: 338
}
},
retweet_count: 4,
retweeted: true,
textParts: [
{
prefix: '',
text: 'Presenting '
},
{
displayUrl: 'mobile.twitter.com',
expandedUrl: 'https://mobile.twitter.com',
isEntity: true,
isUrl: true,
linkRelation: 'nofollow',
prefix: '',
text: '',
textDirection: 'ltr',
url: 'https://t.co/4hRCAxiUUG'
},
{
prefix: '',
text: ' with '
},
{
isEntity: true,
isMention: true,
prefix: '@',
text: 'davidbellona',
textDirection: 'ltr',
url: '/davidbellona'
},
{
prefix: '',
text: " at Twitter's all hands meeting "
}
],
timestamp: 'May 12',
user: {
fullName: 'Nicolas',
screenName: 'necolas',
profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg'
}
};
const renderTweet = (label, components) =>
createRenderBenchmark({
name: `Tweet [${label}]`,
runs: 10,
getElement() {
return (
<div style={{ width: 500 }}>
<Tweet tweet={tweet1} />
<Tweet tweet={tweet2} />
</div>
);
}
});
export default renderTweet;

View File

@@ -1,14 +0,0 @@
import createRenderBenchmark from '../createRenderBenchmark';
import NestedTree from '../src/components/NestedTree';
import React from 'react';
const renderWideTree = (label, components) =>
createRenderBenchmark({
name: `Wide tree [${label}]`,
runs: 20,
getElement() {
return <NestedTree breadth={10} components={components} depth={3} id={0} wrap={4} />;
}
});
export default renderWideTree;

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
module.exports = require('./dist/core');

View File

@@ -1,60 +0,0 @@
# AppRegistry
`AppRegistry` is the control point for registering, running, prerendering, and
unmounting all apps. App root components should register themselves with
`AppRegistry.registerComponent`. Apps can be run by invoking
`AppRegistry.runApplication` (see the [getting started guide](../guides/getting-started.md) for more details).
To "stop" an application when a view should be destroyed, call
`AppRegistry.unmountApplicationComponentAtRootTag` with the tag that was passed
into `runApplication`. These should always be used as a pair.
## Methods
(web) static **getApplication**(appKey:string, appParameters: object)
Returns the given application element. Use this for server-side rendering.
Return object is of type `{ element: ReactElement; stylesheets: [ ReactElement ] }`.
static **registerConfig**(config: Array<AppConfig>)
Registry multiple applications. `AppConfig` is of type `{ appKey: string;
component: ComponentProvider; run?: Function }`.
static **registerComponent**(appKey: string, getComponentFunc: ComponentProvider)
Register a component provider under the given `appKey`.
static **registerRunnable**(appKey: string, run: Function)
Register a custom render function for an application. The function will receive
the `appParameters` passed to `runApplication`.
static **getAppKeys**()
Returns all registered app keys.
static **runApplication**(appKey: string, appParameters?: object)
Runs the application that was registered under `appKey`. The `appParameters`
must include the `rootTag` into which the application is rendered, and
optionally any `initialProps`.
On web, if the `rootTag` is a sub-section of your application it should be
styled as `position:relative` and given an explicit height.
static **unmountApplicationComponentAtRootTag**(rootTag: HTMLElement)
To "stop" an application when a view should be destroyed, call
`AppRegistry.unmountApplicationComponentAtRootTag` with the tag that was passed
into `runApplication`
## Example
```js
AppRegistry.registerComponent('MyApp', () => AppComponent)
AppRegistry.runApplication('MyApp', {
initialProps: {},
rootTag: document.getElementById('react-root')
})
```

View File

@@ -1,60 +0,0 @@
## AppState
`AppState` can tell you if the app is in the foreground or background, and
notify you when the state changes.
States
* `active` - The app is running in the foreground
* `background` - The app is running in the background (i.e., the user has not focused the app's tab).
## Properties
static **currentState**
Returns the current state of the app: `active` or `background`.
## Methods
static **addEventListener**(type: string, handler: Function)
Add a handler to `AppState` changes by listening to the `change` event type and
providing the `handler`. The handler is called with the app state value.
static **removeEventListener**(type: string, handler: Function)
Remove a handler by passing the change event `type` and the `handler`.
## Examples
To see the current state, you can check `AppState.currentState`, which will be
kept up-to-date. This example will only ever appear to say "Current state is:
active" because the app is only visible to the user when in the `active` state,
and the null state will happen only momentarily.
```js
class Example extends React.Component {
constructor(props) {
super(props)
this.state = { currentAppState: AppState.currentState }
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (currentAppState) => {
this.setState({ currentAppState });
}
render() {
return (
<Text>Current state is: {this.state.currentAppState}</Text>
)
}
}
```

View File

@@ -1,71 +0,0 @@
# AsyncStorage
`AsyncStorage` is a simple, asynchronous, persistent, key-value storage system
that is global to the domain. It's a facade over, and should be used instead of
`window.localStorage` to provide an asynchronous API and multi functions. Each
method returns a `Promise` object.
It is recommended that you use an abstraction on top of `AsyncStorage` instead
of `AsyncStorage` directly for anything more than light usage since it operates
globally.
The batched functions are useful for executing a lot of operations at once,
allowing for optimizations to provide the convenience of a single promise after
all operations are complete.
## Methods
static **clear**()
Erases all AsyncStorage. You probably don't want to call this - use
`removeItem` or `multiRemove` to clear only your own keys instead. Returns a
Promise object.
static **getAllKeys**()
Gets all known keys. Returns a Promise object.
static **getItem**(key: string)
Fetches the value of the given key. Returns a Promise object.
static **mergeItem**(key: string, value: string)
Merges existing value with input value, assuming they are stringified JSON.
Returns a Promise object.
static **multiGet**(keys: Array<string>)
`multiGet` results in an array of key-value pair arrays that matches the input
format of `multiSet`. Returns a Promise object.
```js
multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
```
static **multiMerge**(keyValuePairs: Array<Array<string>>)
multiMerge takes an array of key-value array pairs that match the output of
`multiGet`. It merges existing values with input values, assuming they are
stringified JSON. Returns a Promise object.
static **multiRemove**(keys: Array<string>)
Delete all the keys in the keys array. Returns a Promise object.
static **multiSet**(keyValuePairs: Array<Array<string>>)
`multiSet` takes an array of key-value array pairs that match the output of
`multiGet`. Returns a Promise object.
```js
multiSet([['k1', 'val1'], ['k2', 'val2']]);
```
static **removeItem**(key: string)
Removes the value of the given key. Returns a Promise object.
static **setItem**(key: string, value: string)
Sets the value of the given key. Returns a Promise object.

View File

@@ -1,16 +0,0 @@
# Clipboard
Clipboard gives you an interface for setting to the clipboard. (Getting
clipboard content is not supported on web.)
## Methods
static **getString**()
Returns a `Promise` of an empty string.
static **setString**(content: string): boolean
Copies a string to the clipboard. On web, some browsers may not support copying
to the clipboard, therefore, this function returns a boolean to indicate if the
copy was successful.

View File

@@ -1,13 +0,0 @@
# Dimensions
Note: dimensions may change (e.g due to device rotation) so any rendering logic
or styles that depend on these constants should try to call this function on
every render, rather than caching the value.
## Methods
static **get**(dimension: string)
Get a dimension (e.g., `"window"` or `"screen"`).
Example: `const { height, width } = Dimensions.get('window')`

View File

@@ -1,25 +0,0 @@
# I18nManager
Control and set the layout and writing direction of the application.
## Properties
**isRTL**: bool = false
Whether the application is currently in RTL mode.
## Methods
static **allowRTL**(allowRTL: bool)
Allow the application to display in RTL mode.
static **forceRTL**(forceRTL: bool)
Force the application to display in RTL mode.
static **setPreferredLanguageRTL**(isRTL: bool)
Set the application's preferred writing direction to RTL. You will need to
determine the user's preferred locale server-side (from HTTP headers) and
decide whether it's an RTL language.

View File

@@ -1,77 +0,0 @@
# NetInfo
`NetInfo` asynchronously determines the online/offline status of the
application.
Connection types:
* `bluetooth` - The user agent is using a Bluetooth connection.
* `cellular` - The user agent is using a cellular connection (e.g., EDGE, HSPA, LTE, etc.).
* `ethernet` - The user agent is using an Ethernet connection.
* `mixed` - The user agent is using multiple connection types.
* `none` - The user agent will not contact the network (offline).
* `other` - The user agent is using a connection type that is not one of enumerated connection types.
* `unknown` - The user agent has established a network connection, but is unable to determine what is the underlying connection technology.
* `wifi` - The user agent is using a Wi-Fi connection.
* `wimax` - The user agent is using a WiMAX connection.
## Methods
Note that support for retrieving the connection type depends upon browswer
support (and is limited to mobile browsers). It will default to `unknown` when
support is missing.
static **addEventListener**(eventName: ChangeEventName, handler: Function)
static **fetch**(): Promise
static **removeEventListener**(eventName: ChangeEventName, handler: Function)
## Properties
**isConnected**: bool = true
Available on all user agents. Asynchronously fetch a boolean to determine
internet connectivity. Use this if you are only interested with whether the device has internet connectivity.
**isConnected.addEventListener**(eventName: ChangeEventName, handler: Function)
**isConnected.fetch**(): Promise
**isConnected.removeEventListener**(eventName: ChangeEventName, handler: Function)
## Examples
Fetching the connection type:
```js
NetInfo.fetch().then((connectionType) => {
console.log('Connection type:', connectionType);
});
```
Subscribing to changes in the connection type:
```js
const handleConnectivityTypeChange = (connectionType) => {
console.log('Current connection type:', connectionType);
}
NetInfo.addEventListener('change', handleConnectivityTypeChange);
```
Fetching the connection status:
```js
NetInfo.isConnected.fetch().then((isConnected) => {
console.log('Connection status:', (isConnected ? 'online' : 'offline'));
});
```
Subscribing to changes in the connection status:
```js
const handleConnectivityStatusChange = (isConnected) => {
console.log('Current connection status:', (isConnected ? 'online' : 'offline'));
}
NetInfo.isConnected.addEventListener('change', handleConnectivityStatusChange);
```

View File

@@ -1,51 +0,0 @@
# PixelRatio
`PixelRatio` gives access to the device pixel density.
## Methods
static **get**()
Returns the device pixel density. Some examples:
* PixelRatio.get() === 1
* mdpi Android devices (160 dpi)
* PixelRatio.get() === 1.5
* hdpi Android devices (240 dpi)
* PixelRatio.get() === 2
* iPhone 4, 4S
* iPhone 5, 5c, 5s
* iPhone 6
* xhdpi Android devices (320 dpi)
* PixelRatio.get() === 3
* iPhone 6 plus
* xxhdpi Android devices (480 dpi)
* PixelRatio.get() === 3.5
* Nexus 6
static **getPixelSizeForLayoutSize**(layoutSize: number)
Converts a layout size (dp) to pixel size (px). Guaranteed to return an integer
number.
static **roundToNearestPixel**(layoutSize: number)
Rounds a layout size (dp) to the nearest layout size that corresponds to an
integer number of pixels. For example, on a device with a PixelRatio of 3,
`PixelRatio.roundToNearestPixel(8.4)` = `8.33`, which corresponds to exactly
`(8.33 * 3)` = `25` pixels.
## Examples
Fetching a correctly sized image. You should get a higher resolution image if
you are on a high pixel density device. A good rule of thumb is to multiply the
size of the image you display by the pixel ratio.
```js
const image = getImage({
width: PixelRatio.getPixelSizeForLayoutSize(200),
height: PixelRatio.getPixelSizeForLayoutSize(100),
});
<Image source={image} style={{ width: 200, height: 100 }} />
```

View File

@@ -1,45 +0,0 @@
# Platform
Detect what is the platform in which the app is running. This piece of
functionality can be useful when only small parts of a component are platform
specific.
## Properties
**OS**: string
`Platform.OS` will be `web` when running in a Web browser.
```js
import { Platform } from 'react-native';
const styles = StyleSheet.create({
height: (Platform.OS === 'web') ? 200 : 100,
});
```
## Methods
**select**(object): any
`Platform.select` takes an object containing `Platform.OS` as keys and returns
the value for the platform you are currently running on.
```js
import { Platform } from 'react-native';
const containerStyles = {
flex: 1,
...Platform.select({
android: {
backgroundColor: 'blue'
},
ios: {
backgroundColor: 'red'
},
web: {
backgroundColor: 'green'
}
})
});
```

View File

@@ -1,80 +0,0 @@
# StyleSheet
The `StyleSheet` abstraction converts predefined styles to (vendor-prefixed)
CSS without requiring a compile-time step. Styles that cannot be resolved
outside of the render loop (e.g., dynamic positioning) are usually applied as
inline styles. Read more about [how to style your
application](../guides/style.md).
## Methods
**create**(obj: {[key: string]: any})
Each key of the object passed to `create` must define a style object.
**flatten**: function
Flattens an array of styles into a single style object.
(web) **getStyleSheets**: function
Returns an array of stylesheets (`{ id, textContent }`). Useful for
compile-time or server-side rendering.
## Properties
**absoluteFill**: number
A very common pattern is to create overlays with position absolute and zero positioning,
so `absoluteFill` can be used for convenience and to reduce duplication of these repeated
styles.
```js
<View style={StyleSheet.absoluteFill} />
```
**absoluteFillObject**: object
Sometimes you may want `absoluteFill` but with a couple tweaks - `absoluteFillObject` can be
used to create a customized entry in a `StyleSheet`, e.g.:
```js
const styles = StyleSheet.create({
wrapper: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'transparent',
top: 10
}
})
```
**hairlineWidth**: number
## Example
```js
<View style={styles.container}>
<Text
children={'Title text'}
style={[
styles.title,
this.props.isActive && styles.activeTitle
]}
/>
</View>
const styles = StyleSheet.create({
container: {
borderRadius: 4,
borderWidth: 0.5,
borderColor: '#d6d7da',
},
title: {
fontSize: 19,
fontWeight: 'bold',
},
activeTitle: {
color: 'red',
}
})
```

View File

@@ -1,35 +0,0 @@
# Vibration
Vibration is described as a pattern of on-off pulses, which may be of varying
lengths. The pattern may consist of either a single integer, describing the
number of milliseconds to vibrate, or an array of integers describing a pattern
of vibrations and pauses. Vibration is controlled with a single method:
`Vibration.vibrate()`.
The vibration is asynchronous so this method will return immediately. There
will be no effect on devices that do not support vibration.
## Methods
static **cancel**()
Stop the vibration.
static **vibrate**(pattern)
Start the vibration pattern.
## Examples
Vibrate once for 200ms:
```js
Vibration.vibrate(200);
Vibration.vibrate([200]);
```
Vibrate for 200ms, pause for 100ms, vibrate for 200ms:
```js
Vibration.vibrate([200, 100, 200]);
```

View File

@@ -1,70 +0,0 @@
# ActivityIndicator
## Props
[...View props](./View.md)
**animating**: boolean = true
Whether to show the indicator or hide it.
**color**: ?color = '#1976D2'
The foreground color of the spinner.
**hidesWhenStopped**: ?boolean = true
Whether the indicator should hide when not animating.
**size**: ?enum('small, 'large') | number = 'small'
Size of the indicator. Small has a height of `20`, large has a height of `36`.
## Examples
```js
import React, { Component } from 'react'
import { ActivityIndicator, StyleSheet, View } from 'react-native'
class ToggleAnimatingActivityIndicator extends Component {
constructor(props) {
super(props)
this.state = { animating: true }
}
componentDidMount: function() {
this.setToggleTimeout();
}
render() {
return (
<ActivityIndicator
animating={this.state.animating}
size="large"
style={[styles.centering, { height: 80 }]}
/>
);
}
_setToggleTimeout() {
setTimeout(() => {
this.setState({ animating: !this.state.animating })
this._setToggleTimeout()
}, 1200)
}
})
const styles = StyleSheet.create({
centering: {
alignItems: 'center',
justifyContent: 'center'
},
gray: {
backgroundColor: '#cccccc'
},
horizontal: {
flexDirection: 'row',
justifyContent: 'space-around'
}
})
```

View File

@@ -1,43 +0,0 @@
# Button
A basic button component. Supports a minimal level of customization. You can
build your own custom button using `TouchableOpacity` or
`TouchableNativeFeedback`.
## Props
**accessibilityLabel**: ?string
Overrides the text that's read by a screen reader when the user interacts
with the element.
**color**: ?string
Background color of the button.
**disabled**: ?boolean
If `true`, disable all interactions for this element.
**onPress**: function
This function is called on press.
**testID**: ?string
Used to locate this view in end-to-end tests.
**title**: string
Text to display inside the button.
## Examples
```js
<Button
accessibilityLabel="Learn more about this purple button"
color="#841584"
onPress={onPressLearnMore}
title="Learn More"
/>
```

View File

@@ -1,167 +0,0 @@
# Image
An accessibile image component with support for image resizing, default image,
and child content.
Unsupported React Native props:
`capInsets` (ios),
`onProgress` (ios)
## Props
**accessibilityLabel**: ?string
The text that's read by a screenreader when someone interacts with the image.
**accessible**: ?boolean
When `true`, indicates the image is an accessibility element.
**children**: ?any
Content to display over the image.
**defaultSource**: ?object
An image to display as a placeholder while downloading the final image off the
network. `{ uri: string, width, height }`
**onError**: ?function
Invoked on load error with `{nativeEvent: {error}}`.
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onLoad**: ?function
Invoked when load completes successfully.
**onLoadEnd**: ?function
Invoked when load either succeeds or fails,
**onLoadStart**: ?function
Invoked on load start.
**resizeMode**: ?enum('center', 'contain', 'cover', 'none', 'repeat', 'stretch') = 'cover'
Determines how to resize the image when the frame doesn't match the raw image
dimensions.
**source**: ?object
`uri` is a string representing the resource identifier for the image, which
could be an http address or a base64 encoded image. `{ uri: string, width, height }`
**style**: ?style
+ ...[View#style](./View.md)
+ `resizeMode`
**testID**: ?string
Used to locate a view in end-to-end tests.
## Properties
static **resizeMode**: object
Example usage:
```
<Image resizeMode={Image.resizeMode.contain} />
```
## Methods
static **getSize**(uri: string, success: (width, height) => {}, failure: function)
Retrieve the width and height (in pixels) of an image prior to displaying it.
This method can fail if the image cannot be found, or fails to download.
(In order to retrieve the image dimensions, the image may first need to be
loaded or downloaded, after which it will be cached. This means that in
principle you could use this method to preload images, however it is not
optimized for that purpose, and may in future be implemented in a way that does
not fully load/download the image data.)
static **prefetch**(url: string): Promise
Prefetches a remote image for later use by downloading it.
## Examples
```js
import placeholderAvatar from './placeholderAvatar.png'
import React, { Component } from 'react'
import { Image, PropTypes, StyleSheet } from 'react-native'
export default class ImageExample extends Component {
constructor(props, context) {
super(props, context)
this.state = { loading: true }
}
static propTypes = {
size: PropTypes.oneOf(['small', 'normal', 'large']),
testID: Image.propTypes.testID,
user: PropTypes.object
}
static defaultProps = {
size: 'normal'
}
_onLoad(e) {
console.log('Avatar.onLoad', e)
this.setState({ loading: false })
}
render() {
const { size, testID, user } = this.props
const loadingStyle = this.state.loading ? { styles.loading } : { }
return (
<Image
accessibilityLabel={`${user.name}'s profile picture`}
defaultSource={{ uri: placeholderAvatar }}
onLoad={this._onLoad.bind(this)}
resizeMode='cover'
source={{ uri: user.avatarUrl }}
style={[
styles.base,
styles[size],
loadingStyle
]}
/>
)
}
}
const styles = StyleSheet.create({
base: {
borderColor: 'white',
borderRadius: 5,
borderWidth: 5
},
loading: {
opacity: 0.5
},
small: {
height: 32,
width: 32
},
normal: {
height: 48,
width: 48
},
large: {
height: 64,
width: 64
}
})
```

View File

@@ -1,27 +0,0 @@
# ProgressBar
Display an activity progress bar.
## Props
[...View props](./View.md)
**color**: ?string = '#1976D2'
Color of the progress bar.
**indeterminate**: ?boolean = true
Whether the progress bar will show indeterminate progress.
**progress**: ?number
The progress value (between 0 and 1).
**testID**: ?string
Used to locate this view in end-to-end tests.
(web) **trackColor**: ?string = 'transparent'
Color of the track bar.

View File

@@ -1,134 +0,0 @@
# ScrollView
A scrollable `View` that provides itegration with the touch-locking "responder"
system. `ScrollView`'s must have a bounded height: either set the height of the
view directly (discouraged) or make sure all parent views have bounded height
(e.g., transfer `{ flex: 1}` down the view stack).
## Props
[...View props](./View.md)
**contentContainerStyle**: ?style
These styles will be applied to the scroll view content container which wraps
all of the child views.
**horizontal**: ?boolean = false
When `true`, the scroll view's children are arranged horizontally in a row
instead of vertically in a column.
**keyboardDismissMode**: ?enum('none', 'on-drag') = 'none'
Determines whether the keyboard gets dismissed in response to a scroll drag.
* `none` (the default), drags do not dismiss the keyboard.
* `on-drag`, the keyboard is dismissed when a drag begins.
* `interactive` (not supported on web; same as `none`)
**onContentSizeChange**: ?function
Called when scrollable content view of the `ScrollView` changes. It's
implemented using the `onLayout` handler attached to the content container
which this `ScrollView` renders.
**onScroll**: ?function
Fires at most once per frame during scrolling. The frequency of the events can
be contolled using the `scrollEventThrottle` prop.
Invoked on scroll with the following event:
```js
{
nativeEvent: {
contentOffset: { x, y },
contentSize: { height, width },
layoutMeasurement: { height, width }
}
}
```
**refreshControl**: ?element
TODO
A [RefreshControl](../RefreshControl) component, used to provide
pull-to-refresh functionality for the `ScrollView`.
**scrollEnabled**: ?boolean = true
When false, the content does not scroll.
**scrollEventThrottle**: ?number = 0
This controls how often the scroll event will be fired while scrolling (as a
time interval in ms). A lower number yields better accuracy for code that is
tracking the scroll position, but can lead to scroll performance problems. The
default value is `0`, which means the scroll event will be sent only once each
time the view is scrolled.
## Instance methods
**getInnerViewNode()**: any
Returns a reference to the underlying content container DOM node within the `ScrollView`.
**getScrollableNode()**: any
Returns a reference to the underlying scrollable DOM node.
**getScrollResponder()**: Component
Returns a reference to the underlying scroll responder, which supports
operations like `scrollTo`. All `ScrollView`-like components should implement
this method so that they can be composed while providing access to the
underlying scroll responder's methods.
**scrollTo(options: { x: number = 0; y: number = 0; animated: boolean = true })**
Scrolls to a given `x`, `y` offset (animation is not currently supported).
## Examples
```js
import React, { Component } from 'react'
import { ScrollView, StyleSheet } from 'react-native'
import Item from './Item'
export default class ScrollViewExample extends Component {
constructor(props, context) {
super(props, context)
this.state = {
items: Array.from(new Array(20)).map((_, i) => ({ id: i }))
}
}
onScroll(e) {
console.log(e)
}
render() {
return (
<ScrollView
children={this.state.items.map((item) => <Item {...item} />)}
contentContainerStyle={styles.container}
horizontal
onScroll={(e) => this.onScroll(e)}
scrollEventThrottle={100}
style={styles.root}
/>
)
}
}
const styles = StyleSheet.create({
root: {
borderWidth: 1
},
container: {
padding: 10
}
})
```

View File

@@ -1,76 +0,0 @@
# Switch
This is a controlled component that requires an `onValueChange` callback that
updates the value prop in order for the component to reflect user actions. If
the `value` prop is not updated, the component will continue to render the
supplied `value` prop instead of the expected result of any user actions.
## Props
[...View props](./View.md)
**disabled**: ?boolean = false
If `true` the user won't be able to interact with the switch.
**onValueChange**: ?function
Invoked with the new value when the value changes.
**value**: ?boolean = false
The value of the switch. If `true` the switch will be turned on.
(web) **activeThumbColor**: ?color = #009688
The color of the thumb grip when the switch is turned on.
(web) **activeTrackColor**: ?color = #A3D3CF
The color of the track when the switch is turned on.
(web) **thumbColor**: ?color = #FAFAFA
The color of the thumb grip when the switch is turned off.
(web) **trackColor**: ?color = #939393
The color of the track when the switch is turned off.
## Examples
```js
import React, { Component } from 'react'
import { Switch, View } from 'react-native'
class ColorSwitchExample extends Component {
constructor(props) {
super(props)
this.state = {
colorTrueSwitchIsOn: true,
colorFalseSwitchIsOn: false
}
}
render() {
return (
<View>
<Switch
activeThumbColor='#428BCA'
activeTrackColor='#A0C4E3'
onValueChange={(value) => this.setState({ colorFalseSwitchIsOn: value })}
value={this.state.colorFalseSwitchIsOn}
/>
<Switch
activeThumbColor='#5CB85C'
activeTrackColor='#ADDAAD'
onValueChange={(value) => this.setState({ colorTrueSwitchIsOn: value })}
thumbColor='#EBA9A7'
trackColor='#D9534F'
value={this.state.colorTrueSwitchIsOn}
/>
</View>
)
}
}
```

View File

@@ -1,161 +0,0 @@
# Text
`Text` is component for displaying text. It supports style, basic touch
handling, and inherits typographic styles from ancestor elements.
The `Text` is unique relative to layout: child elements use text layout
(`inline`) rather than flexbox layout. This means that elements inside of a
`Text` are not rectangles, as they wrap when reaching the edge of their
container.
Unsupported React Native props:
`allowFontScaling` (ios),
`suppressHighlighting` (ios)
## Props
NOTE: `Text` will transfer all other props to the rendered HTML element.
(web) **accessibilityLabel**: ?string
Overrides the text that's read by a screen reader when the user interacts
with the element. (This is implemented using `aria-label`.)
See the [Accessibility guide](../guides/accessibility) for more information.
(web) **accessibilityRole**: ?oneOf(roles)
Allows assistive technologies to present and support interaction with the view
in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an `accessibilityRole` of
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
See the [Accessibility guide](../guides/accessibility) for more information.
**accessible**: ?boolean
When `true`, indicates that the view is an accessibility element (i.e.,
focusable) and groups its child content. By default, all the touchable elements
and elements with `accessibilityRole` of `button` and `link` are accessible.
(This is implemented using `tabindex`.)
See the [Accessibility guide](../guides/accessibility) for more information.
**children**: ?any
Child content.
**importantForAccessibility**: ?enum('auto', 'no-hide-descendants', 'yes')
A value of `no` will remove the element from the tab flow.
A value of `no-hide-descendants` will hide the element and its children from
assistive technologies. (This is implemented using `aria-hidden`.)
See the [Accessibility guide](../guides/accessibility) for more information.
**numberOfLines**: ?number
Truncates the text with an ellipsis after this many lines. Currently only supports `1`.
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onPress**: ?function
This function is called on press.
**selectable**: ?boolean
When `false`, the text is not selectable.
**style**: ?style
+ ...[View#style](View.md)
+ `color`
+ `fontFamily`
+ `fontFeatureSettings`
+ `fontSize`
+ `fontStyle`
+ `fontWeight`
+ `letterSpacing`
+ `lineHeight`
+ `textAlign`
+ `textAlignVertical`
+ `textDecorationLine`
+ `textIndent`
+ `textOverflow`
+ `textRendering`
+ `textShadowColor`
+ `textShadowOffset`
+ `textShadowRadius`
+ `textTransform`
+ `unicodeBidi`
+ `whiteSpace`
+ `wordWrap`
+ `writingDirection`
‡ web only.
**testID**: ?string
Used to locate this view in end-to-end tests.
## Examples
```js
import React, { Component, PropTypes } from 'react'
import { StyleSheet, Text } from 'react-native'
export default class PrettyText extends Component {
static propTypes = {
...Text.propTypes,
color: PropTypes.oneOf(['white', 'gray', 'red']),
size: PropTypes.oneOf(['small', 'normal', 'large']),
weight: PropTypes.oneOf(['light', 'normal', 'bold'])
};
static defaultProps = {
...Text.defaultProps,
color: 'gray',
size: 'normal',
weight: 'normal'
};
render() {
const { color, size, style, weight, ...other } = this.props;
return (
<Text
...other
style={[
style,
colorStyles[color],
sizeStyles[size],
weightStyles[weight]
]}
/>
);
}
}
const colorStyles = StyleSheet.create({
white: { color: 'white' },
gray: { color: 'gray' },
red: { color: 'red' }
})
const sizeStyles = StyleSheet.create({
small: { fontSize: '0.85rem', padding: '0.5rem' },
normal: { fontSize: '1rem', padding: '0.75rem' },
large: { fontSize: '1.5rem', padding: '1rem' }
})
const weightStyles = StyleSheet.create({
light: { fontWeight: '300' },
normal: { fontWeight: '400' },
bold: { fontWeight: '700' }
})
```

View File

@@ -1,226 +0,0 @@
# TextInput
Accessible single- and multi-line text input via a keyboard. Supports features
such as auto-complete, auto-focus, placeholder text, and event callbacks.
Note: some props are exclusive to or excluded from `multiline`.
Unsupported React Native props:
`onEndEditing`,
`clearButtonMode` (ios),
`enablesReturnKeyAutomatically` (ios),
`placeholderTextColor`,
`returnKeyType` (ios),
`selectionState` (ios),
`underlineColorAndroid` (android)
## Props
[...View props](./View.md)
**autoCapitalize**: ?enum('characters', 'none', 'sentences', 'words') = 'sentences'
Automatically capitalize certain characters (only available in Chrome and iOS Safari).
* `characters`: Automatically capitalize all characters.
* `none`: Completely disables automatic capitalization
* `sentences`: Automatically capitalize the first letter of sentences.
* `words`: Automatically capitalize the first letter of words.
(web) **autoComplete**: ?string
Indicates whether the value of the control can be automatically completed by
the browser. [Accepted values](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
**autoCorrect**: ?boolean = true
Automatically correct spelling mistakes (only available in iOS Safari).
**autoFocus**: ?boolean = false
If `true`, focuses the input on `componentDidMount`. Only the first form element
in a document with `autofocus` is focused.
**blurOnSubmit**: ?boolean
If `true`, the text field will blur when submitted. The default value is `true`
for single-line fields and `false` for multiline fields. Note, for multiline
fields setting `blurOnSubmit` to `true` means that pressing return will blur
the field and trigger the `onSubmitEditing` event instead of inserting a
newline into the field.
**clearTextOnFocus**: ?boolean = false
If `true`, clears the text field automatically when focused.
**defaultValue**: ?string
Provides an initial value that will change when the user starts typing. Useful
for simple use-cases where you don't want to deal with listening to events and
updating the `value` prop to keep the controlled state in sync.
**editable**: ?boolean = true
If `false`, text is not editable (i.e., read-only).
**keyboardType**: enum('default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search') = 'default'
Determines which keyboard to open. (NOTE: Safari iOS requires an ancestral
`<form action>` element to display the `search` keyboard).
(Not available when `multiline` is `true`.)
**maxLength**: ?number
Limits the maximum number of characters that can be entered.
**multiline**: ?boolean = false
If true, the text input can be multiple lines.
**numberOfLines**: ?number = 2
Sets the number of lines for a multiline `TextInput`.
(Requires `multiline` to be `true`.)
**onBlur**: ?function
Callback that is called when the text input is blurred.
**onChange**: ?function
Callback that is called when the text input's text changes.
**onChangeText**: ?function
Callback that is called when the text input's text changes. The text is passed
as an argument to the callback handler.
**onFocus**: ?function
Callback that is called when the text input is focused.
**onKeyPress**: ?function
Callback that is called when a key is pressed. This will be called with `{
nativeEvent: { key: keyValue } }` where keyValue is 'Enter` or 'Backspace' for
respective keys and the typed-in character otherwise including ' ' for space.
Modifier keys are also included in the nativeEvent. Fires before onChange
callbacks.
**onSelectionChange**: ?function
Callback that is called when the text input's selection changes. This will be called with
`{ nativeEvent: { selection: { start, end } } }`.
**onSubmitEditing**: ?function
Callback that is called when the keyboard's submit button is pressed.
**placeholder**: ?string
The string that will be rendered in an empty `TextInput` before text has been
entered.
**secureTextEntry**: ?boolean = false
If true, the text input obscures the text entered so that sensitive text like
passwords stay secure.
(Not available when `multiline` is `true`.)
**selection**: ?{ start: number, end: ?number }
The start and end of the text input's selection. Set start and end to the same value to position the cursor.
**selectTextOnFocus**: ?boolean = false
If `true`, all text will automatically be selected on focus.
**style**: ?style
+ ...[Text#style](./Text.md)
+ `resize` ‡
‡ web only.
**testID**: ?string
Used to locate this view in end-to-end tests.
**value**: ?string
The value to show for the text input. `TextInput` is a controlled component,
which means the native `value` will be forced to match this prop if provided.
Read about how [React form
components](https://facebook.github.io/react/docs/forms.html) work. To prevent
user edits to the value set `editable={false}`.
## Instance methods
**blur()**
Blur the underlying DOM input.
**clear()**
Clear the text from the underlying DOM input.
**focus()**
Focus the underlying DOM input.
**isFocused()**
Returns `true` if the input is currently focused; `false` otherwise.
## Examples
```js
import React, { Component } from 'react'
import { StyleSheet, TextInput } from 'react-native'
export default class TextInputExample extends Component {
constructor(props, context) {
super(props, context)
this.state = { isFocused: false }
}
_onBlur(e) {
this.setState({ isFocused: false })
}
_onFocus(e) {
this.setState({ isFocused: true })
}
render() {
return (
<TextInput
accessibilityLabel='Write a status update'
maxNumberOfLines={5}
multiline
numberOfLines={2}
onBlur={this._onBlur.bind(this)}
onFocus={this._onFocus.bind(this)}
placeholder={`What's happening?`}
style={[
styles.default
this.state.isFocused && styles.focused
]}
/>
);
}
}
const styles = StyleSheet.create({
default: {
borderColor: 'gray',
borderBottomWidth: 2
},
focused: {
borderColor: 'blue'
}
})
```

View File

@@ -1,46 +0,0 @@
# TouchableWithoutFeedback
Do not use unless you have a very good reason. All the elements that respond to
press should have a visual feedback when touched. This is one of the primary
reason a "web" app doesn't feel "native".
**NOTE: `TouchableWithoutFeedback` supports only one child**. If you wish to have
several child components, wrap them in a View.
## Props
[...View props](./View.md)
**delayLongPress**: ?number
Delay in ms, from `onPressIn`, before `onLongPress` is called.
**delayPressIn**: ?number
Delay in ms, from the start of the touch, before `onPressIn` is called.
**delayPressOut**: ?number
Delay in ms, from the release of the touch, before `onPressOut` is called.
**disabled**: ?boolean
If `true`, disable all interactions for this component.
**onLongPress**: ?function
**onPress**: ?function
Called when the touch is released, but not if cancelled (e.g. by a scroll that steals the responder lock).
**onPressIn**: ?function
**onPressOut**: ?function
**pressRetentionOffset**: ?`{top: number, left: number, bottom: number, right: number}`
When the scroll view is disabled, this defines how far your touch may move off
of the button, before deactivating the button. Once deactivated, try moving it
back and you'll see that the button is once again reactivated! Move it back and
forth several times while the scroll view is disabled. Ensure you pass in a
constant to reduce memory allocations.

View File

@@ -1,317 +0,0 @@
# View
`View` is the fundamental UI building block. It is a component that supports
style, layout with flexbox, and accessibility controls. It can be nested
inside another `View` and has 0-to-many children of any type.
Also, refer to React Native's documentation about the [Gesture Responder
System](http://facebook.github.io/react-native/releases/0.22/docs/gesture-responder-system.html).
Unsupported React Native props: `collapsable`, `onAccessibilityTap`, `onMagicTap`.
## Props
NOTE: `View` will transfer all other props to the rendered HTML element.
**accessibilityLabel**: ?string
Overrides the text that's read by a screen reader when the user interacts
with the element. (This is implemented using `aria-label`.)
See the [Accessibility guide](../guides/accessibility.md) for more information.
**accessibilityLiveRegion**: ?enum('assertive', 'none', 'polite')
Indicates to assistive technologies whether to notify the user when the view
changes. The values of this attribute are expressed in degrees of importance.
When regions are specified as `polite` (recommended), updates take low
priority. When regions are specified as `assertive`, assistive technologies
will interrupt and immediately notify the user. (This is implemented using
[`aria-live`](http://www.w3.org/TR/wai-aria/states_and_properties#aria-live).)
See the [Accessibility guide](../guides/accessibility.md) for more information.
(web) **accessibilityRole**: ?enum(roles)
Allows assistive technologies to present and support interaction with the view
in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an `accessibilityRole` of
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
See the [Accessibility guide](../guides/accessibility.md) for more information.
**accessible**: ?boolean
When `true`, indicates that the view is an accessibility element (i.e.,
focusable) and groups its child content. By default, all the touchable elements
and elements with `accessibilityRole` of `button` and `link` are accessible.
(This is implemented using `tabindex`.)
See the [Accessibility guide](../guides/accessibility.md) for more information.
**children**: ?element
Child content.
**hitSlop**: ?object
This defines how far a touch event can start away from the view (in pixels).
Typical interface guidelines recommend touch targets that are at least 30 - 40
points/density-independent pixels.
For example, if a touchable view has a height of `20` the touchable height can
be extended to `40` with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}`.
**importantForAccessibility**: ?enum('auto', 'no', 'no-hide-descendants', 'yes')
A value of `no` will remove the element from the tab flow.
A value of `no-hide-descendants` will hide the element and its children from
assistive technologies. (This is implemented using `aria-hidden`.)
See the [Accessibility guide](../guides/accessibility.md) for more information.
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onMoveShouldSetResponder**: ?function => boolean
Does this view want to "claim" touch responsiveness? This is called for every
touch move on the `View` when it is not the responder.
**onMoveShouldSetResponderCapture**: ?function => boolean
If a parent `View` wants to prevent a child `View` from becoming responder on a
move, it should have this handler return `true`.
**onResponderGrant**: ?function
The `View` is now responding to touch events. This is the time to highlight and
show the user what is happening. For most touch interactions, you'll simply
want to wrap your component in `TouchableHighlight` or `TouchableOpacity`.
**onResponderMove**: ?function
The user is moving their finger.
**onResponderReject**: ?function
Another responder is already active and will not release it to the `View`
asking to be the responder.
**onResponderRelease**: ?function
Fired at the end of the touch.
**onResponderTerminate**: ?function
The responder has been taken from the `View`.
**onResponderTerminationRequest**: ?function
Some other `View` wants to become responder and is asking this `View` to
release its responder. Returning `true` allows its release.
**onStartShouldSetResponder**: ?function => boolean
Does this view want to become responder on the start of a touch?
**onStartShouldSetResponderCapture**: ?function => boolean
If a parent `View` wants to prevent a child `View` from becoming the responder
on a touch start, it should have this handler return `true`.
**pointerEvents**: ?enum('auto', 'box-only', 'box-none', 'none') = 'auto'
Controls whether the View can be the target of touch events. The enhanced
`pointerEvents` modes provided are not part of the CSS spec, therefore,
`pointerEvents` is excluded from `style`.
`box-none` is the equivalent of:
```css
.box-none { pointer-events: none !important; }
.box-none > * { pointer-events: auto; }
```
`box-only` is the equivalent of:
```css
.box-only { pointer-events: auto !important; }
.box-only > * { pointer-events: none; }
```
**style**: ?style
+ `alignContent`
+ `alignItems`
+ `alignSelf`
+ `animationDelay`
+ `animationDirection`
+ `animationDuration`
+ `animationFillMode`
+ `animationIterationCount`
+ `animationName`
+ `animationPlayState`
+ `animationTimingFunction`
+ `backfaceVisibility`
+ `backgroundAttachment`
+ `backgroundBlendMode`
+ `backgroundClip`
+ `backgroundColor`
+ `backgroundImage`
+ `backgroundOrigin`
+ `backgroundPosition`
+ `backgroundRepeat`
+ `backgroundSize`
+ `borderColor` (single value)
+ `borderTopColor`
+ `borderBottomColor`
+ `borderRightColor`
+ `borderLeftColor`
+ `borderRadius` (single value)
+ `borderTopLeftRadius`
+ `borderTopRightRadius`
+ `borderBottomLeftRadius`
+ `borderBottomRightRadius`
+ `borderStyle` (single value)
+ `borderTopStyle`
+ `borderRightStyle`
+ `borderBottomStyle`
+ `borderLeftStyle`
+ `borderWidth` (single value)
+ `borderBottomWidth`
+ `borderLeftWidth`
+ `borderRightWidth`
+ `borderTopWidth`
+ `bottom`
+ `boxShadow`
+ `boxSizing`
+ `clip`
+ `cursor`
+ `display`
+ `filter`
+ `flex` (number)
+ `flexBasis`
+ `flexDirection`
+ `flexGrow`
+ `flexShrink`
+ `flexWrap`
+ `gridAutoColumns`
+ `gridAutoFlow`
+ `gridAutoRows`
+ `gridColumnEnd`
+ `gridColumnGap`
+ `gridColumnStart`
+ `gridRowEnd`
+ `gridRowGap`
+ `gridRowStart`
+ `gridTemplateColumns`
+ `gridTemplateRows`
+ `gridTemplateAreas`
+ `height`
+ `justifyContent`
+ `left`
+ `margin` (single value)
+ `marginBottom`
+ `marginHorizontal`
+ `marginLeft`
+ `marginRight`
+ `marginTop`
+ `marginVertical`
+ `maxHeight`
+ `maxWidth`
+ `minHeight`
+ `minWidth`
+ `opacity`
+ `order`
+ `outline`
+ `outlineColor`
+ `overflow`
+ `overflowX`
+ `overflowY`
+ `padding` (single value)
+ `paddingBottom`
+ `paddingHorizontal`
+ `paddingLeft`
+ `paddingRight`
+ `paddingTop`
+ `paddingVertical`
+ `perspective`
+ `perspectiveOrigin`
+ `position`
+ `right`
+ `shadowColor`
+ `shadowOffset`
+ `shadowOpacity`
+ `shadowRadius`
+ `top`
+ `transform`
+ `transformOrigin`
+ `transitionDelay`
+ `transitionDuration`
+ `transitionProperty`
+ `transitionTimingFunction`
+ `userSelect`
+ `visibility`
+ `width`
+ `willChange`
+ `zIndex`
‡ web only.
Default:
```js
{
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative'
};
```
(See [facebook/yoga](https://github.com/facebook/yoga)).
**testID**: ?string
Used to locate this view in end-to-end tests.
## Examples
```js
import React, { Component, PropTypes } from 'react'
import { StyleSheet, View } from 'react-native'
export default class ViewExample extends Component {
render() {
return (
<View style={styles.row}>
{
['1', '2', '3', '4', '5'].map((value, i) => {
return <View children={value} key={i} style={styles.cell} />
})
}
</View>
);
}
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row'
},
cell: {
flexGrow: 1
}
})
```

View File

@@ -1,204 +0,0 @@
# Getting started
This guide will help you to correctly configure build and test tools to work
with React Native for Web.
Alternatively, you can quickly setup a local project using
[create-react-app](https://github.com/facebookincubator/create-react-app)
(which supports `react-native-web` out-of-the-box once installed),
[react-native-web-starter](https://github.com/grabcode/react-native-web-starter),
or [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack).
It is recommended that your application provide a `Promise` and `Array.from`
polyfill.
## Webpack and Babel
[Webpack](https://webpack.js.org) is a popular build tool for web apps. Below is an
example of how to configure a build that uses [Babel](https://babeljs.io/) to
compile your JavaScript for the web.
```js
// webpack.config.js
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
test: /\.js$/,
// Add every directory that needs to be compiled by Babel during the build
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules/react-native-uncompiled')
],
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// The 'react-native' preset is recommended
presets: ['react-native']
}
}
};
// This is needed for webpack to import static images in JavaScript files
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]'
}
}
};
module.exports = {
// ...the rest of your config
module: {
rules: [
babelLoaderConfiguration,
imageLoaderConfiguration
]
},
plugins: [
// `process.env.NODE_ENV === 'production'` must be `true` for production
// builds to eliminate development checks and reduce build size.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
],
resolve: {
// Maps the 'react-native' import to 'react-native-web'.
alias: {
'react-native': 'react-native-web'
},
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
extensions: [ '.web.js', '.js' ]
}
}
```
Please refer to the Webpack documentation for more information.
## Jest
[Jest](https://facebook.github.io/jest/) also needs to map `react-native` to `react-native-web`.
```
"jest": {
"moduleNameMapper": {
"react-native": "<rootDir>/node_modules/react-native-web"
}
}
```
Please refer to the Jest documentation for more information.
## Web-specific code
Minor platform differences can use the `Platform` module.
```js
import { AppRegistry, Platform } from 'react-native'
AppRegistry.registerComponent('MyApp', () => MyApp)
if (Platform.OS === 'web') {
AppRegistry.runApplication('MyApp', {
rootTag: document.getElementById('react-root')
});
}
```
More significant platform differences should use platform-specific files (see
the webpack configuration above for resolving `*.web.js` files):
For example, with the following files in your project:
```
MyComponent.android.js
MyComponent.ios.js
MyComponent.web.js
```
And the following import:
```js
import MyComponent from './MyComponent';
```
React Native will automatically import the correct variant for each specific
target platform.
## Client-side rendering
Rendering using `ReactNative`:
```js
import React from 'react'
import ReactNative from 'react-native'
// component that renders the app
const AppHeaderContainer = (props) => { /* ... */ }
ReactNative.render(<AppHeaderContainer />, document.getElementById('react-app-header'))
```
Rendering using `AppRegistry`:
```js
import React from 'react'
import ReactNative, { AppRegistry } from 'react-native'
// component that renders the app
const AppContainer = (props) => { /* ... */ }
// register the app
AppRegistry.registerComponent('App', () => AppContainer)
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: document.getElementById('react-app')
})
```
Rendering within `ReactDOM.render` also works when introducing
`react-native-web` to an existing web app, but otherwise it is not recommended.
## Server-side rendering
Rendering using the `AppRegistry`:
```js
import ReactDOMServer from 'react-dom/server'
import ReactNative, { AppRegistry } from 'react-native'
// component that renders the app
const AppContainer = (props) => { /* ... */ }
// register the app
AppRegistry.registerComponent('App', () => AppContainer)
// prerender the app
const { element, stylesheets } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
const initialStyles = stylesheets.map((sheet) => ReactDOMServer.renderToStaticMarkup(sheet)).join('\n');
// construct HTML document
const document = `
<!DOCTYPE html>
<html>
<head>
${initialStyles}
</head>
<body>
${initialHTML}
`
```

View File

@@ -1,31 +0,0 @@
# Known issues
## Safari flexbox performance
Safari version prior to 10.1 can suffer from extremely [poor flexbox
performance](https://bugs.webkit.org/show_bug.cgi?id=150445). The recommended
way to work around this issue (as used on mobile.twitter.com) is to set
`display:block` on Views in your element hierarchy that you know don't need
flexbox layout.
## Missing modules and components
Not all of the views present on iOS/Android are currently available on Web. We
are very much interested in the community's feedback on the next set of modules
and views.
Not all the modules or views for iOS/Android can be implemented on Web. In some
cases it will be necessary to use a Web counterpart or to guard the use of a
module with `Platform.OS` (e.g. `NativeModules`)
## Missing component props
There are properties that do not work across all platforms. All web-specific
props are annotated with `(web)` in the documentation.
## Platform parity
There are some known issues in React Native where APIs could be made more
consistent between platforms. For example, React Native for Web includes
`ActivityIndicator` and a horizontal `ProgressBar` for Web use, in anticipation
of the convergence between the iOS and Android components in React Native.

View File

@@ -1 +0,0 @@
import '@kadira/storybook-addon-options/register';

View File

@@ -1,33 +0,0 @@
const path = require('path');
const webpack = require('webpack');
const DEV = process.env.NODE_ENV !== 'production';
module.exports = {
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: { cacheDirectory: true }
},
{
test: /\.(gif|jpe?g|png|svg)$/,
loader: 'url-loader',
query: { name: '[name].[ext]' }
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.__REACT_NATIVE_DEBUG_ENABLED__': DEV
})
],
resolve: {
alias: {
'react-native': path.join(__dirname, '../../../src/module')
}
}
};

View File

@@ -1,28 +0,0 @@
/**
* @flow
*/
import sources from '../sources';
import React from 'react';
import { Image, StyleSheet, Text } from 'react-native';
const ImageChildrenExample = () =>
<Image source={sources.large} style={styles.image}>
<Text style={styles.text}>React</Text>
</Image>;
const styles = StyleSheet.create({
image: {
width: 60,
height: 60,
backgroundColor: 'transparent',
justifyContent: 'center',
alignItems: 'center'
},
text: {
backgroundColor: 'transparent',
color: 'white'
}
});
export default ImageChildrenExample;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,246 +0,0 @@
/**
* @flow
*/
/*
import React from 'react';
import UIExplorer, { PropText } from '../../ui-explorer';
import { action, storiesOf } from '@kadira/storybook';
import {
Image,
StyleSheet,
Text,
TouchableHighlight,
TouchableOpacity,
Platform,
TouchableNativeFeedback,
View
} from 'react-native';
class TouchableFeedbackEvents extends React.Component {
constructor(props) {
super(props);
this.state = { eventLog: [] };
}
render() {
return (
<View testID="touchable_feedback_events">
<View style={[styles.row, { justifyContent: 'center' }]}>
<TouchableOpacity
accessibilityComponentType="button"
accessibilityLabel="touchable feedback events"
accessibilityTraits="button"
onLongPress={this._createPressHandler('longPress')}
onPress={this._createPressHandler('press')}
onPressIn={this._createPressHandler('pressIn')}
onPressOut={this._createPressHandler('pressOut')}
style={styles.wrapper}
testID="touchable_feedback_events_button"
>
<Text style={styles.button}>
Press Me
</Text>
</TouchableOpacity>
</View>
<View style={styles.eventLogBox} testID="touchable_feedback_events_console">
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
</View>
</View>
);
}
_createPressHandler = eventName => {
return () => {
const limit = 6;
const eventLog = this.state.eventLog.slice(0, limit - 1);
eventLog.unshift(eventName);
this.setState({ eventLog });
};
};
}
class TouchableDelayEvents extends React.Component {
constructor(props) {
super(props);
this.state = { eventLog: [] };
}
render() {
return (
<View testID="touchable_delay_events">
<View style={[styles.row, { justifyContent: 'center' }]}>
<TouchableOpacity
delayLongPress={800}
delayPressIn={400}
delayPressOut={1000}
onLongPress={this._createPressHandler('longPress - 800ms delay')}
onPress={this._createPressHandler('press')}
onPressIn={this._createPressHandler('pressIn - 400ms delay')}
onPressOut={this._createPressHandler('pressOut - 1000ms delay')}
style={styles.wrapper}
testID="touchable_delay_events_button"
>
<Text style={styles.button}>
Press Me
</Text>
</TouchableOpacity>
</View>
<View style={styles.eventLogBox} testID="touchable_delay_events_console">
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
</View>
</View>
);
}
_createPressHandler = eventName => {
return () => {
const limit = 6;
const eventLog = this.state.eventLog.slice(0, limit - 1);
eventLog.unshift(eventName);
this.setState({ eventLog });
};
};
}
const heartImage = { uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small' };
const styles = StyleSheet.create({
row: {
justifyContent: 'center',
flexDirection: 'row'
},
icon: {
width: 24,
height: 24
},
image: {
width: 50,
height: 50
},
text: {
fontSize: 16
},
block: {
padding: 10
},
button: {
color: '#007AFF'
},
disabledButton: {
color: '#007AFF',
opacity: 0.5
},
nativeFeedbackButton: {
textAlign: 'center',
margin: 10
},
hitSlopButton: {
color: 'white'
},
wrapper: {
borderRadius: 8
},
wrapperCustom: {
borderRadius: 8,
padding: 6
},
hitSlopWrapper: {
backgroundColor: 'red',
marginVertical: 30
},
logBox: {
padding: 20,
margin: 10,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9'
},
eventLogBox: {
padding: 10,
margin: 10,
height: 120,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9'
},
forceTouchBox: {
padding: 10,
margin: 10,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9',
alignItems: 'center'
},
textBlock: {
fontWeight: '500',
color: 'blue'
}
});
const examples = [
{
title: '<TouchableHighlight>',
description:
'TouchableHighlight works by adding an extra view with a ' +
'black background under the single child view. This works best when the ' +
'child view is fully opaque, although it can be made to work as a simple ' +
'background color change as well with the activeOpacity and ' +
'underlayColor props.',
render() {
return (
<View>
<View style={styles.row}>
<TouchableHighlight
onPress={() => console.log('stock THW image - highlight')}
style={styles.wrapper}
>
<Image source={heartImage} style={styles.image} />
</TouchableHighlight>
<TouchableHighlight
activeOpacity={1}
onPress={() => console.log('custom THW text - highlight')}
style={styles.wrapper}
underlayColor="rgb(210, 230, 255)"
>
<View style={styles.wrapperCustom}>
<Text style={styles.text}>
Tap Here For Custom Highlight!
</Text>
</View>
</TouchableHighlight>
</View>
</View>
);
}
},
{
title: 'Touchable feedback events',
description:
'<Touchable*> components accept onPress, onPressIn, ' +
'onPressOut, and onLongPress as props.',
render() {
return <TouchableFeedbackEvents />;
}
},
{
title: 'Touchable delay for events',
description:
'<Touchable*> components also accept delayPressIn, ' +
'delayPressOut, and delayLongPress as props. These props impact the ' +
'timing of feedback events.',
render() {
return <TouchableDelayEvents />;
}
},
{
title: 'Disabled Touchable*',
description:
'<Touchable*> components accept disabled prop which prevents ' +
'any interaction with component',
render() {
return <TouchableDisabled />;
}
}
];
*/

View File

@@ -1,91 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem, TextList } from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
<DocItem
description={[
<AppText>
Invokes the listener whenever network status changes. The listener receives one of the
following connectivity types (from the DOM connection API):
</AppText>,
<TextList
items={[
'bluetooth',
'cellular',
'ethernet',
'mixed',
'none',
'other',
'unknown',
'wifi',
'wimax'
]}
/>
]}
example={{
code: "NetInfo.addEventListener('change', (connectionType) => {})"
}}
name="static addEventListener"
typeInfo="(eventName, handler) => void"
/>,
<DocItem
description="Returns a promise that resolves with one of the connectivity types listed above."
example={{
code: `NetInfo.fetch().then((connectionType) => {
console.log('Connection type:', connectionType);
});`
}}
name="static fetch"
typeInfo="() => Promise<string>"
/>,
<DocItem
description="Removes the listener for network status changes."
name="static removeEventListener"
typeInfo="(eventName, handler) => void"
/>
]
},
{
title: 'Properties',
entries: [
<DocItem
description="An object with the same methods as above but the listener receives a boolean which represents the internet connectivity. Use this if you are only interested with whether the device has internet connectivity."
example={{
code: `NetInfo.isConnected.fetch().then((isConnected) => {
console.log('Connection status:', (isConnected ? 'online' : 'offline'));
});`
}}
name="isConnected"
typeInfo="ObjectExpression"
/>
]
}
];
storiesOf('APIs', module).add('NetInfo', () =>
<UIExplorer
description={[
<AppText>
NetInfo asynchronously determines the online/offline status of the application.
</AppText>,
<AppText>
Note that support for retrieving the connection type depends upon browswer support (and is
limited to mobile browsers). It will default to <Code>unknown</Code> when support is
missing.
</AppText>
]}
sections={sections}
title="NetInfo"
url="apis/NetInfo"
/>
);

View File

@@ -1,44 +0,0 @@
/**
* @flow
*/
import DraggableCircleExample from './examples/DraggableCircle';
import { storiesOf } from '@kadira/storybook';
import React from 'react';
import UIExplorer, { AppText, DocItem } from '../../ui-explorer';
const sections = [
{
title: 'Examples',
entries: [
<DocItem
example={{
render: () => <DraggableCircleExample />
}}
/>
]
}
];
storiesOf('APIs', module).add('PanResponder', () =>
<UIExplorer
description={
<AppText>
PanResponder reconciles several touches into a single gesture. It makes single-touch
gestures resilient to extra touches, and can be used to recognize simple multi-touch
gestures. For more information, please refer to the React Native{' '}
<AppText
accessibilityTraits="link"
href="https://facebook.github.io/react-native/docs/panresponder.html"
style={{ color: '#1B95E0' }}
target="_blank"
>
PanResponder documentation
</AppText>
</AppText>
}
sections={sections}
title="PanResponder"
url="apis/PanResponder"
/>
);

View File

@@ -1,5 +0,0 @@
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import Game2048 from './Game2048';
storiesOf('Example apps', module).add('Game2048', () => <Game2048 />);

View File

@@ -1,5 +0,0 @@
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import TicTacToe from './TicTacToe';
storiesOf('Example apps', module).add('TicTacToe', () => <TicTacToe />);

View File

@@ -1,31 +0,0 @@
/* eslint-disable react/prop-types */
/**
* @flow
*/
import React from 'react';
import { StyleSheet, Text } from 'react-native';
const AppText = ({ style, ...rest }) =>
<Text
{...rest}
accessibilityRole={rest.href ? 'link' : null}
style={[styles.baseText, style, rest.href && styles.link]}
/>;
export default AppText;
const styles = StyleSheet.create({
baseText: {
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif, ' +
'"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', // emoji fonts
lineHeight: '1.3125em'
},
link: {
color: '#1B95E0',
marginTop: 'calc(0.5 * 1.3125rem)',
textDecorationLine: 'underline'
}
});

View File

@@ -1,13 +0,0 @@
/**
* @flow
*/
import AppText from './AppText';
import Code from './Code';
import DocItem from './DocItem';
import StyleList from './StyleList';
import TextList from './TextList';
import UIExplorer from './UIExplorer';
export default UIExplorer;
export { AppText, Code, DocItem, StyleList, TextList };

View File

@@ -1,117 +1,73 @@
{
"name": "react-native-web",
"version": "0.0.110",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
"dist",
"src",
"!**/__tests__"
],
"private": true,
"version": "0.4.0",
"name": "react-native-web-monorepo",
"scripts": {
"benchmarks": "cd benchmarks && yarn && webpack && open index.html",
"build": "webpack --config webpack.config.js --sort-assets-by --progress",
"compile": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
"docs:build": "build-storybook -o ./docs/dist -c ./docs/storybook/.storybook",
"docs:start": "start-storybook -p 9001 -c ./docs/storybook/.storybook --dont-track",
"docs:publish": "yarn docs:build && git checkout gh-pages && rm -rf ./storybook && mv docs/dist storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
"clean": "del ./packages/*/dist",
"compile": "yarn clean && cd packages/react-native-web && babel src --out-dir dist --ignore \"**/__tests__\"",
"benchmarks": "cd packages/benchmarks && yarn build",
"benchmarks:release": "cd packages/benchmarks && yarn release",
"website": "cd website && yarn start",
"website:release": "cd website && yarn release",
"flow": "flow",
"fmt": "find benchmarks docs src -name '*.js' | grep -v -E '(node_modules|dist)' | xargs yarn fmt:cmd",
"fmt:cmd": "prettier --print-width=100 --single-quote --write",
"jest": "jest",
"jest:watch": "yarn test -- --watch",
"lint": "yarn lint:cmd -- benchmarks docs src",
"lint:cmd": "eslint --fix --ignore-path .gitignore",
"fmt": "find packages scripts types website -name '*.js' | grep -v -E '(node_modules|dist|vendor)' | xargs yarn fmt:cmd",
"fmt:cmd": "prettier --write",
"jest": "jest --config ./scripts/jest/config.js",
"lint": "yarn lint:check --fix",
"lint:check": "eslint packages scripts website",
"precommit": "lint-staged",
"release": "yarn lint && yarn test && yarn compile && yarn build && npm publish",
"test": "flow && jest"
"prerelease": "yarn test && yarn compile",
"release": "node ./scripts/release/publish.js",
"postrelease": "yarn website:release && yarn benchmarks:release",
"test": "yarn flow && yarn lint:check && yarn jest"
},
"babel": {
"presets": [
"react-native"
],
"plugins": [
[
"transform-react-remove-prop-types",
{
"mode": "wrap"
}
]
]
},
"jest": {
"testEnvironment": "jsdom",
"timers": "fake",
"snapshotSerializers": [
"<rootDir>/node_modules/enzyme-to-json/serializer"
]
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.0.3",
"babel-loader": "^7.1.2",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.10",
"babel-preset-env": "^1.6.1",
"babel-preset-flow": "^6.23.0",
"babel-preset-react": "^6.24.1",
"babel-preset-react-native": "^4.0.0",
"caniuse-api": "^2.0.0",
"del-cli": "^1.1.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.0",
"enzyme-to-json": "^3.2.2",
"eslint": "^4.12.1",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.5.1",
"flow-bin": "^0.63.1",
"glob": "^7.1.2",
"husky": "^0.14.3",
"jest": "^21.2.1",
"lint-staged": "^6.0.0",
"prettier": "^1.8.2",
"raf": "^3.4.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0"
},
"workspaces": [
"packages/*",
"website"
],
"lint-staged": {
"**/*.js": [
"fmt:cmd",
"git update-index --again",
"lint:cmd"
"eslint"
]
},
"dependencies": {
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"babel-runtime": "^6.23.0",
"create-react-class": "^15.6.0",
"debounce": "1.0.2",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.12",
"hyphenate-style-name": "^1.0.2",
"inline-style-prefixer": "^3.0.6",
"normalize-css-color": "^1.0.2",
"prop-types": "^15.5.10",
"react-timer-mixin": "^0.13.3"
},
"devDependencies": {
"@kadira/storybook": "^2.35.3",
"@kadira/storybook-addon-options": "^1.0.2",
"babel-cli": "^6.24.1",
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.6",
"babel-preset-react-native": "^2.1.0",
"del-cli": "^1.1.0",
"enzyme": "^2.9.1",
"enzyme-to-json": "^1.5.1",
"eslint": "^4.2.0",
"eslint-config-prettier": "^2.3.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.1.0",
"file-loader": "^0.11.2",
"flow-bin": "^0.49.1",
"jest": "^20.0.4",
"lint-staged": "^4.0.1",
"prettier": "^1.5.2",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-test-renderer": "^15.6.1",
"url-loader": "^0.5.9",
"webpack": "^3.1.0",
"webpack-bundle-analyzer": "^2.8.2"
},
"peerDependencies": {
"react": "15.4.x || 15.5.x || 15.6.x",
"react-dom": "15.4.x || 15.5.x || 15.6.x"
"prettier": {
"printWidth": 100,
"singleQuote": true
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause",
"repository": {
"type": "git",
"url": "git://github.com/necolas/react-native-web.git"
},
"tags": [
"react"
],
"keywords": [
"react",
"react-component",
"react-native",
"web"
]
"license": "BSD-3-Clause"
}

View File

@@ -0,0 +1,44 @@
# babel-plugin-react-native-web
[![npm version][package-badge]][package-url] [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
A Babel plugin that will alias `react-native` to `react-native-web` and exclude
any modules not required by your app (keeping bundle size down).
## Installation
```
yarn add --dev babel-plugin-react-native-web
```
## Usage
**.babelrc**
```
{
"plugins": ["react-native-web"]
}
```
## Example
NOTE: `react-native-web` internal paths are _not stable_ and you must not rely
on them. Always use the Babel plugin to optimize your build. What follows is an
example of the rewrite performed by the plugin.
**Before**
```js
import { StyleSheet, View } from 'react-native';
```
**After**
```js
import StyleSheet from 'react-native-web/dist/exports/StyleSheet';
import View from 'react-native-web/dist/exports/View';
```
[package-badge]: https://img.shields.io/npm/v/babel-plugin-react-native-web.svg?style=flat
[package-url]: https://yarnpkg.com/en/package/babel-plugin-react-native-web

View File

@@ -0,0 +1 @@
module.exports = require('./src');

View File

@@ -0,0 +1,15 @@
{
"name": "babel-plugin-react-native-web",
"version": "0.4.0",
"description": "Babel plugin for React Native for Web",
"main": "index.js",
"devDependencies": {
"babel-plugin-tester": "^5.0.0"
},
"author": "Nicolas Gallagher",
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/necolas/react-native-web.git"
}
}

View File

@@ -0,0 +1,109 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`export from "react-native" 1`] = `
"
export { View } from 'react-native';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as View } from 'react-native-web/dist/exports/View';
export { default as ColorPropType } from 'react-native-web/dist/exports/ColorPropType';
export { default as StyleSheet } from 'react-native-web/dist/exports/StyleSheet';
export { default as Text } from 'react-native-web/dist/exports/Text';
export { default as createElement } from 'react-native-web/dist/exports/createElement';
"
`;
exports[`export from "react-native-web" 1`] = `
"
export { View } from 'react-native-web';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';
↓ ↓ ↓ ↓ ↓ ↓
export { default as View } from 'react-native-web/dist/exports/View';
export { default as ColorPropType } from 'react-native-web/dist/exports/ColorPropType';
export { default as StyleSheet } from 'react-native-web/dist/exports/StyleSheet';
export { default as Text } from 'react-native-web/dist/exports/Text';
export { default as createElement } from 'react-native-web/dist/exports/createElement';
"
`;
exports[`import from "native-native" 1`] = `
"
import ReactNative from 'react-native';
import { View } from 'react-native';
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import ReactNative from 'react-native-web/dist/index';
import View from 'react-native-web/dist/exports/View';
import Invalid from 'react-native-web/dist/exports/Invalid';
import MyView from 'react-native-web/dist/exports/View';
import ViewPropTypes from 'react-native-web/dist/exports/ViewPropTypes';
import * as ReactNativeModules from 'react-native-web/dist/index';
"
`;
exports[`import from "react-native-web" 1`] = `
"
import { createElement } from 'react-native-web';
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
import * as ReactNativeModules from 'react-native-web';
↓ ↓ ↓ ↓ ↓ ↓
import createElement from 'react-native-web/dist/exports/createElement';
import ColorPropType from 'react-native-web/dist/exports/ColorPropType';
import StyleSheet from 'react-native-web/dist/exports/StyleSheet';
import View from 'react-native-web/dist/exports/View';
import TouchableOpacity from 'react-native-web/dist/exports/TouchableOpacity';
import processColor from 'react-native-web/dist/exports/processColor';
import * as ReactNativeModules from 'react-native-web/dist/index';
"
`;
exports[`require "react-native" 1`] = `
"
const ReactNative = require('react-native');
const { View } = require('react-native');
const { StyleSheet, TouchableOpacity } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web/dist/index').default;
const View = require('react-native-web/dist/exports/View').default;
const StyleSheet = require('react-native-web/dist/exports/StyleSheet').default;
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity').default;
"
`;
exports[`require "react-native-web" 1`] = `
"
const ReactNative = require('react-native-web');
const { createElement } = require('react-native-web');
const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = require('react-native-web');
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web/dist/index').default;
const createElement = require('react-native-web/dist/exports/createElement').default;
const ColorPropType = require('react-native-web/dist/exports/ColorPropType').default;
const StyleSheet = require('react-native-web/dist/exports/StyleSheet').default;
const View = require('react-native-web/dist/exports/View').default;
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity').default;
const processColor = require('react-native-web/dist/exports/processColor').default;
"
`;

View File

@@ -0,0 +1,52 @@
const plugin = require('..');
const pluginTester = require('babel-plugin-tester');
const tests = [
// import react-native
{
title: 'import from "native-native"',
code: `import ReactNative from 'react-native';
import { View } from 'react-native';
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';`,
snapshot: true
},
{
title: 'import from "react-native-web"',
code: `import { createElement } from 'react-native-web';
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
import * as ReactNativeModules from 'react-native-web';`,
snapshot: true
},
{
title: 'export from "react-native"',
code: `export { View } from 'react-native';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';`,
snapshot: true
},
{
title: 'export from "react-native-web"',
code: `export { View } from 'react-native-web';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';`,
snapshot: true
},
{
title: 'require "react-native"',
code: `const ReactNative = require('react-native');
const { View } = require('react-native');
const { StyleSheet, TouchableOpacity } = require('react-native');`,
snapshot: true
},
{
title: 'require "react-native-web"',
code: `const ReactNative = require('react-native-web');
const { createElement } = require('react-native-web');
const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = require('react-native-web');`,
snapshot: true
}
];
pluginTester({
plugin,
tests
});

View File

@@ -0,0 +1,126 @@
const getDistLocation = importName =>
importName ? `react-native-web/dist/exports/${importName}` : undefined;
const isReactNativeRequire = (t, node) => {
const { declarations } = node;
if (declarations.length > 1) {
return false;
}
const { id, init } = declarations[0];
return (
(t.isObjectPattern(id) || t.isIdentifier(id)) &&
t.isCallExpression(init) &&
t.isIdentifier(init.callee) &&
init.callee.name === 'require' &&
init.arguments.length === 1 &&
(init.arguments[0].value === 'react-native' || init.arguments[0].value === 'react-native-web')
);
};
const isReactNativeModule = ({ source, specifiers }) =>
source &&
(source.value === 'react-native' || source.value === 'react-native-web') &&
specifiers.length;
module.exports = function({ types: t }) {
return {
name: 'Rewrite react-native to react-native-web',
visitor: {
ImportDeclaration(path, state) {
const { specifiers } = path.node;
if (isReactNativeModule(path.node)) {
const imports = specifiers
.map(specifier => {
if (t.isImportSpecifier(specifier)) {
const importName = specifier.imported.name;
const distLocation = getDistLocation(importName);
if (distLocation) {
return t.importDeclaration(
[t.importDefaultSpecifier(t.identifier(specifier.local.name))],
t.stringLiteral(distLocation)
);
}
}
return t.importDeclaration(
[specifier],
t.stringLiteral('react-native-web/dist/index')
);
})
.filter(Boolean);
path.replaceWithMultiple(imports);
}
},
ExportNamedDeclaration(path, state) {
const { specifiers } = path.node;
if (isReactNativeModule(path.node)) {
const exports = specifiers
.map(specifier => {
if (t.isExportSpecifier(specifier)) {
const exportName = specifier.exported.name;
const localName = specifier.local.name;
const distLocation = getDistLocation(localName);
if (distLocation) {
return t.exportNamedDeclaration(
null,
[t.exportSpecifier(t.identifier('default'), t.identifier(exportName))],
t.stringLiteral(distLocation)
);
}
}
return t.exportNamedDeclaration(
null,
[specifier],
t.stringLiteral('react-native-web/dist/index')
);
})
.filter(Boolean);
path.replaceWithMultiple(exports);
}
},
VariableDeclaration(path, state) {
if (isReactNativeRequire(t, path.node)) {
const { id } = path.node.declarations[0];
if (t.isObjectPattern(id)) {
const imports = id.properties
.map(identifier => {
const distLocation = getDistLocation(identifier.key.name);
if (distLocation) {
return t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(identifier.value.name),
t.memberExpression(
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)]),
t.identifier('default')
)
)
]);
}
})
.filter(Boolean);
path.replaceWithMultiple(imports);
} else if (t.isIdentifier(id)) {
const name = id.name;
const importIndex = t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(name),
t.memberExpression(
t.callExpression(t.identifier('require'), [
t.stringLiteral('react-native-web/dist/index')
]),
t.identifier('default')
)
)
]);
path.replaceWith(importIndex);
}
}
}
}
};
};

View File

@@ -0,0 +1,92 @@
# benchmarks
Try the [benchmarks app](https://necolas.github.io/react-native-web/benchmarks) online.
To run the benchmarks locally:
```
yarn benchmarks
open ./packages/benchmarks/dist/index.html
```
Develop against these benchmarks:
```
yarn compile --watch
yarn benchmarks --watch
```
## Notes
These benchmarks are approximations of extreme cases that libraries may
encounter. The deep and wide tree cases look at the performance of mounting and
rendering large trees of styled elements. The dynamic case looks at the
performance of repeated style updates to a large mounted tree. Some libraries
must inject new styles for each "dynamic style", whereas others may not.
Libraries without support for dynamic styles (i.e., they rely on user-authored
inline styles) do not include a corresponding benchmark.
The components used in the render benchmarks are simple enough to be
implemented by multiple UI or style libraries. The benchmark implementations
and the features of the style libraries are _only approximately equivalent in
functionality_.
No benchmark will run for more than 20 seconds.
## Example results
### MacBook Pro (2011)
MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3 RAM. Google Chrome 63.
Typical render timings: mean ± standard deviations.
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `15.23` `±04.31` | `21.27` `±07.03` | - |
| `react-native-web@0.3.2` | `17.52` `±04.44` | `24.14` `±04.39` | `15.03` `±02.22` |
| `inline-styles` | `50.06` `±06.70` | `76.38` `±09.58` | `06.43` `±02.02` |
Other libraries
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `aphrodite@1.2.5` | `17.27` `±05.96` | `24.89` `±08.36` | - |
| `glamor@2.20.40` | `21.59` `±05.38` | `27.93` `±07.56` | ‡ |
| `emotion@8.0.12` | `21.07` `±04.16` | `31.40` `±09.40` | ‡ `19.80` `±13.56` |
| `styletron-react@3.0.3` | `23.55` `±05.14` | `34.26` `±07.58` | `10.39` `±02.94` |
| `react-fela@5.0.0` | `27.58` `±04.26` | `39.54` `±05.46` | `10.93` `±01.69` |
| `react-jss@8.2.1` | `27.31` `±07.87` | `40.74` `±10.67` | - |
| `styled-jsx@2.2.1` | `27.46` `±07.85` | `41.47` `±11.53` | `29.16` `±09.98` |
| `styled-components@2.4.0` | `43.89` `±06.99` | `63.26` `±09.02` | `16.17` `±03.71` |
| `reactxp@0.51.0-alpha.9` | `51.86` `±07.21` | `78.80` `±11.85` | `15.04` `±03.92` |
| `radium@0.21.0` | `101.06` `±13.00` | `144.46` `±16.94` | `17.44` `±03.59` |
### Moto G4
Moto G4 (Android 7); Octa-core (4x1.5 GHz & 4x1.2 Ghz); 2 GB RAM. Google Chrome 63
Typical render timings: mean ± standard deviations.
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `56.18` `±19.54` | `75.95` `±23.55` | - |
| `react-native-web@0.3.2` | `68.53` `±21.00` | `101.03` `±25.32` | `60.57` `±09.07` |
| `inline-styles` | `140.32` `±23.91` | `208.55` `±35.25` | `20.36` `±04.92` |
Other libraries
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `aphrodite@1.2.5` | `58.77` `±19.73` | `85.83` `±24.64` | - |
| `glamor@2.20.40` | `81.05` `±15.87` | `104.02` `±20.92` | ‡ |
| `emotion@8.0.12` | `77.12` `±19.61` | `112.04` `±24.43` | ‡ `80.40` `±40.56` |
| `styletron-react@3.0.3` | `91.00` `±17.95` | `130.49` `±20.06` | `39.70` `±06.85` |
| `react-fela@5.0.0` | `101.36` `±19.55` | `142.18` `±21.87` | `43.64` `±12.24` |
| `styled-jsx@2.2.1` | `101.60` `±25.26` | `144.12` `±30.79` | `115.63` `±32.77` |
| `react-jss@8.2.1` | `112.46` `±32.07` | `165.96` `±42.54` | - |
| `styled-components@2.4.0` | `159.85` `±24.30` | `231.00` `±31.34` | `53.86` `±13.40` |
| `reactxp@0.51.0-alpha.9` | `182.05` `±30.72` | `261.25` `±35.54` | `58.20` `±08.62` |
| `radium@0.21.0` | `323.93` `±41.46` | `464.70` `±53.93` | `59.13` `±09.76` |
‡Glamor essentially crashes the browser tab. Emotion gets slower every iteration.

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Performance tests</title>
<meta name="viewport" content="width=device-width">
<style>
html, body { height: 100%; width: 100%; overflow: hidden; }
.root { height: 100%; overflow: hidden; }
</style>
</head>
<body>
<div class="root"></div>
<script src="./bundle.js"></script>
</body>
</html>

View File

@@ -0,0 +1,37 @@
{
"private": true,
"name": "benchmarks",
"version": "0.4.0",
"scripts": {
"build": "mkdir -p dist && cp -f index.html dist/index.html && webpack --config ./webpack.config.js",
"release": "yarn build && git checkout gh-pages && rm -rf ../../benchmarks && mv dist ../../benchmarks && git add -A && git commit -m \"Benchmarks deploy\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"aphrodite": "1.2.5",
"classnames": "^2.2.5",
"d3-scale-chromatic": "^1.1.1",
"emotion": "8.0.12",
"fela": "6.1.3",
"glamor": "2.20.40",
"radium": "0.21.2",
"react": "^16.2.0",
"react-component-benchmark": "^0.0.4",
"react-dom": "^16.2.0",
"react-fela": "6.2.4",
"react-jss": "8.2.1",
"react-native-web": "0.4.0",
"reactxp": "0.51.2",
"style-loader": "0.20.1",
"styled-components": "3.1.2",
"styled-jsx": "2.2.2",
"styletron-client": "3.0.4",
"styletron-react": "3.0.4"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.4.0",
"css-loader": "^0.28.9",
"style-loader": "^0.19.1",
"webpack": "^3.10.0",
"webpack-bundle-analyzer": "^2.9.2"
}
}

View File

@@ -0,0 +1,298 @@
/* eslint-disable react/prop-types */
import Benchmark from './Benchmark';
import { Picker, StyleSheet, ScrollView, TouchableOpacity, View } from 'react-native';
import React, { Component } from 'react';
import Button from './Button';
import { IconClear, IconEye } from './Icons';
import ReportCard from './ReportCard';
import Text from './Text';
import Layout from './Layout';
import { colors } from './theme';
const Overlay = () => <View style={[StyleSheet.absoluteFill, { zIndex: 2 }]} />;
export default class App extends Component {
static displayName = '@app/App';
constructor(props, context) {
super(props, context);
const currentBenchmarkName = Object.keys(props.tests)[0];
this.state = {
currentBenchmarkName,
currentLibraryName: 'react-native-web',
status: 'idle',
results: []
};
}
render() {
const { tests } = this.props;
const { currentBenchmarkName, status, currentLibraryName, results } = this.state;
const currentImplementation = tests[currentBenchmarkName][currentLibraryName];
const { Component, Provider, getComponentProps, sampleCount } = currentImplementation;
return (
<Layout
actionPanel={
<View>
<View style={styles.pickers}>
<View style={styles.pickerContainer}>
<Text style={styles.pickerTitle}>Library</Text>
<Text style={{ fontWeight: 'bold' }}>{currentLibraryName}</Text>
<Picker
enabled={status !== 'running'}
onValueChange={this._handleChangeLibrary}
selectedValue={currentLibraryName}
style={styles.picker}
>
{Object.keys(tests[currentBenchmarkName]).map(libraryName => (
<Picker.Item key={libraryName} label={libraryName} value={libraryName} />
))}
</Picker>
</View>
<View style={{ width: 1, backgroundColor: colors.fadedGray }} />
<View style={styles.pickerContainer}>
<Text style={styles.pickerTitle}>Benchmark</Text>
<Text>{currentBenchmarkName}</Text>
<Picker
enabled={status !== 'running'}
onValueChange={this._handleChangeBenchmark}
selectedValue={currentBenchmarkName}
style={styles.picker}
>
{Object.keys(tests).map(test => (
<Picker.Item key={test} label={test} value={test} />
))}
</Picker>
</View>
</View>
<View style={{ flexDirection: 'row', height: 50 }}>
<View style={styles.grow}>
<Button
onPress={this._handleStart}
style={styles.button}
title={status === 'running' ? 'Running…' : 'Run'}
/>
</View>
</View>
{status === 'running' ? <Overlay /> : null}
</View>
}
listPanel={
<View style={styles.listPanel}>
<View style={styles.grow}>
<View style={styles.listBar}>
<View style={styles.iconClearContainer}>
<TouchableOpacity onPress={this._handleClear}>
<IconClear />
</TouchableOpacity>
</View>
</View>
<ScrollView ref={this._setScrollRef} style={styles.grow}>
{results.map((r, i) => (
<ReportCard
benchmarkName={r.benchmarkName}
key={i}
libraryName={r.libraryName}
libraryVersion={r.libraryVersion}
mean={r.mean}
meanLayout={r.meanLayout}
meanScripting={r.meanScripting}
runTime={r.runTime}
sampleCount={r.sampleCount}
stdDev={r.stdDev}
/>
))}
{status === 'running' ? (
<ReportCard
benchmarkName={currentBenchmarkName}
libraryName={currentLibraryName}
/>
) : null}
</ScrollView>
</View>
{status === 'running' ? <Overlay /> : null}
</View>
}
viewPanel={
<View style={styles.viewPanel}>
<View style={styles.iconEyeContainer}>
<TouchableOpacity onPress={this._handleVisuallyHideBenchmark}>
<IconEye style={styles.iconEye} />
</TouchableOpacity>
</View>
<Provider>
{status === 'running' ? (
<React.Fragment>
<View ref={this._setBenchWrapperRef}>
<Benchmark
component={Component}
forceLayout={true}
getComponentProps={getComponentProps}
onComplete={this._createHandleComplete({
sampleCount,
benchmarkName: currentBenchmarkName,
libraryName: currentLibraryName
})}
ref={this._setBenchRef}
sampleCount={sampleCount}
timeout={20000}
type={Component.benchmarkType}
/>
</View>
</React.Fragment>
) : (
<Component {...getComponentProps({ cycle: 10 })} />
)}
</Provider>
{status === 'running' ? <Overlay /> : null}
</View>
}
/>
);
}
_handleChangeBenchmark = value => {
this.setState(() => ({ currentBenchmarkName: value }));
};
_handleChangeLibrary = value => {
this.setState(() => ({ currentLibraryName: value }));
};
_handleStart = () => {
this.setState(
() => ({ status: 'running' }),
() => {
if (this._shouldHideBenchmark && this._benchWrapperRef) {
this._benchWrapperRef.setNativeProps({ style: { opacity: 0 } });
}
this._benchmarkRef.start();
this._scrollToEnd();
}
);
};
// hide the benchmark as it is performed (no flashing on screen)
_handleVisuallyHideBenchmark = () => {
this._shouldHideBenchmark = !this._shouldHideBenchmark;
if (this._benchWrapperRef) {
this._benchWrapperRef.setNativeProps({
style: { opacity: this._shouldHideBenchmark ? 0 : 1 }
});
}
};
_createHandleComplete = ({ benchmarkName, libraryName, sampleCount }) => results => {
this.setState(
state => ({
results: state.results.concat([
{
...results,
benchmarkName,
libraryName,
libraryVersion: this.props.tests[benchmarkName][libraryName].version
}
]),
status: 'complete'
}),
this._scrollToEnd
);
// console.log(results);
// console.log(results.samples.map(sample => sample.elapsed.toFixed(1)).join('\n'));
};
_handleClear = () => {
this.setState(() => ({ results: [] }));
};
_setBenchRef = ref => {
this._benchmarkRef = ref;
};
_setBenchWrapperRef = ref => {
this._benchWrapperRef = ref;
};
_setScrollRef = ref => {
this._scrollRef = ref;
};
// scroll the most recent result into view
_scrollToEnd = () => {
window.requestAnimationFrame(() => {
if (this._scrollRef) {
this._scrollRef.scrollToEnd();
}
});
};
}
const styles = StyleSheet.create({
viewPanel: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
overflow: 'hidden',
backgroundColor: 'black'
},
iconEye: {
color: 'white',
height: 32
},
iconEyeContainer: {
position: 'absolute',
top: 10,
right: 10,
zIndex: 1
},
iconClearContainer: {
height: '100%',
marginLeft: 5
},
grow: {
flex: 1
},
listPanel: {
flex: 1,
width: '100%',
marginHorizontal: 'auto'
},
listBar: {
padding: 5,
alignItems: 'center',
flexDirection: 'row',
backgroundColor: colors.fadedGray,
borderBottomWidth: 1,
borderBottomColor: colors.mediumGray,
justifyContent: 'flex-end'
},
pickers: {
flexDirection: 'row'
},
pickerContainer: {
flex: 1,
padding: 5
},
pickerTitle: {
fontSize: 12,
color: colors.deepGray
},
picker: {
...StyleSheet.absoluteFillObject,
appearance: 'none',
opacity: 0,
width: '100%'
},
button: {
borderRadius: 0,
fontSize: 32,
flex: 1
}
});

View File

@@ -0,0 +1,247 @@
/* global $Values */
/**
* @flow
*/
import * as Timing from './timing';
import React, { Component } from 'react';
import { getMean, getMedian, getStdDev } from './math';
import type { BenchResultsType, FullSampleTimingType, SampleTimingType } from './types';
export const BenchmarkType = {
MOUNT: 'mount',
UPDATE: 'update',
UNMOUNT: 'unmount'
};
const shouldRender = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
switch (type) {
// Render every odd iteration (first, third, etc)
// Mounts and unmounts the component
case BenchmarkType.MOUNT:
case BenchmarkType.UNMOUNT:
return !((cycle + 1) % 2);
// Render every iteration (updates previously rendered module)
case BenchmarkType.UPDATE:
return true;
default:
return false;
}
};
const shouldRecord = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
switch (type) {
// Record every odd iteration (when mounted: first, third, etc)
case BenchmarkType.MOUNT:
return !((cycle + 1) % 2);
// Record every iteration
case BenchmarkType.UPDATE:
return true;
// Record every even iteration (when unmounted)
case BenchmarkType.UNMOUNT:
return !(cycle % 2);
default:
return false;
}
};
const isDone = (
cycle: number,
sampleCount: number,
type: $Values<typeof BenchmarkType>
): boolean => {
switch (type) {
case BenchmarkType.MOUNT:
return cycle >= sampleCount * 2 - 1;
case BenchmarkType.UPDATE:
return cycle >= sampleCount - 1;
case BenchmarkType.UNMOUNT:
return cycle >= sampleCount * 2;
default:
return true;
}
};
const sortNumbers = (a: number, b: number): number => a - b;
type BenchmarkPropsType = {
component: typeof React.Component,
forceLayout?: boolean,
getComponentProps: Function,
onComplete: (x: BenchResultsType) => void,
sampleCount: number,
timeout: number,
type: $Values<typeof BenchmarkType>
};
type BenchmarkStateType = {
componentProps: Object,
cycle: number,
running: boolean
};
/**
* Benchmark
* TODO: documentation
*/
export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkStateType> {
_raf: ?Function;
_startTime: number;
_samples: Array<SampleTimingType>;
static displayName = 'Benchmark';
static defaultProps = {
sampleCount: 50,
timeout: 10000, // 10 seconds
type: BenchmarkType.MOUNT
};
static Type = BenchmarkType;
constructor(props: BenchmarkPropsType, context?: {}) {
super(props, context);
const cycle = 0;
const componentProps = props.getComponentProps({ cycle });
this.state = {
componentProps,
cycle,
running: false
};
this._startTime = 0;
this._samples = [];
}
componentWillReceiveProps(nextProps: BenchmarkPropsType) {
if (nextProps) {
this.setState(state => ({ componentProps: nextProps.getComponentProps(state.cycle) }));
}
}
componentWillUpdate(nextProps: BenchmarkPropsType, nextState: BenchmarkStateType) {
if (nextState.running && !this.state.running) {
this._startTime = Timing.now();
}
}
componentDidUpdate() {
const { forceLayout, sampleCount, timeout, type } = this.props;
const { cycle, running } = this.state;
if (running && shouldRecord(cycle, type)) {
this._samples[cycle].scriptingEnd = Timing.now();
// force style recalc that would otherwise happen before the next frame
if (forceLayout) {
this._samples[cycle].layoutStart = Timing.now();
if (document.body) {
document.body.offsetWidth;
}
this._samples[cycle].layoutEnd = Timing.now();
}
}
if (running) {
const now = Timing.now();
if (!isDone(cycle, sampleCount, type) && now - this._startTime < timeout) {
this._handleCycleComplete();
} else {
this._handleComplete(now);
}
}
}
componentWillUnmount() {
if (this._raf) {
window.cancelAnimationFrame(this._raf);
}
}
render() {
const { component: Component, type } = this.props;
const { componentProps, cycle, running } = this.state;
if (running && shouldRecord(cycle, type)) {
this._samples[cycle] = { scriptingStart: Timing.now() };
}
return running && shouldRender(cycle, type) ? <Component {...componentProps} /> : null;
}
start() {
this._samples = [];
this.setState(() => ({ running: true, cycle: 0 }));
}
_handleCycleComplete() {
const { getComponentProps, type } = this.props;
const { cycle } = this.state;
let componentProps;
if (getComponentProps) {
// Calculate the component props outside of the time recording (render)
// so that it doesn't skew results
componentProps = getComponentProps({ cycle });
// make sure props always change for update tests
if (type === BenchmarkType.UPDATE) {
componentProps['data-test'] = cycle;
}
}
this._raf = window.requestAnimationFrame(() => {
this.setState((state: BenchmarkStateType) => ({
cycle: state.cycle + 1,
componentProps
}));
});
}
getSamples(): Array<FullSampleTimingType> {
return this._samples.reduce(
(
memo: Array<FullSampleTimingType>,
{ scriptingStart, scriptingEnd, layoutStart, layoutEnd }: SampleTimingType
): Array<FullSampleTimingType> => {
memo.push({
start: scriptingStart,
end: layoutEnd || scriptingEnd || 0,
scriptingStart,
scriptingEnd: scriptingEnd || 0,
layoutStart,
layoutEnd
});
return memo;
},
[]
);
}
_handleComplete(endTime: number) {
const { onComplete } = this.props;
const samples = this.getSamples();
this.setState(() => ({ running: false, cycle: 0 }));
const runTime = endTime - this._startTime;
const sortedElapsedTimes = samples.map(({ start, end }) => end - start).sort(sortNumbers);
const sortedScriptingElapsedTimes = samples
.map(({ scriptingStart, scriptingEnd }) => scriptingEnd - scriptingStart)
.sort(sortNumbers);
const sortedLayoutElapsedTimes = samples
.map(({ layoutStart, layoutEnd }) => (layoutEnd || 0) - (layoutStart || 0))
.sort(sortNumbers);
onComplete({
startTime: this._startTime,
endTime,
runTime,
sampleCount: samples.length,
samples: samples,
max: sortedElapsedTimes[sortedElapsedTimes.length - 1],
min: sortedElapsedTimes[0],
median: getMedian(sortedElapsedTimes),
mean: getMean(sortedElapsedTimes),
stdDev: getStdDev(sortedElapsedTimes),
meanLayout: getMean(sortedLayoutElapsedTimes),
meanScripting: getMean(sortedScriptingElapsedTimes)
});
}
}

View File

@@ -0,0 +1,27 @@
// @flow
type ValuesType = Array<number>;
export const getStdDev = (values: ValuesType): number => {
const avg = getMean(values);
const squareDiffs = values.map((value: number) => {
const diff = value - avg;
return diff * diff;
});
return Math.sqrt(getMean(squareDiffs));
};
export const getMean = (values: ValuesType): number => {
const sum = values.reduce((sum: number, value: number) => sum + value, 0);
return sum / values.length;
};
export const getMedian = (values: ValuesType): number => {
if (values.length === 1) {
return values[0];
}
const numbers = values.sort((a: number, b: number) => a - b);
return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2;
};

View File

@@ -0,0 +1,18 @@
// @flow
const NS_PER_MS = 1e6;
const MS_PER_S = 1e3;
// Returns a high resolution time (if possible) in milliseconds
export function now(): number {
if (window && window.performance) {
return window.performance.now();
} else if (process && process.hrtime) {
const [seconds, nanoseconds] = process.hrtime();
const secInMS = seconds * MS_PER_S;
const nSecInMS = nanoseconds / NS_PER_MS;
return secInMS + nSecInMS;
} else {
return Date.now();
}
}

View File

@@ -0,0 +1,31 @@
/**
* @flow
*/
export type BenchResultsType = {
startTime: number,
endTime: number,
runTime: number,
sampleCount: number,
samples: Array<FullSampleTimingType>,
max: number,
min: number,
median: number,
mean: number,
stdDev: number
};
export type SampleTimingType = {
scriptingStart: number,
scriptingEnd?: number,
layoutStart?: number,
layoutEnd?: number
};
export type FullSampleTimingType = {
start: number,
end: number,
scriptingStart: number,
scriptingEnd: number,
layoutStart?: number,
layoutEnd?: number
};

View File

@@ -0,0 +1,70 @@
import { ColorPropType, StyleSheet, TouchableHighlight, Text } from 'react-native';
import React, { Component } from 'react';
import { bool, func, string } from 'prop-types';
export default class Button extends Component<*> {
static displayName = '@app/Button';
static propTypes = {
accessibilityLabel: string,
color: ColorPropType,
disabled: bool,
onPress: func.isRequired,
style: TouchableHighlight.propTypes.style,
testID: string,
textStyle: Text.propTypes.style,
title: string.isRequired
};
render() {
const {
accessibilityLabel,
color,
disabled,
onPress,
style,
textStyle,
testID,
title
} = this.props;
return (
<TouchableHighlight
accessibilityLabel={accessibilityLabel}
accessibilityRole="button"
disabled={disabled}
onPress={onPress}
style={[
styles.button,
style,
color && { backgroundColor: color },
disabled && styles.buttonDisabled
]}
testID={testID}
>
<Text style={[styles.text, textStyle, disabled && styles.textDisabled]}>{title}</Text>
</TouchableHighlight>
);
}
}
const styles = StyleSheet.create({
button: {
backgroundColor: '#2196F3',
borderRadius: 0,
justifyContent: 'center'
},
text: {
color: '#fff',
fontWeight: '500',
padding: 8,
textAlign: 'center',
textTransform: 'uppercase'
},
buttonDisabled: {
backgroundColor: '#dfdfdf'
},
textDisabled: {
color: '#a1a1a1'
}
});

View File

@@ -0,0 +1,55 @@
import React, { Fragment } from 'react';
import { createElement, StyleSheet, Text } from 'react-native';
const styles = StyleSheet.create({
root: {
display: 'inline-block',
fill: 'currentcolor',
height: '1.25em',
maxWidth: '100%',
position: 'relative',
userSelect: 'none',
textAlignVertical: 'text-bottom'
}
});
const createIcon = children => {
const Icon = props =>
createElement(
'svg',
{
style: StyleSheet.compose(styles.root, props.style),
width: 24,
height: 24,
viewBox: '0 0 24 24'
},
children
);
Icon.propTypes = {
style: Text.propTypes.style
};
return Icon;
};
export const IconClear = createIcon(
<Fragment>
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
<path d="M12 1.25C6.072 1.25 1.25 6.072 1.25 12S6.072 22.75 12 22.75 22.75 17.928 22.75 12 17.928 1.25 12 1.25zm0 1.5c2.28 0 4.368.834 5.982 2.207L4.957 17.982C3.584 16.368 2.75 14.282 2.75 12c0-5.1 4.15-9.25 9.25-9.25zm0 18.5c-2.28 0-4.368-.834-5.982-2.207L19.043 6.018c1.373 1.614 2.207 3.7 2.207 5.982 0 5.1-4.15 9.25-9.25 9.25z" />
</Fragment>
);
export const IconEye = createIcon(
<Fragment>
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
<path d="M14.548 11.634c-1.207 0-2.188-.98-2.188-2.188 0-.664.302-1.25.77-1.653-.363-.097-.736-.165-1.13-.165-2.416 0-4.375 1.96-4.375 4.376S9.585 16.38 12 16.38c2.418 0 4.377-1.96 4.377-4.376 0-.4-.07-.78-.17-1.146-.402.47-.992.776-1.66.776z" />
<path d="M12 19.79c-7.228 0-10.12-6.724-10.24-7.01-.254-.466-.254-1.105.035-1.642C1.88 10.923 4.772 4.2 12 4.2s10.12 6.723 10.24 7.01c.254.465.254 1.104-.035 1.64-.085.216-2.977 6.94-10.205 6.94zm0-14c-6.154 0-8.668 5.787-8.772 6.033-.068.135-.068.208-.033.273.137.316 2.65 6.104 8.805 6.104 6.18 0 8.747-5.973 8.772-6.033.07-.136.07-.21.034-.274-.138-.316-2.652-6.103-8.806-6.103z" />
</Fragment>
);
export const IconCopy = createIcon(
<Fragment>
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
<path d="M11.47 14.53c.146.146.338.22.53.22s.384-.073.53-.22l5-5c.293-.293.293-.768 0-1.06s-.768-.294-1.06 0l-3.72 3.72V2c0-.414-.337-.75-.75-.75s-.75.336-.75.75v10.19L7.53 8.47c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l5 5z" />
<path d="M21.25 13.25c-.414 0-.75.336-.75.75v5.652c0 .437-.355.792-.792.792H4.292c-.437 0-.792-.355-.792-.792V14c0-.414-.336-.75-.75-.75S2 13.586 2 14v5.652c0 1.264 1.028 2.292 2.292 2.292h15.416c1.264 0 2.292-1.028 2.292-2.292V14c0-.414-.336-.75-.75-.75z" />
</Fragment>
);

View File

@@ -0,0 +1,68 @@
import { colors } from './theme';
import { element } from 'prop-types';
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
export default class Layout extends Component {
static propTypes = {
actionPanel: element,
listPanel: element,
viewPanel: element
};
state = {
widescreen: false
};
render() {
const { viewPanel, actionPanel, listPanel } = this.props;
const { widescreen } = this.state;
return (
<View onLayout={this._handleLayout} style={[styles.root, widescreen && styles.row]}>
<View style={[widescreen ? styles.grow : styles.stackPanel, styles.layer]}>
{viewPanel}
</View>
<View style={styles.grow}>
<View style={[styles.grow, styles.layer]}>{listPanel}</View>
<View style={styles.divider} />
<View style={styles.layer}>{actionPanel}</View>
</View>
</View>
);
}
_handleLayout = ({ nativeEvent }) => {
const { layout } = nativeEvent;
const { width } = layout;
if (width >= 740) {
this.setState(() => ({ widescreen: true }));
} else {
this.setState(() => ({ widescreen: false }));
}
};
}
const styles = StyleSheet.create({
root: {
height: '100%'
},
row: {
flexDirection: 'row'
},
divider: {
height: 10,
backgroundColor: colors.fadedGray,
borderBottomWidth: 1,
borderTopWidth: 1,
borderColor: colors.mediumGray
},
grow: {
flex: 1
},
stackPanel: {
height: '33.33%'
},
layer: {
transform: [{ translateZ: '0' }]
}
});

View File

@@ -0,0 +1,83 @@
/* eslint-disable react/prop-types */
import Text from './Text';
import { StyleSheet, View } from 'react-native';
import React, { Fragment } from 'react';
const fmt = (time: number) => {
const i = Number(Math.round(time + 'e2') + 'e-2').toFixed(2);
return 10 / i > 1 ? `0${i}` : i;
};
class ReportCard extends React.PureComponent {
render() {
const {
benchmarkName,
libraryName,
sampleCount,
mean,
meanLayout,
meanScripting,
stdDev,
libraryVersion
} = this.props;
const sampleCountText = sampleCount != null ? `(${sampleCount})` : '';
return (
<View style={styles.root}>
<View style={styles.left}>
<Text numberOfLines={1} style={styles.bold}>
{`${libraryName}${libraryVersion ? '@' + libraryVersion : ''}`}
</Text>
<Text numberOfLines={1}>
{benchmarkName} {sampleCountText}
</Text>
</View>
<View style={styles.right}>
{mean ? (
<Fragment>
<Text style={[styles.bold, styles.monoFont]}>
{fmt(mean)} ±{fmt(stdDev)} ms
</Text>
<Text style={[styles.smallText, styles.monoFont]}>
(S/L) {fmt(meanScripting)} / {fmt(meanLayout)} ms
</Text>
</Fragment>
) : (
<Text style={styles.bold}>In progress</Text>
)}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
flexDirection: 'row',
alignItems: 'center',
padding: 5,
borderBottomWidth: 1,
borderBottomColor: '#eee'
},
bold: {
fontWeight: 'bold'
},
smallText: { fontSize: 12 },
monoFont: {
fontFamily: 'monospace'
},
centerText: {
display: 'flex',
alignItems: 'center'
},
left: {
width: '50%'
},
right: {
flex: 1,
alignItems: 'flex-end'
}
});
export default ReportCard;

View File

@@ -0,0 +1,30 @@
/* eslint-disable react/prop-types */
import { bool } from 'prop-types';
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import { colors } from './theme';
class AppText extends React.Component {
static displayName = '@app/Text';
static contextTypes = {
isInAParentText: bool
};
render() {
const { style, ...rest } = this.props;
const { isInAParentText } = this.context;
return <Text {...rest} style={[!isInAParentText && styles.baseText, style]} />;
}
}
const styles = StyleSheet.create({
baseText: {
color: colors.textBlack,
fontSize: '1rem',
lineHeight: '1.3125em'
}
});
export default AppText;

View File

@@ -0,0 +1,101 @@
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import { Dimensions, Platform } from 'react-native';
const baseFontSize = 14;
const baseUnit = 1.3125;
const createPlatformLength = multiplier =>
Platform.select({ web: `${multiplier}rem`, default: multiplier * baseFontSize });
/**
* Exported variables
*/
export const borderRadii = {
normal: Platform.select({ web: '0.35rem', default: 5 }),
infinite: '9999px'
};
export const breakpoints = {
small: 360,
medium: 600,
large: 800,
xLarge: 1100
};
/**
* Color palette
* DO NOT add new colors unless they are part of @design's color palette.
* DO NOT use colors that are not specified here.
* source: go/uicolors
*/
export const colors = {
// Primary
blue: '#1DA1F2',
purple: '#794BC4',
green: '#17BF63',
yellow: '#FFAD1F',
orange: '#F45D22',
red: '#E0245E',
// Text and interface grays
textBlack: '#14171A',
deepGray: '#657786',
mediumGray: '#AAB8C2',
lightGray: '#CCD6DD',
fadedGray: '#E6ECF0',
faintGray: '#F5F8FA',
white: '#FFF',
textBlue: '#1B95E0'
};
export const fontFamilies = {
normal: 'System',
japan: Platform.select({
web:
'Arial, "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, " Pゴシック", "MS PGothic", sans-serif',
default: 'System'
}),
rtl: Platform.select({ web: 'Tahoma, Arial, sans-serif', default: 'System' })
};
export const fontSizes = {
// font scale
small: createPlatformLength(0.85),
normal: createPlatformLength(1),
large: createPlatformLength(1.25),
xLarge: createPlatformLength(1.5),
jumbo: createPlatformLength(2)
};
export const lineHeight = Platform.select({ web: `${baseUnit}` });
export const spaces = {
// This set of space variables should be used for margin, padding
micro: createPlatformLength(baseUnit * 0.125),
xxSmall: createPlatformLength(baseUnit * 0.25),
xSmall: createPlatformLength(baseUnit * 0.5),
small: createPlatformLength(baseUnit * 0.75),
medium: createPlatformLength(baseUnit),
large: createPlatformLength(baseUnit * 1.5),
xLarge: createPlatformLength(baseUnit * 2),
xxLarge: createPlatformLength(baseUnit * 2.5),
jumbo: createPlatformLength(baseUnit * 3)
};
// On web, change the root font-size at specific breakpoints to scale the UI
// for larger viewports.
if (Platform.OS === 'web' && canUseDOM) {
const { medium, large } = breakpoints;
const htmlElement = document.documentElement;
const setFontSize = width => {
const fontSize = width > medium ? (width > large ? '18px' : '17px') : '16px';
if (htmlElement) {
htmlElement.style.fontSize = fontSize;
}
};
setFontSize(Dimensions.get('window').width);
Dimensions.addEventListener('change', dimensions => {
setFontSize(dimensions.window.width);
});
}

View File

@@ -0,0 +1,90 @@
import { BenchmarkType } from 'react-component-benchmark';
import { number, object } from 'prop-types';
import React from 'react';
import { interpolatePurples, interpolateBuPu, interpolateRdPu } from 'd3-scale-chromatic';
const targetSize = 10;
class SierpinskiTriangle extends React.Component {
static displayName = 'SierpinskiTriangle';
static benchmarkType = BenchmarkType.UPDATE;
static propTypes = {
components: object,
depth: number,
renderCount: number,
s: number,
x: number,
y: number
};
static defaultProps = {
depth: 0,
renderCount: 0
};
render() {
const { components, x, y, depth, renderCount } = this.props;
let { s } = this.props;
const { Dot } = components;
if (Dot) {
if (s <= targetSize) {
let fn;
switch (depth) {
case 1:
fn = interpolatePurples;
break;
case 2:
fn = interpolateBuPu;
break;
case 3:
default:
fn = interpolateRdPu;
}
// introduce randomness to ensure that repeated runs don't produce the same colors
const color = fn(renderCount * Math.random() / 20);
return (
<Dot color={color} size={targetSize} x={x - targetSize / 2} y={y - targetSize / 2} />
);
}
s /= 2;
return (
<React.Fragment>
<SierpinskiTriangle
components={components}
depth={1}
renderCount={renderCount}
s={s}
x={x}
y={y - s / 2}
/>
<SierpinskiTriangle
components={components}
depth={2}
renderCount={renderCount}
s={s}
x={x - s}
y={y + s / 2}
/>
<SierpinskiTriangle
components={components}
depth={3}
renderCount={renderCount}
s={s}
x={x + s}
y={y + s / 2}
/>
</React.Fragment>
);
} else {
return <span style={{ color: 'white' }}>No implementation available</span>;
}
}
}
export default SierpinskiTriangle;

View File

@@ -0,0 +1,45 @@
import { BenchmarkType } from 'react-component-benchmark';
import { number, object } from 'prop-types';
import React, { Component } from 'react';
class Tree extends Component {
static displayName = 'Tree';
static benchmarkType = BenchmarkType.MOUNT;
static propTypes = {
breadth: number.isRequired,
components: object,
depth: number.isRequired,
id: number.isRequired,
wrap: number.isRequired
};
render() {
const { breadth, components, depth, id, wrap } = this.props;
const { Box } = components;
let result = (
<Box color={id % 3} layout={depth % 2 === 0 ? 'column' : 'row'} outer>
{depth === 0 && <Box color={id % 3 + 3} fixed />}
{depth !== 0 &&
Array.from({ length: breadth }).map((el, i) => (
<Tree
breadth={breadth}
components={components}
depth={depth - 1}
id={i}
key={i}
wrap={wrap}
/>
))}
</Box>
);
for (let i = 0; i < wrap; i++) {
result = <Box>{result}</Box>;
}
return result;
}
}
export default Tree;

View File

@@ -0,0 +1,36 @@
import { type Component } from 'react';
import packageJson from '../package.json';
const context = require.context('./implementations/', true, /index\.js$/);
const { dependencies } = packageJson;
type ComponentsType = {
Box: Component,
Dot: Component,
Provider: Component,
View: Component
};
type ImplementationType = {
components: ComponentsType,
name: string,
version: string
};
const toImplementations = (context: Object): Array<ImplementationType> =>
context.keys().map(path => {
const components = context(path).default;
const name = path.split('/')[1];
const version = dependencies[name] || '';
return { components, name, version };
});
const toObject = (impls: Array<ImplementationType>): Object =>
impls.reduce((acc, impl) => {
acc[impl.name] = impl;
return acc;
}, {});
const map = toObject(toImplementations(context));
export default map;

View File

@@ -1,9 +1,9 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from '../View/aphrodite';
import View from './View';
import { StyleSheet } from 'aphrodite';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
style={[
@@ -12,37 +12,39 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
layout === 'row' && styles.row,
outer && styles.outer
]}
/>;
/>
);
const styles = StyleSheet.create({
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: 'orange'
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: 'red'
backgroundColor: '#E0245E'
},
fixed: {
width: 20,
height: 20
width: 6,
height: 6
}
});
module.exports = Box;
export default Box;

View File

@@ -0,0 +1,2 @@
import View from './View';
export default View;

View File

@@ -28,4 +28,4 @@ const styles = StyleSheet.create({
}
});
module.exports = View;
export default View;

View File

@@ -0,0 +1,9 @@
import Box from './Box';
import Provider from './Provider';
import View from './View';
export default {
Box,
Provider,
View
};

View File

@@ -1,10 +1,10 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import View from '../View/css-modules';
import styles from './styles.css';
import View from './View';
import styles from './box-styles.css';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
className={classnames(styles[`color${color}`], {
@@ -12,6 +12,7 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
[styles.outer]: outer,
[styles.row]: layout === 'row'
})}
/>;
/>
);
module.exports = Box;
export default Box;

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