Compare commits

...

152 Commits

Author SHA1 Message Date
Nicolas Gallagher
b7c72308ea 0.0.80 2017-03-22 23:43:37 -07:00
Nicolas Gallagher
5fee075774 Add 'processColor' tests 2017-03-22 23:40:40 -07:00
Nicolas Gallagher
25204eeff0 [fix] convert color values to CSS color
Convert all hex and numeric colors to rgba. Assume non-hex strings are
valid CSS colors.
2017-03-22 23:15:42 -07:00
Nicolas Gallagher
9c61fe58d3 [add] View 'hitSlop' shim
Shim the 'hitSlop' prop using a positioned element to extend the size of
a View's touch target without changing layout. Unlike the native
implementation, the touch target may extend past the parent view bounds.
2017-03-22 23:01:53 -07:00
Nicolas Gallagher
782125d169 Remove pointerEvents code from View 2017-03-20 22:53:57 -07:00
Nicolas Gallagher
af805d67e6 0.0.79 2017-03-20 22:42:00 -07:00
Nicolas Gallagher
68068f8cb6 [fix] support React Native props in 'setNativeProps'
React Native allows props like 'pointerEvents' to be set using
'setNativeProps'.

Fix #392
2017-03-20 22:33:59 -07:00
Nicolas Gallagher
e05e2122d7 [fix] avoid setting empty style objects 2017-03-20 22:33:59 -07:00
Nicolas Gallagher
47dac44120 [fix] filter 'lineBreakMode' from Text props 2017-03-20 22:33:53 -07:00
Nicolas Gallagher
22af6894c2 Update jest 2017-03-20 22:18:50 -07:00
Nicolas Gallagher
458c534200 [change] improve button accessibility and styling
1. If no 'accessibilityRole' is set, fallback to looking for 'button'
role in the equivalent native props. This helps improve accessibility of
button-like components authored without the web platform in mind.

2. Ensure button context is properly inherited.

3. Add 'appearance:none' to DOM button elements to enable better styling
support in Safari

Fix #378
2017-03-20 14:50:01 -07:00
Nicolas Gallagher
ec2db3e2a3 0.0.78 2017-03-19 18:45:21 -07:00
Bruno Lemos
e6f00f7592 [add] default option for Platform.select
See: f30ab35e92
2017-03-19 15:27:07 -07:00
Nicolas Gallagher
976320916e [change] move bridge code into createDOMElement
Moves event normalization and the ResponderEventPlugin injection from
'View' to 'createDOMElement'.

The 'react-native-web/lite' variant is removed from the performance
directory as the implementation is not substantially different.
Micro-optimizations to marginally narrow the performance gap to
css-modules.
2017-03-19 15:19:26 -07:00
Nicolas Gallagher
808790505e [change] onLayout improvements
1. Fires when window resizes
2. Guards against nodes being unmounted

Fix #397
Ref #60
2017-03-19 13:29:47 -07:00
Nicolas Gallagher
89ad493ce5 Update benchmark results 2017-03-14 17:23:16 -07:00
Nicolas Gallagher
c4f2869ad8 0.0.77 2017-03-09 18:20:07 -08:00
Nicolas Gallagher
3ae1e5b786 [fix] don't run 'modality' without a DOM
Fix #387
2017-03-09 18:13:00 -08:00
Nicolas Gallagher
e929fb572d [change] add 'testID' prop to 'Button'
Close #386
2017-03-09 18:09:27 -08:00
Nicolas Gallagher
5af9d537c2 Fix stylesheet snapshots 2017-03-06 12:56:50 -08:00
Nicolas Gallagher
b7d3f0d099 0.0.76 2017-03-06 12:31:41 -08:00
Nicolas Gallagher
f1ca00a13a [fix] ProgressBar animation styles
Fix #384
2017-03-06 12:30:34 -08:00
Aaron Craig
9451c0f85e [fix] avoid passing ListView props to ScrollView
The 'onChangeVisibleRows' and 'onEndReachedThreshold' props should not
be passed to 'ScrollView', as it results in React warnings about unknown
props on the underlying DOM node.

Close #383
2017-03-05 09:21:10 -08:00
Nicolas Gallagher
b408bc5537 Use a package for style name hyphenation
The 'hyphenate-style-name' packages is already a dependency of
'inline-style-prefixer', so we don't need our own implementation.
2017-03-02 19:10:05 -08:00
Nicolas Gallagher
a2f25a46c4 Reformat 'performance' and 'src' code 2017-03-02 18:59:33 -08:00
Nicolas Gallagher
29d52f5b31 Remove formatting rules from eslint config 2017-03-02 18:59:32 -08:00
Nicolas Gallagher
ba6be1f64a Install prettier code formatter 2017-03-02 18:59:32 -08:00
Nicolas Gallagher
43f78828a5 Update benchmarks to use styled-components@2.0.0-5
No significant change in the benchmark results
2017-03-02 18:57:57 -08:00
Nicolas Gallagher
26bc8173f0 0.0.75 2017-02-27 23:11:30 -08:00
Nicolas Gallagher
ecae52ccc5 [change] Touchable pass through props
Add support for web-specific props to improve accessibility, e.g., ARIA
properties (and declarative links).

Fix #65
2017-02-27 17:05:38 -08:00
Nicolas Gallagher
997653863f [fix] ignore unsupported style prop types
Fix #347
Close #371
2017-02-27 16:40:54 -08:00
Nicolas Gallagher
5dc692719f Update snapshots after StyleSheet refactor 2017-02-27 15:59:18 -08:00
Nicolas Gallagher
0361845537 [change] StyleSheet refactor
* HTML class names are now hashes of the corresponding declaration
* Simplifies 'setNativeProps' logic
* Fixes use of server-rendered style sheet
* Fixes duplicate insertion of style sheets with hot-reloading

No significant change to the benchmark results
2017-02-27 15:59:18 -08:00
Nicolas Gallagher
f391031fb1 0.0.74 2017-02-27 14:55:34 -08:00
Matthias Le Brun
77799abf9b [fix] Touchable support for 'Enter' keypress event
Close #375
2017-02-27 14:53:27 -08:00
Nicolas Gallagher
2cfd09ecdb [fix] server-side rendering
e1fc253 added deferred image loading but didn't guard the
'requestIdleFallback' shim for use in Node.js

Fix #376
2017-02-27 14:50:29 -08:00
Nicolas Gallagher
89eea2b366 Remove unused style in 'View' 2017-02-27 14:41:47 -08:00
Nicolas Gallagher
18440158b3 Add comparative performance benchmarks
Includes a 'css-modules' implementation of the nested trees to act as a
baseline for comparison.
2017-02-26 17:19:43 -08:00
Nicolas Gallagher
24eda7c4ad 0.0.73 2017-02-24 12:35:18 -08:00
Nicolas Gallagher
44ebd8f5a3 [change] modality-specific focus styles
Remove the default focus ring when the keyboard is not being used. This
provides a superior UX when using touch or mouse.

Fix #310
2017-02-18 13:39:25 -08:00
Matthias Le Brun
a3ed8f05e6 [add] resize 'TextInput' style
Fix #363
2017-02-18 12:49:25 -08:00
Nicolas Gallagher
b653fe0bd3 [add] fontFeatureSettings 'Text' style
Fix #331
2017-02-18 12:18:28 -08:00
Nicolas Gallagher
30da226e4d Update component style docs 2017-02-18 12:07:30 -08:00
Nicolas Gallagher
f1f39bfabd 0.0.72 2017-02-18 11:34:33 -08:00
Nicolas Gallagher
267c5aab7e Allow benchmark to run in Safari
Performing teardown in a new frame was causing Safari to hang on the 2nd
benchmark run.
2017-02-18 11:34:33 -08:00
Nicolas Gallagher
fe71c7efe5 [fix] Image browser context menu
Fix #368
2017-02-18 11:34:28 -08:00
Nicolas Gallagher
eb59875bed [change] defer Image loading
x4 faster render benchmark
2017-02-18 09:46:10 -08:00
Nicolas Gallagher
e1fc253277 Add image benchmark 2017-02-18 09:46:10 -08:00
Nicolas Gallagher
40b03aca52 [change] improve 'View' render performance
1. Register the 'pointerEvents' styles to enable memoization
2. Don't flatten styles in render; move flex reset to 'expandStyle'

Reduces benchmark render times by ~10% on early 2011 MacBook Pro
2017-02-18 09:46:04 -08:00
Nicolas Gallagher
418a1a9516 [change] depend on animated@0.2.0 2017-02-17 13:28:51 -08:00
Nicolas Gallagher
8762f8e9c8 [change] depend on inline-style-prefixer@3.0.0 2017-02-17 13:28:49 -08:00
Nicolas Gallagher
10ef2bfe20 [fix] i18n styles
1. Fix auto-flipping of styles

The StyleRegistry didn't account for LTR/RTL when caching the results of
style resolution. The 'writingDirection' style is no longer flipped; no
clear use case for it.

2. Remove experimental '$noI18n' style prop suffix

This feature is essentially unused, and less likely to be used with the
introduction of 'dir=auto' on 'Text'. Removing also marginally improves
render performance.
2017-02-17 13:25:54 -08:00
Nicolas Gallagher
6d2ae4597e Update babel packages 2017-02-17 12:04:30 -08:00
Nicolas Gallagher
a34b8b149f [fix] V8 deopt in compiled 'createDOMElement'
Fixes V8 "deopt" warning: "Bad value context for arguments value".

This deopt was caused by the babel-compiled output of the ES6 argument
default value for 'rnProps':

    var t = arguments.length > 1 && void 0 !== arguments[1]
      ? arguments[1]
      : l

Not relying on ES6 default arguments avoids the function 'deopt'.
2017-02-17 10:23:59 -08:00
Nicolas Gallagher
6166024d15 [fix] NODE_ENV check in 'flattenStyle' 2017-02-17 09:59:56 -08:00
Nicolas Gallagher
701ecb7c52 0.0.71 2017-02-17 08:54:33 -08:00
Li Hau Tan
75042093c2 [fix] add 'State' static to 'TextInput'
Fix #365
Close #366
2017-02-17 08:51:22 -08:00
Nathan Tran
bb417900a9 [add] willChange style prop type
Close #367
2017-02-17 08:44:12 -08:00
Nicolas Gallagher
89e0a15d1b [fix] add 'timeStamp' to scroll and layout events 2017-02-16 08:18:56 -08:00
Nicolas Gallagher
b2e0a3702f [fix] Linking methods return promises 2017-02-11 14:13:25 -08:00
Rodrigo Moyle
a4644c204d [fix] add shadow style props to Image styles
Fix #357
Close #358
2017-02-11 11:21:45 -08:00
Nicolas Gallagher
1e9536b611 0.0.70 2017-02-06 17:15:43 -08:00
Nicolas Gallagher
d15dafc108 Build UMD bundle from source 2017-02-06 13:14:48 -08:00
Nicolas Gallagher
c9c1aab97e Rearrange propType imports 2017-02-05 16:50:06 -08:00
Nicolas Gallagher
a2903f9d30 Move TextPropTypes to TextStylePropTypes 2017-02-05 16:37:54 -08:00
Nicolas Gallagher
c7771ac64f [change] avoid 'react-dom/lib/*' where possible 2017-02-05 16:36:26 -08:00
Nicolas Gallagher
c8129c2a99 Remove NODE_ENV wrappers 2017-02-05 16:35:04 -08:00
Matthias Le Brun
b793737393 [fix] onLayout calculation
The 0.0.69 release introduced a regression in UIManager's measurement
calculations.

Using `offset` properties returns the offset relative to the closest
positioned ancestor. Make `getRect` iterate over the ancestor chain.

Fix #341
Close #345
2017-02-05 12:09:42 -08:00
Nicolas Gallagher
2a4d1c81d8 0.0.69 2017-01-28 11:01:23 -08:00
Nicolas Gallagher
a8a25d66ea [fix] measure CSS layout independent of transforms
Fix #332
2017-01-28 10:37:49 -08:00
Gethin Webster
e06d7a9650 [fix] Prevent props warnings from ScrollView in ListView 2017-01-28 10:01:16 -08:00
Nicolas Gallagher
c2501f2bc2 Add documentation for webpack@2 *.web.js resolving
Fix #334
2017-01-28 09:57:14 -08:00
Nicolas Gallagher
c51e7f1965 [fix] Linking method names
Fix #339
2017-01-28 09:50:15 -08:00
Nicolas Gallagher
dfff6b3780 [fix] StyleSheet resolving for 'number' style 2017-01-16 14:36:20 -08:00
Nicolas Gallagher
5f6b4a746a Update webpack-bundler-analyzer 2017-01-13 13:21:23 -08:00
Nicolas Gallagher
f077907dd4 Fix AppRegistry/renderApplication snapshot 2017-01-11 13:15:07 -08:00
Nicolas Gallagher
a94367bdcb 0.0.68 2017-01-11 13:12:25 -08:00
Nicolas Gallagher
65febbbc52 [fix] error in setNativeProps when no style
Close #325
Close #327
2017-01-11 13:08:10 -08:00
Nicolas Gallagher
b14d2e5bd8 [fix] CSS pointer event selectors 2017-01-11 12:47:24 -08:00
Nicolas Gallagher
7c83ba162d 0.0.67 2017-01-08 18:40:02 -08:00
Nicolas Gallagher
3ffc005a7b [fix] setNativeProps resolving logic
Since styles are set using both class names and inline styles,
'setNativeProps' needs an additional resolving step that accounts for
the pre-existing state of RN-managed styles on the DOM node.

Fix #321
2017-01-08 18:25:39 -08:00
Nicolas Gallagher
50a70ad02f 0.0.66 2017-01-07 19:05:54 -08:00
Nicolas Gallagher
768e895701 [fix] View transforms; add perspective styles
Fixes a regression introduced by
5db300df35

The `perspective` function is distinct from the `perspective` property.
This patch reverts the regression and adds support for `perspective`,
`perspectiveOrigin`, and `transformOrigin`.

Fix #208
2017-01-07 19:02:57 -08:00
Nicolas Gallagher
af5fde994d Fix yarn.lock 2017-01-07 18:25:54 -08:00
Paul Le Cam
c3d0763944 [fix] ListView imports and 'getRowAndSectionCount'
Close #316
2017-01-07 18:22:47 -08:00
Nicolas Gallagher
0aba506725 Fix UIManager tests 2017-01-07 18:18:56 -08:00
Nicolas Gallagher
91032d8565 [change] allow 'display' in ViewStylePropTypes
Fix #296
2017-01-07 18:15:16 -08:00
Nicolas Gallagher
0696721488 Document transition and animation View styles 2017-01-07 18:12:26 -08:00
Nicolas Gallagher
fe18830ce6 [change] use classList in UIManager
Prevent setting the same class multiple times
2017-01-07 18:03:51 -08:00
Nicolas Gallagher
1b86d02300 [change] wrap layout measurement in 'asap' 2017-01-07 18:03:03 -08:00
Nicolas Gallagher
c56b472258 [change] depend on normalize-css-color
Fix #308
2017-01-07 18:00:17 -08:00
Nicolas Gallagher
b00132f007 [fix] TouchableOpacity transition duration 2017-01-07 17:50:59 -08:00
Nicolas Gallagher
8b8f8f0374 [fix] CSS properties that support unitless numbers 2017-01-05 14:47:10 -08:00
Nicolas Gallagher
8e94af34e1 Remove use of 'keyOf' 2017-01-05 14:47:10 -08:00
Nicolas Gallagher
7ffaf592d5 [fix] Text automatic writing direction detection 2017-01-05 14:47:08 -08:00
Nicolas Gallagher
a1017fa785 0.0.65 2017-01-04 18:25:39 -08:00
Nicolas Gallagher
5db300df35 [fix] transform perspective resolution 2017-01-04 18:04:15 -08:00
Nicolas Gallagher
214d862e61 [add] ScrollView to Animated 2017-01-04 18:04:05 -08:00
Nicolas Gallagher
4ef5453b33 0.0.64 2017-01-04 10:58:15 -08:00
Nicolas Gallagher
a27671d7cf [fix] passing on RN style props in createDOMElement
The 'createDOMElement' function wasn't pulling 'style' out of the
props. A change to the logic that sets DOM props meant that if
'StyleRegistry.resolve' didn't return a 'style' object, the React Native
styles would be passed through to the underlying DOM node.

Fix #315
2017-01-04 10:43:19 -08:00
Nicolas Gallagher
8d2a650670 [fix] StyleSheet selector escaping
Values that contain '*' (e.g. 'calc(2 * 2)') were not properly escaped,
resulting in broken selectors.
2017-01-03 14:25:39 -08:00
Nicolas Gallagher
4cc1c983e8 0.0.63 2017-01-02 23:44:10 -08:00
Nicolas Gallagher
37413fd55f Update Text snapshot 2017-01-02 23:43:39 -08:00
Nicolas Gallagher
07ff0ea104 Use setNativeProps to update ProgressBar 2017-01-02 23:28:14 -08:00
Nicolas Gallagher
1a87657500 [fix] Switch thumb positioning 2017-01-02 23:25:15 -08:00
Nicolas Gallagher
5e4c8e520a [fix] StyleSheet registry key check 2017-01-02 23:24:24 -08:00
Nicolas Gallagher
236121e32c Add unregistered styles benchmark 2017-01-02 22:46:38 -08:00
Nicolas Gallagher
39c76ca50c Minor StyleSheet/injector refactor; small fixes 2017-01-02 15:07:05 -08:00
Nicolas Gallagher
e65f91d849 [change] simplify CSS generation 2017-01-02 13:15:38 -08:00
Nicolas Gallagher
a535c558d8 [change] Move 'onResponderRelease' cancel to 'Touchable'
Moves a fix for double-firing Touchables into 'Touchable'
2017-01-02 13:12:13 -08:00
Nicolas Gallagher
a74be91b7c Minor documentation changes 2017-01-02 11:27:46 -08:00
Nicolas Gallagher
8eaaf28a32 0.0.62 2017-01-01 20:45:45 -08:00
Nicolas Gallagher
16d448dc5b Update various dependencies 2017-01-01 20:45:14 -08:00
Nicolas Gallagher
ea75cced13 [change] ProgressBar using CSS animations
Fix #299
2017-01-01 20:22:42 -08:00
Nicolas Gallagher
cfc56a1354 [change] ActivityIndicator using CSS animations
Ref #299
2017-01-01 19:48:50 -08:00
Nicolas Gallagher
b1cd92a65d [change] update Animated
Fix #309
2017-01-01 18:35:18 -08:00
Nicolas Gallagher
d87f71ebc1 [change] StyleSheet performance rewrite
Improves StyleSheet benchmark performance by 13x. Removes undocumented
`StyleSheet.resolve` API.

Typical mean (and first) task duration.

[benchmark] DeepTree: depth=3, breadth=10, wrap=4)
  -master  2809.76ms (3117.64ms)
  -patch     211.2ms  (364.28ms)

[benchmark] DeepTree: depth=5, breadth=3, wrap=1)
  -master   421.25ms (428.15ms)
  -patch     32.46ms  (47.36ms)

This patch adds memoization of DOM prop resolution (~3-4x faster), and
re-introduces a `className`-based styling strategy (~3-4x faster).
Styles map to "atomic css" rules.

Fix #307
2017-01-01 17:42:25 -08:00
Nicolas Gallagher
a2cafe56fc Add initial performance benchmarks
Simple render tree benchmarks originally developed by @lelandrichardson

Fix #306
2017-01-01 14:43:47 -08:00
Nicolas Gallagher
351c0ac3d4 Minor changes 2016-12-30 22:19:51 -08:00
Nicolas Gallagher
877c0d2818 Simplify StyleSheet's expandStyle 2016-12-30 22:14:39 -08:00
Nicolas Gallagher
3afc5d5de6 Rename style processors to resolvers 2016-12-29 19:17:12 -08:00
Nicolas Gallagher
edf3b9b7ff Move 'flattenStyle' into 'StyleSheet' 2016-12-29 16:31:14 -08:00
Nicolas Gallagher
518a85bf1b Rename 'createReactStyleObject' to 'createReactDOMStyle' 2016-12-29 16:26:31 -08:00
vaukalak
ba75acb66a [add] BackAndroid API stub 2016-12-27 18:14:31 +00:00
Nicolas Gallagher
bc68b0b6f4 0.0.61 2016-12-27 18:09:11 +00:00
Nicolas Gallagher
44ecbc072e [change] update React and Touchables
Update to React@15.4. The 'EventConstants' module no longer exports a
key-mirror, which was preventing the 'ResponderEventPlugin' from working
as it did with React@15.3.

Close #255
2016-12-27 17:57:27 +00:00
Nicolas Gallagher
4cf4905fc2 [change] add support for ShadowPropTypes
Fix #44
2016-12-26 13:57:19 +00:00
Nicolas Gallagher
509920be4b [add] Image 'prefetch' and 'getSize' statics
Fix #160
2016-12-26 13:31:40 +00:00
Gethin Webster
04e3c23e67 [fix] ListView updates rows when dataSource changes
Close #295
2016-12-23 12:51:40 +00:00
Nicolas Gallagher
32f454de66 [change] add Platform and Touchable to 'core' module 2016-12-23 12:45:54 +00:00
Nicolas Gallagher
1273bfc7cf Address avoidable object creation 2016-12-23 12:22:32 +00:00
Nicolas Gallagher
dc7f526f6b [fix] TextInput props
- Add missing 'onSubmitEditing' propType and test
- Add 'dir=auto' DOM attribute to allow browser to switch writing
  direction for RTL languages
2016-12-17 23:38:36 +00:00
Nicolas Gallagher
7cda89c5ce 0.0.60 2016-12-16 12:15:23 +00:00
Nicolas Gallagher
695eba45af [add] Clipboard API
Close #125
Fix #122
2016-12-16 11:59:22 +00:00
Nicolas Gallagher
92a2cb274a [fix] remove TextInput default flex value 2016-12-16 11:39:12 +00:00
Nicolas Gallagher
b1ca04d11e Rename I18nManager example 2016-12-16 11:35:11 +00:00
Nicolas Gallagher
22ab70ea6f 0.0.59 2016-12-14 17:42:15 +00:00
Gethin Webster
49f36d8eb1 Update to ListView functionality
Re-build ListView from the core react-native component, to get better
feature parity

Ensure lists with small initialListSize render correctly

Changes as requested via PR
2016-12-14 09:39:05 -08:00
Nicolas Gallagher
80ba119b83 Update install command
Close #285
Close #286
2016-12-14 17:05:15 +00:00
Nicolas Gallagher
c30b67f6db 0.0.57 2016-12-12 15:10:39 +00:00
Nicolas Gallagher
4580f93199 Use fbjs requestAnimationFrame in Image 2016-12-12 14:26:07 +00:00
Nicolas Gallagher
4c46126ffe [change] ScrollView event normalization 2016-12-12 14:21:33 +00:00
Calvin Chan
f8d5c15405 [add] Button component 2016-12-12 13:02:16 +00:00
Nicolas Gallagher
dc54e03625 [add] Linking API
Adds support for opening external URLs in a new tab/window. Includes
patches to 'Text' to improve accessibility and 'createDOMElement' to
improve external link security.

Fix #198
2016-12-12 11:45:30 +00:00
Nicolas Gallagher
4d5819ae28 [fix] RTL translateX; Switch transition 2016-12-08 19:40:34 -08:00
Nicolas Gallagher
5c482ef3be 0.0.56 2016-12-08 18:29:38 -08:00
Nicolas Gallagher
f51592f96e [change] TouchableOpacity without Animated
Fix #259
2016-12-08 18:22:25 -08:00
Nicolas Gallagher
6bffe1775f Fix lint error 2016-12-07 16:49:53 -08:00
Nicolas Gallagher
7e75d037f2 [fix] Image passes unknown props to underlying View
Fix #267
2016-12-07 16:37:24 -08:00
Nicolas Gallagher
7536508fe3 Update docs 2016-12-07 16:22:39 -08:00
Maxime Thirouin
945fff0015 Add source files to published package 2016-11-25 12:38:29 -08:00
Nicolas Gallagher
5032ed6fe1 Update AppRegistry docs 2016-11-25 12:36:05 -08:00
208 changed files with 7399 additions and 5441 deletions

View File

@@ -9,6 +9,10 @@
},
"sourceType": "module"
},
"extends": [
"prettier",
"prettier/react"
],
"plugins": [
"jsx-a11y",
"promise",
@@ -24,31 +28,12 @@
"window": false
},
"rules": {
"accessor-pairs": 2,
"array-bracket-spacing": ["error", "always"],
"arrow-parens": [2, "always"],
"arrow-spacing": [2, { "before": true, "after": true }],
"block-spacing": [2, "always"],
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": 0,
"comma-dangle": [2, "never"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"computed-property-spacing": ["error", "never"],
"constructor-super": 2,
"curly": [2, "all"],
"default-case": [2, { commentPattern: '^no default$' }],
"dot-location": [2, "property"],
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"generator-star-spacing": [2, { "before": true, "after": true }],
"handle-callback-err": [2, "^(err|error)$" ],
"indent": [2, 2, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"keyword-spacing": [2, { "before": true, "after": true }],
"max-len": [2, 120, 4],
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"new-parens": 2,
"no-alert": 1,
"no-array-constructor": 2,
"no-caller": 2,
@@ -71,8 +56,6 @@
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": [2, "functions"],
"no-extra-semi": 2,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
@@ -85,10 +68,7 @@
"no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
"no-lone-blocks": 2,
"no-loop-func": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-new": 2,
@@ -110,11 +90,9 @@
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-this-before-super": 2,
"no-throw-literal": 2,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-unexpected-multiline": 2,
@@ -129,30 +107,13 @@
"no-useless-constructor": 2,
"no-useless-escape": 2,
"no-var": 2,
"no-whitespace-before-property": 2,
"no-with": 2,
"object-curly-spacing": ["error", "always"],
"operator-linebreak": [2, "after"],
"padded-blocks": [2, "never"],
"prefer-const": 2,
"prefer-rest-params": 2,
"prefer-template": 2,
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"rest-spread-spacing": ["error"],
"semi": [2, "always"],
"semi-spacing": [2, { "before": false, "after": true }],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, { "anonymous": "always", "named": "never" }],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
"template-curly-spacing": [2, "never"],
"use-isnan": 2,
"valid-typeof": 2,
"wrap-iife": [2, "outside"],
"yield-star-spacing": [2, "both"],
"yoda": [2, "never"],
// promise
@@ -181,14 +142,10 @@
"jsx-a11y/tabindex-no-positive": 2,
// react
"jsx-quotes": [2, "prefer-single"],
"react/display-name": 0,
"react/jsx-boolean-value": 2,
"react/jsx-handler-names": [2, {
"eventHandlerPrefix": "_handle"
}],
"react/jsx-indent": [2, 2],
"react/jsx-indent-props": [2, 2],
"react/jsx-no-bind": 2,
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-undef": 2,

View File

@@ -13,5 +13,6 @@ https://github.com/necolas/react-native-web/CONTRIBUTING.md
- [ ] includes documentation
- [ ] includes tests
- [ ] includes benchmark reports
- [ ] includes an interactive example
- [ ] includes screenshots/videos

6
.gitignore vendored
View File

@@ -1,3 +1,3 @@
/dist
/dist-examples
/node_modules
node_modules
dist
dist-examples

View File

@@ -5,5 +5,4 @@ before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
script:
- npm run lint
- npm test

View File

@@ -1,7 +1,5 @@
# Contributing
We are open to, and grateful for, any contributions made by the community.
## Reporting Issues and Asking Questions
Before opening an issue, please search the [issue
@@ -31,6 +29,12 @@ Run the examples:
npm run examples
```
Run the benchmarks in a browser by opening `./performance/index.html` after running:
```
npm run build:performance
```
### Building
```
@@ -57,7 +61,7 @@ To continuously watch and run tests, run the following:
npm run test:watch
```
To perform linting, run the following:
To perform only linting, run the following:
```
npm run lint

View File

@@ -27,7 +27,7 @@ online with [React Native for Web: Playground](http://codepen.io/necolas/pen/PZz
To install in your app:
```
npm install --save react react-native-web
npm install --save react@15.4 react-native-web
```
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
@@ -53,6 +53,7 @@ Exported modules:
* Components
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
* [`Button`](docs/components/Button.md)
* [`Image`](docs/components/Image.md)
* [`ListView`](docs/components/ListView.md)
* [`ProgressBar`](docs/components/ProgressBar.md)
@@ -69,6 +70,7 @@ Exported modules:
* [`AppRegistry`](docs/apis/AppRegistry.md)
* [`AppState`](docs/apis/AppState.md)
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
* [`Clipboard`](docs/apis/Clipboard.md)
* [`Dimensions`](docs/apis/Dimensions.md)
* [`I18nManager`](docs/apis/I18nManager.md)
* [`NativeMethods`](docs/apis/NativeMethods.md)
@@ -80,6 +82,7 @@ Exported modules:
* [`Vibration`](docs/apis/Vibration.md)
<span id="#why"></span>
## Why?
There are many different teams at Twitter building web applications with React.
@@ -142,6 +145,7 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
* [react-web](https://github.com/taobaofed/react-web)
* [react-native-for-web](https://github.com/KodersLab/react-native-for-web)
* [rhinos-app](https://github.com/rhinos-app/rhinos-app-dev)
## License

View File

@@ -3,8 +3,7 @@
`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`, and prerendered by invoking
`AppRegistry.prerenderApplication` (see the [client and server rendering
`AppRegistry.runApplication` (see the [client and server rendering
guide](../guides/rendering.md) for more details).
To "stop" an application when a view should be destroyed, call

16
docs/apis/Clipboard.md Normal file
View File

@@ -0,0 +1,16 @@
# 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

@@ -15,9 +15,9 @@ Each key of the object passed to `create` must define a style object.
Flattens an array of styles into a single style object.
**render**: function
**renderToString**: function
Returns a React `<style>` element for use in server-side rendering.
Returns a string of the stylesheet for use in server-side rendering.
## Properties

39
docs/components/Button.md Normal file
View File

@@ -0,0 +1,39 @@
# 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
Defines the text available to assistive technologies upon interaction with the
element. (This is implemented using `aria-label`.)
**color**: string
Background color of the button.
**disabled**: bool = false
If true, disable all interactions for this component
**onPress**: function
This function is called on press.
**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

@@ -75,6 +75,23 @@ 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

View File

@@ -38,6 +38,18 @@ which this `ScrollView` renders.
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
@@ -51,8 +63,8 @@ When false, the content does not scroll.
**scrollEventThrottle**: number = 0
This controls how often the scroll event will be fired while scrolling (in
events per seconds). A higher number yields better accuracy for code that is
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.
@@ -104,7 +116,7 @@ export default class ScrollViewExample extends Component {
contentContainerStyle={styles.container}
horizontal
onScroll={(e) => this.onScroll(e)}
scrollEventThrottle={60}
scrollEventThrottle={100}
style={styles.root}
/>
)

View File

@@ -63,27 +63,27 @@ Lets the user select the text.
+ ...[View#style](View.md)
+ `color`
+ `fontFamily`
+ `fontFeatureSettings`
+ `fontSize`
+ `fontStyle`
+ `fontWeight`
+ `letterSpacing`
+ `lineHeight`
+ `textAlign`
+ `textAlign`
+ `textAlignVertical`
+ `textDecorationLine`
+ `textOverflow`
+ `textRendering`
+ `textOverflow`
+ `textRendering`
+ `textShadowColor`
+ `textShadowOffset`
+ `textShadowOffset`
+ `textShadowRadius`
+ `textTransform`
+ `unicodeBidi`
+ `textTransform`
+ `unicodeBidi`
+ `whiteSpace`
+ `wordWrap`
+ `writingDirection`
+ `wordWrap`
+ `writingDirection`
This property can be suffixed with `$noI18n` to prevent automatic
bidi-flipping in RTL mode. This is only supported if `Platform.OS === 'web'`.
web only.
**testID**: string

View File

@@ -144,7 +144,9 @@ If `true`, all text will automatically be selected on focus.
**style**: style
+ ...[Text#style](./Text.md)
+ `outline`
+ `resize`
‡ web only.
**testID**: string

View File

@@ -45,9 +45,7 @@ If true, disable all interactions for this component.
**hitSlop**: `{top: number, left: number, bottom: number, right: number}`
This defines how far your touch can start away from the button. This is added
to `pressRetentionOffset` when moving off of the button. **NOTE**: The touch
area never extends past the parent view bounds and the z-index of sibling views
always takes precedence if a touch hits two overlapping views.
to `pressRetentionOffset` when moving off of the button.
**onLayout**: function

View File

@@ -18,8 +18,10 @@ NOTE: `View` will transfer all other props to the rendered HTML element.
**accessibilityLabel**: string
Defines the text available to assistive technologies upon interaction with the
element. (This is implemented using `aria-label`.)
Overrides the text that's read by the screen reader when the user interacts
with the element. By default, the label is constructed by traversing all the
children and accumulating all the `Text` nodes separated by space. (This is
implemented using `aria-label`.)
**accessibilityLiveRegion**: oneOf('assertive', 'off', 'polite') = 'off'
@@ -46,6 +48,15 @@ assistive technologies of a `role` value change.
When `false`, the view is hidden from assistive technologies. (This is
implemented using `aria-hidden`.)
**hitSlop**: {top: number, left: number, bottom: number, right: number}
This defines how far a touch event can start away from the view. 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}}`.
**onLayout**: function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
@@ -99,39 +110,48 @@ from `style`.
+ `alignContent`
+ `alignItems`
+ `alignSelf`
+ `animationDelay`
+ `animationDirection`
+ `animationDuration`
+ `animationFillMode`
+ `animationIterationCount`
+ `animationName`
+ `animationPlayState`
+ `animationTimingFunction`
+ `backfaceVisibility`
+ `backgroundAttachment`
+ `backgroundClip`
+ `backgroundAttachment`
+ `backgroundClip`
+ `backgroundColor`
+ `backgroundImage`
+ `backgroundOrigin`
+ `backgroundPosition`
+ `backgroundRepeat`
+ `backgroundSize`
+ `backgroundImage`
+ `backgroundOrigin`
+ `backgroundPosition`
+ `backgroundRepeat`
+ `backgroundSize`
+ `borderColor` (single value)
+ `borderTopColor`
+ `borderBottomColor`
+ `borderRightColor`
+ `borderLeftColor`
+ `borderRightColor`
+ `borderLeftColor`
+ `borderRadius` (single value)
+ `borderTopLeftRadius`
+ `borderTopRightRadius`
+ `borderBottomLeftRadius`
+ `borderBottomRightRadius`
+ `borderTopLeftRadius`
+ `borderTopRightRadius`
+ `borderBottomLeftRadius`
+ `borderBottomRightRadius`
+ `borderStyle` (single value)
+ `borderTopStyle`
+ `borderRightStyle`
+ `borderRightStyle`
+ `borderBottomStyle`
+ `borderLeftStyle`
+ `borderLeftStyle`
+ `borderWidth` (single value)
+ `borderBottomWidth`
+ `borderLeftWidth`
+ `borderRightWidth`
+ `borderLeftWidth`
+ `borderRightWidth`
+ `borderTopWidth`
+ `bottom`
+ `boxShadow`
+ `boxSizing`
+ `cursor`
+ `cursor`
+ `display`
+ `flex` (number)
+ `flexBasis`
+ `flexDirection`
@@ -140,12 +160,12 @@ from `style`.
+ `flexWrap`
+ `height`
+ `justifyContent`
+ `left`
+ `left`
+ `margin` (single value)
+ `marginBottom`
+ `marginHorizontal`
+ `marginLeft`
+ `marginRight`
+ `marginLeft`
+ `marginRight`
+ `marginTop`
+ `marginVertical`
+ `maxHeight`
@@ -154,27 +174,35 @@ from `style`.
+ `minWidth`
+ `opacity`
+ `order`
+ `outline`
+ `overflow`
+ `overflowX`
+ `overflowY`
+ `overflowX`
+ `overflowY`
+ `padding` (single value)
+ `paddingBottom`
+ `paddingHorizontal`
+ `paddingLeft`
+ `paddingRight`
+ `paddingLeft`
+ `paddingRight`
+ `paddingTop`
+ `paddingVertical`
+ `perspective`
+ `perspectiveOrigin`
+ `position`
+ `right`
+ `right`
+ `top`
+ `transform`
+ `userSelect`
+ `visibility`
+ `transformOrigin`
+ `transitionDelay`
+ `transitionDuration`
+ `transitionProperty`
+ `transitionTimingFunction`
+ `userSelect`
+ `visibility`
+ `width`
+ `willChange`
+ `zIndex`
This property can be suffixed with `$noI18n` to prevent automatic
bidi-flipping in RTL mode. This is only supported if `Platform.OS === 'web'`.
web only.
Default:

View File

@@ -4,7 +4,7 @@ It is sometimes necessary to make changes directly to a component without using
state/props to trigger a re-render of the entire subtree in the browser, this
is done by directly modifying a DOM node. `setNativeProps` is the React Native
equivalent to setting properties directly on a DOM node. Use direct
manipulation when frequent re-rendering creates a performance bottleneck Direct
manipulation when frequent re-rendering creates a performance bottleneck. Direct
manipulation will not be a tool that you reach for frequently.
## `setNativeProps` and `shouldComponentUpdate`

View File

@@ -4,11 +4,6 @@ To support right-to-left languages, application layout can be automatically
flipped from LTR to RTL. The `I18nManager` API can be used to help with more
fine-grained control and testing of RTL layouts.
React Native for Web provides an experimental feature to support "true left"
and "true right" styles. For example, `left` will be flipped to `right` in RTL
mode, but `left$noI18n` will not. More information is available in the `Text`
and `View` documentation.
## Working with icons and images
Icons and images that must match the LTR or RTL layout of the app need to be manually flipped.

View File

@@ -27,14 +27,48 @@ the `url-loader` to the webpack config:
module.exports = {
// ...
module: {
loaders: {
test: /\.(gif|jpe?g|png|svg)$/,
loader: 'url-loader',
query: { name: '[name].[hash:16].[ext]' }
}
loaders: [
{
test: /\.(gif|jpe?g|png|svg)$/,
loader: 'url-loader',
query: { name: '[name].[hash:16].[ext]' }
}
]
}
};
```
## Dependencies
Many OSS React Native packages are not compiled to ES5 before being published.
This can result in webpack build errors. To avoid this issue you should
configure webpack (or your bundler of choice) to run
`babel-preset-react-native` over the necessary `node_module`. For example:
```js
// webpack.config.js
module.exports = {
// ...
module: {
loaders: [
{
// transpile to ES5
test: /\.jsx?$/,
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules/react-native-something')
],
loader: 'babel-loader',
query: { cacheDirectory: true }
}
]
}
};
```
Please refer to the webpack documentation for more information.
## Web-specific code
Minor platform differences can use the `Platform` module.
@@ -52,7 +86,19 @@ if (Platform.OS === 'web') {
```
More substantial Web-specific implementation code should be written in files
with the extension `.web.js`, which webpack will automatically resolve.
with the extension `.web.js`. Webpack@1 will automatically resolve these files.
Webpack@2 requires additional configuration.
```js
// webpack.config.js
module.exports = {
// ...
resolve: {
extensions: [ '.web.js', '.js' ]
}
};
```
## Optimizations

View File

@@ -16,8 +16,9 @@ module.exports = {
}
```
The `react-native-web` package also includes a `core` module that exports only
`ReactNative`, `Image`, `StyleSheet`, `Text`, `TextInput`, and `View`.
The `react-native-web` package also includes a `core` module that exports a
subset of modules: `ReactNative`, `I18nManager`, `Platform`, `StyleSheet`,
`Image`, `Text`, `TextInput`, `Touchable`, and `View`.
```js
// webpack.config.js
@@ -62,16 +63,17 @@ AppRegistry.runApplication('App', {
initialProps: {},
rootTag: document.getElementById('react-app')
})
// prerender the app
const { html, styleElement } = AppRegistry.prerenderApplication('App', { initialProps })
```
Setting `process.env.__REACT_NATIVE_DEBUG_ENABLED__` to `true` will expose some
debugging logs. This can help track down when you're rendering without the
performance benefit of cached styles.
## Server-side rendering
Rendering using the `AppRegistry`:
```
```js
import ReactDOMServer from 'react-dom/server'
import ReactNative, { AppRegistry } from 'react-native'
@@ -84,4 +86,4 @@ AppRegistry.registerComponent('App', () => AppContainer)
// prerender the app
const { element, stylesheet } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
```
```

View File

@@ -31,10 +31,9 @@ const styles = StyleSheet.create({
})
```
Using `StyleSheet.create` is optional but provides some key advantages: styles
are immutable in development, certain declarations are automatically converted
to CSS rather than applied as inline styles, and styles are only created once
for the application and not on every render.
Using `StyleSheet.create` is optional but provides the best performance
(`style` is resolved to CSS stylesheets). Avoid creating unregistered style
objects.
The attribute names and values are a subset of CSS. See the `style`
documentation of individual components.
@@ -56,12 +55,6 @@ A common pattern is to conditionally add style based on a condition:
styles.base,
this.state.active && styles.active
]} />
// or
<View style={{
...styles.base,
...(this.state.active && styles.active)
}} />
```
## Composing styles

View File

@@ -1,6 +1,8 @@
const path = require('path')
const webpack = require('webpack')
const DEV = process.env.NODE_ENV !== 'production';
module.exports = {
module: {
loaders: [
@@ -19,13 +21,9 @@ module.exports = {
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.__REACT_NATIVE_DEBUG_ENABLED__': DEV
}),
// https://github.com/animatedjs/animated/issues/40
new webpack.NormalModuleReplacementPlugin(
/es6-set/,
path.join(__dirname, '../../src/modules/polyfills/Set.js')
),
new webpack.optimize.OccurenceOrderPlugin()
],
resolve: {

View File

@@ -0,0 +1,33 @@
import { Clipboard, Text, TextInput, View } from 'react-native'
import React, { Component } from 'react';
import { action, storiesOf } from '@kadira/storybook';
class ClipboardExample extends Component {
render() {
return (
<View style={{ minWidth: 300 }}>
<Text onPress={this._handleSet}>Copy to clipboard</Text>
<TextInput
multiline={true}
placeholder={'Try pasting here afterwards'}
style={{ borderWidth: 1, height: 200, marginVertical: 20 }}
/>
<Text onPress={this._handleGet}>(Clipboard.getString returns a Promise that always resolves to an empty string on web)</Text>
</View>
)
}
_handleGet() {
Clipboard.getString().then((value) => { console.log(`Clipboard value: ${value}`) });
}
_handleSet() {
const success = Clipboard.setString('This text was copied to the clipboard by React Native');
console.log(`Clipboard.setString success? ${success}`);
}
}
storiesOf('api: Clipboard', module)
.add('setString', () => (
<ClipboardExample />
));

View File

@@ -1,8 +1,8 @@
import { storiesOf } from '@kadira/storybook';
import { I18nManager, StyleSheet, TouchableHighlight, Text, View } from 'react-native'
import React, { Component } from 'react';
import { storiesOf, action } from '@kadira/storybook';
class RTLExample extends Component {
class I18nManagerExample extends Component {
componentWillUnmount() {
I18nManager.setPreferredLanguageRTL(false)
}
@@ -14,14 +14,22 @@ class RTLExample extends Component {
LTR/RTL layout example!
</Text>
<Text style={styles.text}>
This is sample text. The writing direction can be changed by pressing the button below.
</Text>
<Text style={[ styles.text, styles.ltrText ]}>
This is text that will always display LTR.
The writing direction of text is automatically determined by the browser, independent of the global writing direction of the app.
</Text>
<Text style={[ styles.text, styles.rtlText ]}>
This is text that will always display RTL.
أحب اللغة العربية
</Text>
<Text style={[ styles.text, styles.textAlign ]}>
textAlign toggles
</Text>
<View style={styles.horizontal}>
<View style={[ styles.box, { backgroundColor: 'lightblue' } ]}>
<Text>One</Text>
</View>
<View style={[ styles.box ]}>
<Text>Two</Text>
</View>
</View>
<TouchableHighlight
onPress={this._handleToggle}
style={styles.toggle}
@@ -34,8 +42,8 @@ class RTLExample extends Component {
}
_handleToggle = () => {
this._isRTL = !this._isRTL
I18nManager.setPreferredLanguageRTL(this._isRTL)
I18nManager.setPreferredLanguageRTL(!I18nManager.isRTL)
this.forceUpdate();
}
}
@@ -55,13 +63,16 @@ const styles = StyleSheet.create({
fontSize: 18,
marginBottom: 5
},
ltrText: {
textAlign$noI18n: 'left',
writingDirection$noI18n: 'ltr'
textAlign: {
textAlign: 'left'
},
rtlText: {
textAlign$noI18n: 'right',
writingDirection$noI18n: 'rtl'
horizontal: {
flexDirection: 'row',
marginVertical: 10
},
box: {
borderWidth: 1,
flex: 1
},
toggle: {
alignSelf: 'center',
@@ -75,5 +86,5 @@ const styles = StyleSheet.create({
storiesOf('api: I18nManager', module)
.add('RTL layout', () => (
<RTLExample />
<I18nManagerExample />
))

View File

@@ -0,0 +1,37 @@
import { Linking, StyleSheet, Text, View } from 'react-native'
import React, { Component } from 'react';
import { storiesOf, action } from '@kadira/storybook';
const url = 'https://mathiasbynens.github.io/rel-noopener/malicious.html';
class LinkingExample extends Component {
handlePress() {
Linking.canOpenURL(url).then((supported) => {
return Linking.openURL(url);
});
}
render() {
return (
<View>
<Text onPress={this.handlePress} style={styles.text}>
Linking.openURL (Expect: "The previous tab is safe and intact")
</Text>
<Text accessibilityRole='link' href='https://mathiasbynens.github.io/rel-noopener/malicious.html' style={styles.text} target='_blank'>
target="_blank" (Expect: "The previous tab is safe and intact")
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
text: {
marginVertical: 10
}
});
storiesOf('api: Linking', module)
.add('Safe linking', () => (
<LinkingExample />
));

View File

@@ -50,7 +50,8 @@ const ToggleAnimatingActivityIndicator = React.createClass({
return (
<ActivityIndicator
animating={this.state.animating}
style={[styles.centering, {height: 80}]}
style={styles.centering}
hidesWhenStopped={this.props.hidesWhenStopped}
size="large"
/>
);
@@ -121,7 +122,12 @@ const examples = [
{
title: 'Start/stop',
render() {
return <ToggleAnimatingActivityIndicator />;
return (
<View style={[styles.horizontal, styles.centering]}>
<ToggleAnimatingActivityIndicator />
<ToggleAnimatingActivityIndicator hidesWhenStopped={false} />
</View>
);
}
},
{
@@ -129,7 +135,7 @@ const examples = [
render() {
return (
<View style={[styles.horizontal, styles.centering]}>
<ActivityIndicator size="40" />
<ActivityIndicator size={40} />
<ActivityIndicator
style={{ marginLeft: 20, transform: [ {scale: 1.5} ] }}
size="large"

View File

@@ -0,0 +1,80 @@
import React from 'react';
import { action, storiesOf } from '@kadira/storybook';
import { Button, StyleSheet, View } from 'react-native';
const onButtonPress = action('Button has been pressed!');
const examples = [
{
title: 'Simple Button',
description: 'The title and onPress handler are required. It is ' +
'recommended to set accessibilityLabel to help make your app usable by ' +
'everyone.',
render: function() {
return (
<Button
onPress={onButtonPress}
title="Press Me"
accessibilityLabel="See an informative alert"
/>
);
},
},
{
title: 'Adjusted color',
description: 'Adjusts the color in a way that looks standard on each ' +
'platform. On iOS, the color prop controls the color of the text. On ' +
'Android, the color adjusts the background color of the button.',
render: function() {
return (
<Button
onPress={onButtonPress}
title="Press Purple"
color="#841584"
accessibilityLabel="Learn more about purple"
/>
);
},
},
{
title: 'Fit to text layout',
description: 'This layout strategy lets the title define the width of ' +
'the button',
render: function() {
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<Button
onPress={onButtonPress}
title="This looks great!"
accessibilityLabel="This sounds great!"
/>
<Button
onPress={onButtonPress}
title="Ok!"
color="#841584"
accessibilityLabel="Ok, Great!"
/>
</View>
);
},
},
{
title: 'Disabled Button',
description: 'All interactions for the component are disabled.',
render: function() {
return (
<Button
disabled
onPress={onButtonPress}
title="I Am Disabled"
accessibilityLabel="See an informative alert"
/>
);
},
},
];
examples.forEach((example) => {
storiesOf('component: Button', module)
.add(example.title, () => example.render());
});

View File

@@ -28,10 +28,9 @@ import { ActivityIndicator, Image, Platform, StyleSheet, Text, View } from 'reac
var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg==';
//var ImageCapInsetsExample = require('./ImageCapInsetsExample');
//const IMAGE_PREFETCH_URL = 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1&t=' + Date.now();
//var prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL);
const IMAGE_PREFETCH_URL = 'http://origami.design/public/images/bird-logo.png?r=1&t=' + Date.now();
var prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL);
/*
var NetworkImageCallbackExample = React.createClass({
getInitialState: function() {
return {
@@ -88,7 +87,6 @@ var NetworkImageCallbackExample = React.createClass({
});
}
});
*/
var NetworkImageExample = React.createClass({
getInitialState: function() {
@@ -118,7 +116,6 @@ var NetworkImageExample = React.createClass({
}
});
/*
var ImageSizeExample = React.createClass({
getInitialState: function() {
return {
@@ -133,24 +130,25 @@ var ImageSizeExample = React.createClass({
},
render: function() {
return (
<View style={{flexDirection: 'row'}}>
<Image
style={{
width: 60,
height: 60,
backgroundColor: 'transparent',
marginRight: 10,
}}
source={this.props.source} />
<View>
<Text>
Actual dimensions:{'\n'}
Width: {this.state.width}, Height: {this.state.height}
width: {this.state.width}, height: {this.state.height}
</Text>
<Image
source={this.props.source}
style={{
backgroundColor: '#eee',
height: 227,
marginTop: 10,
width: 323
}}
/>
</View>
);
},
});
*/
/*
var MultipleSourcesExample = React.createClass({
getInitialState: function() {
@@ -239,17 +237,17 @@ const examples = [
);
},
},
/*
{
title: 'Image Loading Events',
render: function() {
return (
<NetworkImageCallbackExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1&t=' + Date.now()}}
prefetchedSource={{uri: IMAGE_PREFETCH_URL}}/>
<NetworkImageCallbackExample
source={{uri: 'http://origami.design/public/images/bird-logo.png?r=1&t=' + Date.now()}}
prefetchedSource={{uri: IMAGE_PREFETCH_URL}}
/>
);
},
},
*/
{
title: 'Error Handler',
render: function() {
@@ -263,7 +261,7 @@ const examples = [
title: 'Image Download Progress',
render: function() {
return (
<NetworkImageExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1'}}/>
<NetworkImageExample source={{uri: 'http://origami.design/public/images/bird-logo.png?r=1'}}/>
);
},
platform: 'ios',
@@ -567,14 +565,12 @@ const examples = [
platform: 'ios',
},
*/
/*
{
title: 'Image Size',
render: function() {
return <ImageSizeExample source={fullImage} />;
return <ImageSizeExample source={{ uri: 'https://upload.wikimedia.org/wikipedia/commons/d/d7/Chestnut-mandibled_Toucan.jpg' }} />;
},
},
*/
/*
{
title: 'MultipleSourcesExample',

View File

@@ -1,4 +1,80 @@
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import { ListView } from 'react-native'
import { storiesOf } from '@kadira/storybook';
import { ListView, StyleSheet, Text, View } from 'react-native';
const generateData = (length) => Array.from({ length }).map((item, i) => i);
const dataSource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
storiesOf('component: ListView', module)
.add('vertical', () => (
<View style={styles.scrollViewContainer}>
<ListView
contentContainerStyle={styles.scrollViewContentContainerStyle}
dataSource={dataSource.cloneWithRows(generateData(100))}
initialListSize={100}
// eslint-disable-next-line react/jsx-no-bind
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
// eslint-disable-next-line react/jsx-no-bind
renderRow={(row) => (
<View><Text>{row}</Text></View>
)}
scrollEventThrottle={1000} // 1 event per second
style={styles.scrollViewStyle}
/>
</View>
))
.add('incremental rendering - large pageSize', () => (
<View style={styles.scrollViewContainer}>
<ListView
contentContainerStyle={styles.scrollViewContentContainerStyle}
dataSource={dataSource.cloneWithRows(generateData(5000))}
initialListSize={100}
// eslint-disable-next-line react/jsx-no-bind
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
pageSize={50}
// eslint-disable-next-line react/jsx-no-bind
renderRow={(row) => (
<View><Text>{row}</Text></View>
)}
scrollEventThrottle={1000} // 1 event per second
style={styles.scrollViewStyle}
/>
</View>
))
.add('incremental rendering - small pageSize', () => (
<View style={styles.scrollViewContainer}>
<ListView
contentContainerStyle={styles.scrollViewContentContainerStyle}
dataSource={dataSource.cloneWithRows(generateData(5000))}
initialListSize={5}
// eslint-disable-next-line react/jsx-no-bind
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
pageSize={1}
// eslint-disable-next-line react/jsx-no-bind
renderRow={(row) => (
<View><Text>{row}</Text></View>
)}
scrollEventThrottle={1000} // 1 event per second
style={styles.scrollViewStyle}
/>
</View>
));
const styles = StyleSheet.create({
box: {
flexGrow: 1,
justifyContent: 'center',
borderWidth: 1
},
scrollViewContainer: {
height: '200px',
width: 300
},
scrollViewStyle: {
borderWidth: '1px'
},
scrollViewContentContainerStyle: {
backgroundColor: '#eee',
padding: '10px'
}
});

View File

@@ -1,13 +1,15 @@
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import { action, storiesOf } from '@kadira/storybook';
import { ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native'
const onScroll = action('ScrollView.onScroll');
storiesOf('component: ScrollView', module)
.add('vertical', () => (
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
onScroll={e => { console.log('ScrollView.onScroll', e); } }
onScroll={onScroll}
scrollEventThrottle={1000} // 1 event per second
style={styles.scrollViewStyle}
>
@@ -24,8 +26,8 @@ storiesOf('component: ScrollView', module)
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
horizontal
onScroll={e => console.log('ScrollView.onScroll', e)}
scrollEventThrottle={1} // 1 event per second
onScroll={onScroll}
scrollEventThrottle={16} // ~60 events per second
style={styles.scrollViewStyle}
>
{Array.from({ length: 50 }).map((item, i) => (
@@ -48,10 +50,10 @@ const styles = StyleSheet.create({
width: 300
},
scrollViewStyle: {
borderWidth: '1px'
borderWidth: 1
},
scrollViewContentContainerStyle: {
backgroundColor: '#eee',
padding: '10px'
padding: 10
}
})

View File

@@ -271,7 +271,7 @@ const examples = [
<Text>
auto (default) - english LTR
</Text>
<Text style={{ writingDirection$noI18n: 'rtl' }}>
<Text>
أحب اللغة العربية auto (default) - arabic RTL
</Text>
<Text style={{textAlign: 'left'}}>

View File

@@ -59,7 +59,7 @@ class TextEventsExample extends React.Component {
render() {
return (
<View>
<View style={{ alignItems: 'center' }}>
<TextInput
autoCapitalize="none"
placeholder="Enter text to see events"
@@ -83,7 +83,7 @@ class TextEventsExample extends React.Component {
onKeyPress={(event) => {
this.updateText('onKeyPress key: ' + event.nativeEvent.key);
}}
style={styles.default}
style={[ styles.default, { maxWidth: 200 } ]}
/>
<Text style={styles.eventLabel}>
{this.state.curText}{'\n'}

View File

@@ -57,7 +57,6 @@ const examples = [
<TouchableHighlight
style={styles.wrapper}
activeOpacity={1}
animationVelocity={0}
underlayColor="rgb(210, 230, 255)"
onPress={() => console.log('custom THW text - highlight')}>
<View style={styles.wrapperCustom}>
@@ -307,21 +306,20 @@ var TouchableDisabled = React.createClass({
render: function() {
return (
<View>
<TouchableOpacity disabled={true} style={[styles.row, styles.block]}>
<TouchableOpacity disabled={true} style={[styles.row, styles.block]} onPress={action('TouchableOpacity')}>
<Text style={styles.disabledButton}>Disabled TouchableOpacity</Text>
</TouchableOpacity>
<TouchableOpacity disabled={false} style={[styles.row, styles.block]}>
<TouchableOpacity disabled={false} style={[styles.row, styles.block]} onPress={action('TouchableOpacity')}>
<Text style={styles.button}>Enabled TouchableOpacity</Text>
</TouchableOpacity>
<TouchableHighlight
activeOpacity={1}
disabled={true}
animationVelocity={0}
underlayColor="rgb(210, 230, 255)"
style={[styles.row, styles.block]}
onPress={() => console.log('custom THW text - highlight')}>
onPress={action('TouchableHighlight')}>
<Text style={styles.disabledButton}>
Disabled TouchableHighlight
</Text>
@@ -329,10 +327,9 @@ var TouchableDisabled = React.createClass({
<TouchableHighlight
activeOpacity={1}
animationVelocity={0}
underlayColor="rgb(210, 230, 255)"
style={[styles.row, styles.block]}
onPress={() => console.log('custom THW text - highlight')}>
onPress={action('TouchableHighlight')}>
<Text style={styles.button}>
Enabled TouchableHighlight
</Text>

View File

@@ -31,6 +31,17 @@ var styles = StyleSheet.create({
borderColor: '#000033',
borderWidth: 1,
},
shadowBox: {
width: 100,
height: 100,
borderWidth: 2,
},
shadow: {
shadowOpacity: 0.5,
shadowColor: 'red',
shadowRadius: 3,
shadowOffset: { width: 3, height: 3 },
},
zIndex: {
justifyContent: 'space-around',
width: 100,
@@ -242,6 +253,19 @@ const examples = [
return <ZIndexExample />;
},
},
{
title: 'Basic shadow',
render() {
return <View style={[ styles.shadowBox, styles.shadow ]} />;
}
},
{
title: 'Shaped shadow',
description: 'borderRadius: 50',
render() {
return <View style={[ styles.shadowBox, styles.shadow, {borderRadius: 50} ]} />;
}
}
];
examples.forEach((example) => {

View File

@@ -113,7 +113,7 @@ var Cell = React.createClass({
case 2:
return styles.cellTextO;
default:
return {};
return null;
}
},

View File

@@ -1,60 +1,69 @@
{
"name": "react-native-web",
"version": "0.0.55",
"version": "0.0.80",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
"dist"
"dist",
"src",
"!**/__tests__"
],
"scripts": {
"build": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
"build:examples": "build-storybook -o dist-examples -c ./examples/.storybook",
"build:performance": "cd performance && yarn && webpack",
"build:umd": "webpack --config webpack.config.js --sort-assets-by --progress",
"deploy:examples": "git checkout gh-pages && rm -rf ./storybook && mv dist-examples storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
"examples": "start-storybook -p 9001 -c ./examples/.storybook --dont-track",
"lint": "eslint src",
"fmt": "find performance src -name '*.js' | grep -v -E '(node_modules|dist)' | xargs prettier --print-width=100 --single-quote --write",
"lint": "eslint performance src --ignore-path .gitignore",
"prepublish": "npm run build && npm run build:umd",
"test": "jest",
"test:watch": "npm run test -- --watch"
"test": "npm run lint && npm run test:jest",
"test:jest": "jest",
"test:watch": "npm run test:jest -- --watch"
},
"dependencies": {
"animated": "^0.1.3",
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"babel-runtime": "^6.11.6",
"babel-runtime": "^6.23.0",
"debounce": "^1.0.0",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.4",
"inline-style-prefixer": "^2.0.1",
"react-dom": "~15.3.2",
"fbjs": "^0.8.8",
"hyphenate-style-name": "^1.0.2",
"inline-style-prefixer": "^3.0.0",
"normalize-css-color": "^1.0.2",
"react-dom": "~15.4.1",
"react-textarea-autosize": "^4.0.4",
"react-timer-mixin": "^0.13.3"
},
"devDependencies": {
"@kadira/storybook": "^2.5.1",
"babel-cli": "^6.14.0",
"babel-core": "^6.14.0",
"babel-eslint": "^6.1.2",
"babel-loader": "^6.2.5",
"babel-plugin-transform-react-remove-prop-types": "^0.2.11",
"babel-preset-react-native": "^1.9.0",
"del-cli": "^0.2.0",
"babel-cli": "^6.23.0",
"babel-core": "^6.23.1",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.3.2",
"babel-plugin-transform-react-remove-prop-types": "^0.3.2",
"babel-preset-react-native": "^1.9.1",
"del-cli": "^0.2.1",
"enzyme": "^2.4.1",
"eslint": "^3.4.0",
"eslint-config-prettier": "^1.4.0",
"eslint-plugin-jsx-a11y": "^2.2.0",
"eslint-plugin-promise": "^2.0.1",
"eslint-plugin-react": "^6.1.2",
"file-loader": "^0.9.0",
"jest": "^16.0.2",
"jest": "^19.0.2",
"node-libs-browser": "^0.5.3",
"react": "~15.3.2",
"react-addons-test-utils": "~15.3.2",
"react-test-renderer": "~15.3.2",
"prettier": "^0.19.0",
"react": "~15.4.1",
"react-addons-test-utils": "~15.4.1",
"react-test-renderer": "~15.4.1",
"url-loader": "^0.5.7",
"webpack": "^1.13.2",
"webpack-bundle-analyzer": "^1.5.3"
"webpack-bundle-analyzer": "^2.2.1"
},
"peerDependencies": {
"react": "~15.3.2"
"react": "~15.4.1"
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause",

27
performance/README.md Normal file
View File

@@ -0,0 +1,27 @@
# 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.
## Benchmark results
Typical render timings*: mean ± two standard deviations
| Implementation | Deep tree (ms) | Wide tree (ms) |
| :--- | ---: | ---: |
| `css-modules` | `76.66` `±18.46` | `157.03` `±19.79` |
| `react-native-web@0.0.78` | `90.13` `±20.91` | `198.72` `±24.44` |
| `styled-components@2.0.0-7` | `263.06` `±31.87` | `564.53` `±27.62` |
| `glamor@3.0.0-1` | `267.49` `±35.12` | `451.99` `±37.32` |
*MacBook Pro (13-inch, Early 2011); 2.7 GHz Intel Core i7; 16 GB 1600 MHz DDR3. Google Chrome 56.

View File

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

View File

@@ -0,0 +1,36 @@
.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

@@ -0,0 +1,8 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import styles from './styles.css';
const View = props => <div {...props} className={classnames(styles.initial, props.className)} />;
module.exports = View;

View File

@@ -0,0 +1,21 @@
.initial {
align-items: stretch;
border-width: 0;
border-style: solid;
box-sizing: border-box;
display: flex;
flex-basis: auto;
flex-direction: column;
flex-shrink: 0;
margin: 0;
padding: 0;
position: relative;
background-color: transparent;
color: inherit;
font: inherit;
text-align: inherit;
text-decoration: none;
list-style: none;
min-height: 0;
min-width: 0;
};

View File

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

View File

@@ -0,0 +1,49 @@
/* eslint-disable react/prop-types */
import { css } from 'glamor';
import React from 'react';
import View from '../View';
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: css({
padding: 4
}),
row: css({
flexDirection: 'row'
}),
color0: css({
backgroundColor: '#222'
}),
color1: css({
backgroundColor: '#666'
}),
color2: css({
backgroundColor: '#999'
}),
color3: css({
backgroundColor: 'blue'
}),
color4: css({
backgroundColor: 'orange'
}),
color5: css({
backgroundColor: 'red'
}),
fixed: css({
width: 20,
height: 20
})
};
module.exports = Box;

View File

@@ -0,0 +1,32 @@
/* eslint-disable react/prop-types */
import { css } from 'glamor';
import React from 'react';
const View = props => <div {...props} className={css(viewStyle, props.style)} />;
const viewStyle = {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// button and anchor reset
backgroundColor: 'transparent',
color: 'inherit',
font: 'inherit',
textAlign: 'inherit',
textDecorationLine: 'none',
// list reset
listStyle: 'none',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
};
module.exports = View;

View File

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

View File

@@ -0,0 +1,49 @@
/* eslint-disable react/prop-types */
import React from 'react';
import StyleSheet from 'react-native/apis/StyleSheet';
import View from '../View';
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 = StyleSheet.create({
outer: {
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
},
color1: {
backgroundColor: '#666'
},
color2: {
backgroundColor: '#999'
},
color3: {
backgroundColor: 'blue'
},
color4: {
backgroundColor: 'orange'
},
color5: {
backgroundColor: 'red'
},
fixed: {
width: 20,
height: 20
}
});
module.exports = Box;

View File

@@ -0,0 +1,2 @@
import View from 'react-native/components/View';
export default View;

View File

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

View File

@@ -0,0 +1,31 @@
import styled from 'styled-components';
import View from '../View';
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

@@ -0,0 +1,25 @@
import styled from 'styled-components';
const View = styled.div`
align-items: stretch;
border-width: 0;
border-style: solid;
box-sizing: border-box;
display: flex;
flex-basis: auto;
flex-direction: column;
flex-shrink: 0;
margin: 0;
padding: 0;
position: relative;
background-color: transparent;
color: inherit;
font: inherit;
text-align: inherit;
text-decoration: none;
list-style: none;
min-height: 0;
min-width: 0;
`;
module.exports = View;

View File

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

11
performance/index.html Normal file
View File

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

23
performance/index.js Normal file
View File

@@ -0,0 +1,23 @@
import cssModules from './implementations/css-modules';
import glamor from './implementations/glamor';
import reactNative from './implementations/react-native-web';
import styledComponents from './implementations/styled-components';
import renderDeepTree from './tests/renderDeepTree';
import renderWideTree from './tests/renderWideTree';
const tests = [
// deep tree
() => renderDeepTree('css-modules', cssModules),
() => renderDeepTree('react-native-web', reactNative),
() => renderDeepTree('styled-components', styledComponents),
() => renderDeepTree('glamor', glamor),
// wide tree
() => renderWideTree('css-modules', cssModules),
() => renderWideTree('react-native-web', reactNative),
() => renderWideTree('styled-components', styledComponents),
() => renderWideTree('glamor', glamor)
];
// run benchmarks
tests.reduce((promise, test) => promise.then(test()), Promise.resolve());

View File

@@ -0,0 +1,40 @@
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

@@ -0,0 +1,97 @@
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

@@ -0,0 +1,20 @@
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;

14
performance/package.json Normal file
View File

@@ -0,0 +1,14 @@
{
"name": "performance",
"private": true,
"dependencies": {
"classnames": "^2.2.5",
"glamor": "3.0.0-1",
"marky": "^1.1.3",
"styled-components": "2.0.0-7"
},
"devDependencies": {
"css-loader": "^0.26.2",
"style-loader": "^0.13.2"
}
}

View File

@@ -0,0 +1,13 @@
import createRenderBenchmark from '../modules/createRenderBenchmark';
import NestedTree from '../modules/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

@@ -0,0 +1,13 @@
import createRenderBenchmark from '../modules/createRenderBenchmark';
import NestedTree from '../modules/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;

View File

@@ -0,0 +1,51 @@
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const path = require('path');
const webpack = require('webpack');
module.exports = {
context: __dirname,
entry: './index',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'performance.bundle.js'
},
module: {
loaders: [
{
test: /\.css$/,
loader: 'style-loader!css-loader?module&localIdentName=[hash:base64:8]'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
cacheDirectory: true
}
}
]
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
dead_code: true,
screw_ie8: true,
warnings: true
}
})
],
resolve: {
alias: {
'react-native': path.join(__dirname, '../src')
}
}
};

935
performance/yarn.lock Normal file
View File

@@ -0,0 +1,935 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
argparse@^1.0.7:
version "1.0.9"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
dependencies:
sprintf-js "~1.0.2"
asap@~2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"
autoprefixer@^6.3.1:
version "6.7.5"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.5.tgz#50848f39dc08730091d9495023487e7cc21f518d"
dependencies:
browserslist "^1.7.5"
caniuse-db "^1.0.30000624"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
postcss "^5.2.15"
postcss-value-parser "^3.2.3"
babel-code-frame@^6.11.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
dependencies:
chalk "^1.1.0"
esutils "^2.0.2"
js-tokens "^3.0.0"
babel-runtime@^6.18.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
balanced-match@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
base64-js@^1.0.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
big.js@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978"
bowser@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.6.0.tgz#37fc387b616cb6aef370dab4d6bd402b74c5c54d"
browserslist@^1.0.1, browserslist@^1.5.2, browserslist@^1.7.5:
version "1.7.5"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.5.tgz#eca4713897b51e444283241facf3985de49a9e2b"
dependencies:
caniuse-db "^1.0.30000624"
electron-to-chromium "^1.2.3"
buffer@^5.0.3:
version "5.0.5"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.5.tgz#35c9393244a90aff83581063d16f0882cecc9418"
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
caniuse-api@^1.5.2:
version "1.5.3"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.5.3.tgz#5018e674b51c393e4d50614275dc017e27c4a2a2"
dependencies:
browserslist "^1.0.1"
caniuse-db "^1.0.30000346"
lodash.memoize "^4.1.0"
lodash.uniq "^4.3.0"
caniuse-db@^1.0.30000346, caniuse-db@^1.0.30000624:
version "1.0.30000628"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000628.tgz#3d010e2a8e2537a8d135792e90e4f2ce0eb838cc"
chalk@^1.1.0, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies:
ansi-styles "^2.2.1"
escape-string-regexp "^1.0.2"
has-ansi "^2.0.0"
strip-ansi "^3.0.0"
supports-color "^2.0.0"
clap@^1.0.9:
version "1.1.2"
resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.2.tgz#316545bf22229225a2cecaa6824cd2f56a9709ed"
dependencies:
chalk "^1.1.3"
classnames@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
clone@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149"
coa@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3"
dependencies:
q "^1.1.2"
color-convert@^1.3.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
dependencies:
color-name "^1.1.1"
color-name@^1.0.0, color-name@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689"
color-string@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
dependencies:
color-name "^1.0.0"
color@^0.11.0:
version "0.11.4"
resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
dependencies:
clone "^1.0.2"
color-convert "^1.3.0"
color-string "^0.3.0"
colormin@^1.0.5:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
dependencies:
color "^0.11.0"
css-color-names "0.0.4"
has "^1.0.1"
colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
core-js@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
css-loader@^0.26.2:
version "0.26.2"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.26.2.tgz#a9cd4c2b1a559b45d8efc04fc311ab5d2aaccb9d"
dependencies:
babel-code-frame "^6.11.0"
css-selector-tokenizer "^0.7.0"
cssnano ">=2.6.1 <4"
loader-utils "^1.0.2"
lodash.camelcase "^4.3.0"
object-assign "^4.0.1"
postcss "^5.0.6"
postcss-modules-extract-imports "^1.0.0"
postcss-modules-local-by-default "^1.0.1"
postcss-modules-scope "^1.0.0"
postcss-modules-values "^1.1.0"
source-list-map "^0.1.7"
css-selector-tokenizer@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz#6445f582c7930d241dcc5007a43d6fcb8f073152"
dependencies:
cssesc "^0.1.0"
fastparse "^1.1.1"
regexpu-core "^1.0.0"
css-selector-tokenizer@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
dependencies:
cssesc "^0.1.0"
fastparse "^1.1.1"
regexpu-core "^1.0.0"
css-to-react-native@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.0.1.tgz#179b25cba8cab7798118b1679b81f4d6b3fa0de3"
dependencies:
css-color-keywords "^1.0.0"
fbjs "^0.8.5"
postcss-value-parser "^3.3.0"
cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
"cssnano@>=2.6.1 <4":
version "3.10.0"
resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
dependencies:
autoprefixer "^6.3.1"
decamelize "^1.1.2"
defined "^1.0.0"
has "^1.0.1"
object-assign "^4.0.1"
postcss "^5.0.14"
postcss-calc "^5.2.0"
postcss-colormin "^2.1.8"
postcss-convert-values "^2.3.4"
postcss-discard-comments "^2.0.4"
postcss-discard-duplicates "^2.0.1"
postcss-discard-empty "^2.0.1"
postcss-discard-overridden "^0.1.1"
postcss-discard-unused "^2.2.1"
postcss-filter-plugins "^2.0.0"
postcss-merge-idents "^2.1.5"
postcss-merge-longhand "^2.0.1"
postcss-merge-rules "^2.0.3"
postcss-minify-font-values "^1.0.2"
postcss-minify-gradients "^1.0.1"
postcss-minify-params "^1.0.4"
postcss-minify-selectors "^2.0.4"
postcss-normalize-charset "^1.1.0"
postcss-normalize-url "^3.0.7"
postcss-ordered-values "^2.1.0"
postcss-reduce-idents "^2.2.2"
postcss-reduce-initial "^1.0.0"
postcss-reduce-transforms "^1.0.3"
postcss-svgo "^2.1.1"
postcss-unique-selectors "^2.0.2"
postcss-value-parser "^3.2.3"
postcss-zindex "^2.0.1"
csso@~2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.1.tgz#4f8d91a156f2f1c2aebb40b8fb1b5eb83d94d3b9"
dependencies:
clap "^1.0.9"
source-map "^0.5.3"
decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
electron-to-chromium@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.2.4.tgz#9751cbea89fa120bf88c226ba41eb8d0b6f1b597"
emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
encoding@^0.1.11:
version "0.1.12"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
dependencies:
iconv-lite "~0.4.13"
escape-string-regexp@^1.0.2:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
esprima@^2.6.0:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
fastparse@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
fbjs@^0.8.5, fbjs@^0.8.8, fbjs@^0.8.9:
version "0.8.9"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.9.tgz#180247fbd347dcc9004517b904f865400a0c8f14"
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.9"
flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
function-bind@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
glamor@3.0.0-1:
version "3.0.0-1"
resolved "https://registry.yarnpkg.com/glamor/-/glamor-3.0.0-1.tgz#60f489e96d96c12620803d3677ac26413cb76a95"
dependencies:
babel-runtime "^6.18.0"
fbjs "^0.8.8"
object-assign "^4.1.0"
glamor@^2.20.22:
version "2.20.24"
resolved "https://registry.yarnpkg.com/glamor/-/glamor-2.20.24.tgz#a299af2eec687322634ba38e4a0854d8743d2041"
dependencies:
babel-runtime "^6.18.0"
fbjs "^0.8.8"
object-assign "^4.1.0"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
dependencies:
ansi-regex "^2.0.0"
has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
has@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
dependencies:
function-bind "^1.0.2"
html-comment-regex@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
hyphenate-style-name@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b"
iconv-lite@~0.4.13:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
icss-replace-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5"
ieee754@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
indexes-of@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
inline-style-prefixer@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz#c153c7e88fd84fef5c602e95a8168b2770671fe7"
dependencies:
bowser "^1.0.0"
hyphenate-style-name "^1.0.1"
is-absolute-url@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
is-function@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
is-plain-obj@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
is-plain-object@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.1.tgz#4d7ca539bc9db9b737b8acb612f2318ef92f294f"
dependencies:
isobject "^1.0.0"
is-stream@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
is-svg@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
dependencies:
html-comment-regex "^1.1.0"
isobject@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-1.0.2.tgz#f0f9b8ce92dd540fa0740882e3835a2e022ec78a"
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
js-base64@^2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
js-tokens@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
js-yaml@~3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
dependencies:
argparse "^1.0.7"
esprima "^2.6.0"
jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
json5@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
loader-utils@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.0.2.tgz#a9f923c865a974623391a8602d031137fad74830"
dependencies:
big.js "^3.1.3"
emojis-list "^2.0.0"
json5 "^0.5.0"
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
lodash.memoize@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
lodash.uniq@^4.3.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
loose-envify@^1.0.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
js-tokens "^3.0.0"
macaddress@^0.2.8:
version "0.2.8"
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
marky@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/marky/-/marky-1.1.3.tgz#b5b914c661f73355862a77acf21aadfc60745e37"
math-expression-evaluator@^1.2.14:
version "1.2.16"
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
node-fetch@^1.0.1:
version "1.6.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
normalize-range@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
normalize-url@^1.4.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.0.tgz#c2bb50035edee62cd81edb2d45da68dc25e3423e"
dependencies:
object-assign "^4.0.1"
prepend-http "^1.0.0"
query-string "^4.1.0"
sort-keys "^1.0.0"
num2fraction@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
postcss-calc@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
dependencies:
postcss "^5.0.2"
postcss-message-helpers "^2.0.0"
reduce-css-calc "^1.2.6"
postcss-colormin@^2.1.8:
version "2.2.2"
resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
dependencies:
colormin "^1.0.5"
postcss "^5.0.13"
postcss-value-parser "^3.2.3"
postcss-convert-values@^2.3.4:
version "2.6.1"
resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
dependencies:
postcss "^5.0.11"
postcss-value-parser "^3.1.2"
postcss-discard-comments@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
dependencies:
postcss "^5.0.14"
postcss-discard-duplicates@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.0.2.tgz#02be520e91571ffb10738766a981d5770989bb32"
dependencies:
postcss "^5.0.4"
postcss-discard-empty@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
dependencies:
postcss "^5.0.14"
postcss-discard-overridden@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
dependencies:
postcss "^5.0.16"
postcss-discard-unused@^2.2.1:
version "2.2.3"
resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
dependencies:
postcss "^5.0.14"
uniqs "^2.0.0"
postcss-filter-plugins@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c"
dependencies:
postcss "^5.0.4"
uniqid "^4.0.0"
postcss-merge-idents@^2.1.5:
version "2.1.7"
resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
dependencies:
has "^1.0.1"
postcss "^5.0.10"
postcss-value-parser "^3.1.1"
postcss-merge-longhand@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
dependencies:
postcss "^5.0.4"
postcss-merge-rules@^2.0.3:
version "2.1.2"
resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
dependencies:
browserslist "^1.5.2"
caniuse-api "^1.5.2"
postcss "^5.0.4"
postcss-selector-parser "^2.2.2"
vendors "^1.0.0"
postcss-message-helpers@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
postcss-minify-font-values@^1.0.2:
version "1.0.5"
resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
dependencies:
object-assign "^4.0.1"
postcss "^5.0.4"
postcss-value-parser "^3.0.2"
postcss-minify-gradients@^1.0.1:
version "1.0.5"
resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
dependencies:
postcss "^5.0.12"
postcss-value-parser "^3.3.0"
postcss-minify-params@^1.0.4:
version "1.2.2"
resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
dependencies:
alphanum-sort "^1.0.1"
postcss "^5.0.2"
postcss-value-parser "^3.0.2"
uniqs "^2.0.0"
postcss-minify-selectors@^2.0.4:
version "2.1.1"
resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
dependencies:
alphanum-sort "^1.0.2"
has "^1.0.1"
postcss "^5.0.14"
postcss-selector-parser "^2.0.0"
postcss-modules-extract-imports@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341"
dependencies:
postcss "^5.0.4"
postcss-modules-local-by-default@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce"
dependencies:
css-selector-tokenizer "^0.6.0"
postcss "^5.0.4"
postcss-modules-scope@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29"
dependencies:
css-selector-tokenizer "^0.6.0"
postcss "^5.0.4"
postcss-modules-values@^1.1.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1"
dependencies:
icss-replace-symbols "^1.0.2"
postcss "^5.0.14"
postcss-normalize-charset@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
dependencies:
postcss "^5.0.5"
postcss-normalize-url@^3.0.7:
version "3.0.8"
resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
dependencies:
is-absolute-url "^2.0.0"
normalize-url "^1.4.0"
postcss "^5.0.14"
postcss-value-parser "^3.2.3"
postcss-ordered-values@^2.1.0:
version "2.2.3"
resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
dependencies:
postcss "^5.0.4"
postcss-value-parser "^3.0.1"
postcss-reduce-idents@^2.2.2:
version "2.4.0"
resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
dependencies:
postcss "^5.0.4"
postcss-value-parser "^3.0.2"
postcss-reduce-initial@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
dependencies:
postcss "^5.0.4"
postcss-reduce-transforms@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
dependencies:
has "^1.0.1"
postcss "^5.0.8"
postcss-value-parser "^3.0.1"
postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
dependencies:
flatten "^1.0.2"
indexes-of "^1.0.1"
uniq "^1.0.1"
postcss-svgo@^2.1.1:
version "2.1.6"
resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
dependencies:
is-svg "^2.0.0"
postcss "^5.0.14"
postcss-value-parser "^3.2.3"
svgo "^0.7.0"
postcss-unique-selectors@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
dependencies:
alphanum-sort "^1.0.1"
postcss "^5.0.4"
uniqs "^2.0.0"
postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
postcss-zindex@^2.0.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
dependencies:
has "^1.0.1"
postcss "^5.0.4"
uniqs "^2.0.0"
postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.15:
version "5.2.15"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.15.tgz#a9e8685e50e06cc5b3fdea5297273246c26f5b30"
dependencies:
chalk "^1.1.3"
js-base64 "^2.1.9"
source-map "^0.5.6"
supports-color "^3.2.3"
prepend-http@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
promise@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf"
dependencies:
asap "~2.0.3"
q@^1.1.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
query-string@^4.1.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.2.tgz#ec0fd765f58a50031a3968c2431386f8947a5cdd"
dependencies:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
reduce-css-calc@^1.2.6:
version "1.3.0"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
dependencies:
balanced-match "^0.4.2"
math-expression-evaluator "^1.2.14"
reduce-function-call "^1.0.1"
reduce-function-call@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99"
dependencies:
balanced-match "^0.4.2"
regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
regenerator-runtime@^0.10.0:
version "0.10.3"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e"
regexpu-core@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
dependencies:
regenerate "^1.2.1"
regjsgen "^0.2.0"
regjsparser "^0.1.4"
regjsgen@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
regjsparser@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
dependencies:
jsesc "~0.5.0"
sax@~1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
sort-keys@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
dependencies:
is-plain-obj "^1.0.0"
source-list-map@^0.1.7:
version "0.1.8"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
source-map@^0.5.3, source-map@^0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
strip-ansi@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
dependencies:
ansi-regex "^2.0.0"
style-loader@^0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.2.tgz#74533384cf698c7104c7951150b49717adc2f3bb"
dependencies:
loader-utils "^1.0.2"
styled-components@2.0.0-7:
version "2.0.0-7"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.0.0-7.tgz#a420b99622eacec26ae3d62c1c75985791d9691b"
dependencies:
buffer "^5.0.3"
css-to-react-native "2.0.1"
fbjs "^0.8.9"
glamor "^2.20.22"
inline-style-prefixer "^2.0.5"
is-function "^1.0.1"
is-plain-object "^2.0.1"
stylis "^1.2.0"
supports-color "^3.2.3"
stylis@^1.2.0:
version "1.2.6"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-1.2.6.tgz#d7e72d3c8de52684a4f6cc82b3086e3634dc3c13"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
supports-color@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
dependencies:
has-flag "^1.0.0"
svgo@^0.7.0:
version "0.7.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
dependencies:
coa "~1.0.1"
colors "~1.1.2"
csso "~2.3.1"
js-yaml "~3.7.0"
mkdirp "~0.5.1"
sax "~1.2.1"
whet.extend "~0.9.9"
ua-parser-js@^0.7.9:
version "0.7.12"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
uniqid@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1"
dependencies:
macaddress "^0.2.8"
uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
vendors@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
whatwg-fetch@>=0.10.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e"
whet.extend@~0.9.9:
version "0.9.9"
resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"

View File

@@ -1,5 +1,6 @@
import Animated from 'animated';
import Image from '../../components/Image';
import ScrollView from '../../components/ScrollView';
import StyleSheet from '../StyleSheet';
import Text from '../../components/Text';
import View from '../../components/View';
@@ -9,6 +10,7 @@ Animated.inject.FlattenStyle(StyleSheet.flatten);
module.exports = {
...Animated,
Image: Animated.createAnimatedComponent(Image),
ScrollView: Animated.createAnimatedComponent(ScrollView),
Text: Animated.createAnimatedComponent(Text),
View: Animated.createAnimatedComponent(View)
};

View File

@@ -0,0 +1,59 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto;}
.rn-pointerEvents-12vffkv{pointer-events:none;}
.rn-pointerEvents-12vffkv *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
.rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-position-u8s1d{position:absolute}
.rn-position-bnwqim{position:relative}
.rn-right-zchlnj{right:0px}
.rn-top-ipm5af{top:0px}
.rn-alignItems-1oszu61{-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
.rn-backgroundColor-wib322{background-color:transparent}
.rn-borderTopStyle-1efd50x{border-top-style:solid}
.rn-borderRightStyle-14skgim{border-right-style:solid}
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
.rn-borderLeftStyle-mm0ijv{border-left-style:solid}
.rn-borderTopWidth-13yce4e{border-top-width:0px}
.rn-borderRightWidth-fnigne{border-right-width:0px}
.rn-borderBottomWidth-ndvcnb{border-bottom-width:0px}
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.rn-boxSizing-deolkf{box-sizing:border-box}
.rn-color-homxoj{color:inherit}
.rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
.rn-flexShrink-1qe8dj5{-webkit-flex-shrink:0;flex-shrink:0}
.rn-flexBasis-1mlwlqe{-webkit-flex-basis:auto;flex-basis:auto}
.rn-flexDirection-eqz5dr{-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;flex-direction:column}
.rn-font-1lw9tu2{font:inherit}
.rn-listStyle-1ebb2ja{list-style:none}
.rn-marginTop-1mnahxq{margin-top:0px}
.rn-marginRight-61z16t{margin-right:0px}
.rn-marginBottom-p1pxzi{margin-bottom:0px}
.rn-marginLeft-11wrixw{margin-left:0px}
.rn-minHeight-ifefl9{min-height:0px}
.rn-minWidth-bcqeeo{min-width:0px}
.rn-paddingTop-wk8lta{padding-top:0px}
.rn-paddingRight-9aemit{padding-right:0px}
.rn-paddingBottom-1mdbw0j{padding-bottom:0px}
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-textAlign-1ttztb7{text-align:inherit}
.rn-textDecoration-bauka4{text-decoration:none}
.rn-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
.rn-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
</style>"
`;

View File

@@ -10,6 +10,6 @@ describe('apis/AppRegistry/renderApplication', () => {
const { element, stylesheet } = getApplication(component, {});
expect(element).toBeTruthy();
expect(stylesheet.type).toEqual('style');
expect(stylesheet).toMatchSnapshot();
});
});

View File

@@ -8,17 +8,18 @@
import { Component } from 'react';
import invariant from 'fbjs/lib/invariant';
import { unmountComponentAtNode } from 'react/lib/ReactMount';
import { unmountComponentAtNode } from 'react-dom';
import renderApplication, { getApplication } from './renderApplication';
const emptyObject = {};
const runnables = {};
type ComponentProvider = () => Component<any, any, any>
type ComponentProvider = () => Component<any, any, any>;
type AppConfig = {
appKey: string;
component?: ComponentProvider;
run?: Function;
appKey: string,
component?: ComponentProvider,
run?: Function
};
/**
@@ -33,7 +34,7 @@ class AppRegistry {
invariant(
runnables[appKey] && runnables[appKey].getApplication,
`Application ${appKey} has not been registered. ` +
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
);
return runnables[appKey].getApplication(appParameters);
@@ -41,8 +42,10 @@ class AppRegistry {
static registerComponent(appKey: string, getComponentFunc: ComponentProvider): string {
runnables[appKey] = {
getApplication: ({ initialProps } = {}) => getApplication(getComponentFunc(), initialProps),
run: ({ initialProps, rootTag }) => renderApplication(getComponentFunc(), initialProps, rootTag)
getApplication: ({ initialProps } = emptyObject) =>
getApplication(getComponentFunc(), initialProps),
run: ({ initialProps = emptyObject, rootTag }) =>
renderApplication(getComponentFunc(), initialProps, rootTag)
};
return appKey;
}
@@ -71,14 +74,14 @@ class AppRegistry {
console.log(
`Running application "${appKey}" with appParams: ${JSON.stringify(params)}. ` +
`development-level warnings are ${isDevelopment ? 'ON' : 'OFF'}, ` +
`performance optimizations are ${isDevelopment ? 'OFF' : 'ON'}`
`development-level warnings are ${isDevelopment ? 'ON' : 'OFF'}, ` +
`performance optimizations are ${isDevelopment ? 'OFF' : 'ON'}`
);
invariant(
runnables[appKey] && runnables[appKey].run,
`Application "${appKey}" has not been registered. ` +
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
);
runnables[appKey].run(appParameters);

View File

@@ -7,31 +7,26 @@
*/
import invariant from 'fbjs/lib/invariant';
import { render } from 'react/lib/ReactMount';
import { render } from 'react-dom/lib/ReactMount';
import ReactNativeApp from './ReactNativeApp';
import StyleSheet from '../../apis/StyleSheet';
import React, { Component } from 'react';
export default function renderApplication(RootComponent: Component, initialProps: Object, rootTag: any) {
export default function renderApplication(
RootComponent: Component,
initialProps: Object,
rootTag: any
) {
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
const component = (
<ReactNativeApp
initialProps={initialProps}
rootComponent={RootComponent}
rootTag={rootTag}
/>
<ReactNativeApp initialProps={initialProps} rootComponent={RootComponent} rootTag={rootTag} />
);
render(component, rootTag);
}
export function getApplication(RootComponent: Component, initialProps: Object): Object {
const element = (
<ReactNativeApp
initialProps={initialProps}
rootComponent={RootComponent}
/>
);
const stylesheet = StyleSheet.render();
const element = <ReactNativeApp initialProps={initialProps} rootComponent={RootComponent} />;
const stylesheet = StyleSheet.renderToString();
return { element, stylesheet };
}

View File

@@ -6,7 +6,9 @@ describe('apis/AppState', () => {
const handler = () => {};
afterEach(() => {
try { AppState.removeEventListener('change', handler); } catch (e) {}
try {
AppState.removeEventListener('change', handler);
} catch (e) {}
});
describe('addEventListener', () => {

View File

@@ -2,7 +2,7 @@ import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import findIndex from 'array-find-index';
import invariant from 'fbjs/lib/invariant';
const EVENT_TYPES = [ 'change' ];
const EVENT_TYPES = ['change'];
const VISIBILITY_CHANGE_EVENT = 'visibilitychange';
const AppStates = {
@@ -13,7 +13,7 @@ const AppStates = {
const listeners = [];
class AppState {
static isSupported = ExecutionEnvironment.canUseDOM && document.visibilityState
static isSupported = ExecutionEnvironment.canUseDOM && document.visibilityState;
static get currentState() {
if (!AppState.isSupported) {
@@ -32,18 +32,29 @@ class AppState {
static addEventListener(type: string, handler: Function) {
if (AppState.isSupported) {
invariant(EVENT_TYPES.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
invariant(
EVENT_TYPES.indexOf(type) !== -1,
'Trying to subscribe to unknown event: "%s"',
type
);
const callback = () => handler(AppState.currentState);
listeners.push([ handler, callback ]);
listeners.push([handler, callback]);
document.addEventListener(VISIBILITY_CHANGE_EVENT, callback, false);
}
}
static removeEventListener(type: string, handler: Function) {
if (AppState.isSupported) {
invariant(EVENT_TYPES.indexOf(type) !== -1, 'Trying to remove listener for unknown event: "%s"', type);
const listenerIndex = findIndex(listeners, (pair) => pair[0] === handler);
invariant(listenerIndex !== -1, 'Trying to remove AppState listener for unregistered handler');
invariant(
EVENT_TYPES.indexOf(type) !== -1,
'Trying to remove listener for unknown event: "%s"',
type
);
const listenerIndex = findIndex(listeners, pair => pair[0] === handler);
invariant(
listenerIndex !== -1,
'Trying to remove AppState listener for unregistered handler'
);
const callback = listeners[listenerIndex][1];
document.removeEventListener(VISIBILITY_CHANGE_EVENT, callback, false);
listeners.splice(listenerIndex, 1);

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/AsyncStorage mergeLocalStorageItem should have same behavior as react-native 1`] = `
Object {
"age": 31,

View File

@@ -32,7 +32,7 @@ const originalLocalStorage = window.localStorage;
describe('apis/AsyncStorage', () => {
describe('mergeLocalStorageItem', () => {
test('should have same behavior as react-native', (done) => {
test('should have same behavior as react-native', done => {
window.localStorage = mockLocalStorage;
// https://facebook.github.io/react-native/docs/asyncstorage.html
const UID123_object = {
@@ -45,30 +45,33 @@ describe('apis/AsyncStorage', () => {
traits: { eyes: 'blue', shoe_size: 10 }
};
waterfall([
(cb) => {
AsyncStorage.setItem('UID123', JSON.stringify(UID123_object))
.then(() => cb(null))
.catch(cb);
},
(cb) => {
AsyncStorage.mergeItem('UID123', JSON.stringify(UID123_delta))
.then(() => cb(null))
.catch(cb);
},
(cb) => {
AsyncStorage.getItem('UID123')
.then((result) => {
cb(null, JSON.parse(result));
})
.catch(cb);
waterfall(
[
cb => {
AsyncStorage.setItem('UID123', JSON.stringify(UID123_object))
.then(() => cb(null))
.catch(cb);
},
cb => {
AsyncStorage.mergeItem('UID123', JSON.stringify(UID123_delta))
.then(() => cb(null))
.catch(cb);
},
cb => {
AsyncStorage.getItem('UID123')
.then(result => {
cb(null, JSON.parse(result));
})
.catch(cb);
}
],
(err, result) => {
expect(err).toEqual(null);
expect(result).toMatchSnapshot();
window.localStorage = originalLocalStorage;
done();
}
], (err, result) => {
expect(err).toEqual(null);
expect(result).toMatchSnapshot();
window.localStorage = originalLocalStorage;
done();
});
);
});
});
});

View File

@@ -82,12 +82,13 @@ class AsyncStorage {
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
*/
static multiGet(keys: Array<string>) {
const promises = keys.map((key) => AsyncStorage.getItem(key));
const promises = keys.map(key => AsyncStorage.getItem(key));
return Promise.all(promises).then(
(result) => Promise.resolve(result.map((value, i) => [ keys[i], value ])),
(error) => Promise.reject(error)
);
return Promise.all(promises)
.then(
result => Promise.resolve(result.map((value, i) => [keys[i], value])),
error => Promise.reject(error)
);
}
/**
@@ -97,24 +98,18 @@ class AsyncStorage {
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
*/
static multiMerge(keyValuePairs: Array<Array<string>>) {
const promises = keyValuePairs.map((item) => AsyncStorage.mergeItem(item[0], item[1]));
const promises = keyValuePairs.map(item => AsyncStorage.mergeItem(item[0], item[1]));
return Promise.all(promises).then(
() => Promise.resolve(null),
(error) => Promise.reject(error)
);
return Promise.all(promises).then(() => Promise.resolve(null), error => Promise.reject(error));
}
/**
* Delete all the keys in the `keys` array.
*/
static multiRemove(keys: Array<string>) {
const promises = keys.map((key) => AsyncStorage.removeItem(key));
const promises = keys.map(key => AsyncStorage.removeItem(key));
return Promise.all(promises).then(
() => Promise.resolve(null),
(error) => Promise.reject(error)
);
return Promise.all(promises).then(() => Promise.resolve(null), error => Promise.reject(error));
}
/**
@@ -122,12 +117,9 @@ class AsyncStorage {
* multiSet([['k1', 'val1'], ['k2', 'val2']])
*/
static multiSet(keyValuePairs: Array<Array<string>>) {
const promises = keyValuePairs.map((item) => AsyncStorage.setItem(item[0], item[1]));
const promises = keyValuePairs.map(item => AsyncStorage.setItem(item[0], item[1]));
return Promise.all(promises).then(
() => Promise.resolve(null),
(error) => Promise.reject(error)
);
return Promise.all(promises).then(() => Promise.resolve(null), error => Promise.reject(error));
}
/**

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* web stub for BackAndroid.android.js
*
* @providesModule BackAndroid
*/
'use strict';
function emptyFunction() {}
const BackAndroid = {
exitApp: emptyFunction,
addEventListener() {
return {
remove: emptyFunction
};
},
removeEventListener: emptyFunction
};
module.exports = BackAndroid;

View File

@@ -0,0 +1,21 @@
class Clipboard {
static getString() {
return Promise.resolve('');
}
static setString(text) {
let success = false;
const textField = document.createElement('textarea');
textField.innerText = text;
document.body.appendChild(textField);
textField.select();
try {
document.execCommand('copy');
success = true;
} catch (e) {}
document.body.removeChild(textField);
return success;
}
}
module.exports = Clipboard;

View File

@@ -6,11 +6,11 @@
* @flow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import debounce from 'debounce';
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import invariant from 'fbjs/lib/invariant';
const win = ExecutionEnvironment.canUseDOM ? window : { screen: {} };
const win = canUseDOM ? window : { screen: {} };
const dimensions = {};
@@ -38,6 +38,9 @@ class Dimensions {
}
Dimensions.set();
ExecutionEnvironment.canUseDOM && window.addEventListener('resize', debounce(Dimensions.set, 50));
if (canUseDOM) {
window.addEventListener('resize', debounce(Dimensions.set, 16), false);
}
module.exports = Dimensions;

View File

@@ -5,7 +5,7 @@ type I18nManagerStatus = {
forceRTL: (forceRTL: boolean) => {},
setRTL: (setRTL: boolean) => {},
isRTL: boolean
}
};
let isPreferredLanguageRTL = false;
let isRTLAllowed = true;

View File

@@ -17,10 +17,7 @@ const InteractionManager = {
* Schedule a function to run after all interactions have completed.
*/
runAfterInteractions(callback: Function) {
invariant(
typeof callback === 'function',
'Must specify a function to schedule.'
);
invariant(typeof callback === 'function', 'Must specify a function to schedule.');
callback();
},
@@ -35,10 +32,7 @@ const InteractionManager = {
* Notify manager that an interaction has completed.
*/
clearInteractionHandle(handle) {
invariant(
!!handle,
'Must provide a handle to clear.'
);
invariant(!!handle, 'Must provide a handle to clear.');
},
addListener: () => {}

45
src/apis/Linking/index.js Normal file
View File

@@ -0,0 +1,45 @@
const Linking = {
addEventListener() {},
removeEventListener() {},
canOpenURL() {
return Promise.resolve(true);
},
getInitialURL() {
return Promise.resolve('');
},
openURL(url) {
try {
iframeOpen(url);
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
}
};
/**
* Tabs opened using JavaScript may redirect the parent tab using
* `window.opener.location`, ignoring cross-origin restrictions and enabling
* phishing attacks.
*
* Safari requires that we open the url by injecting a hidden iframe that calls
* window.open(), then removes the iframe from the DOM.
*
* https://mathiasbynens.github.io/rel-noopener/
*/
const iframeOpen = url => {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
const script = iframeDoc.createElement('script');
script.text = `
window.parent = null; window.top = null; window.frameElement = null;
var child = window.open("${url}"); child.opener = null;
`;
iframeDoc.body.appendChild(script);
document.body.removeChild(iframe);
};
module.exports = Linking;

View File

@@ -7,7 +7,9 @@ describe('apis/NetInfo', () => {
const handler = () => {};
afterEach(() => {
try { NetInfo.isConnected.removeEventListener('change', handler); } catch (e) {}
try {
NetInfo.isConnected.removeEventListener('change', handler);
} catch (e) {}
});
describe('addEventListener', () => {

View File

@@ -10,13 +10,12 @@ import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import findIndex from 'array-find-index';
import invariant from 'fbjs/lib/invariant';
const connection = ExecutionEnvironment.canUseDOM && (
window.navigator.connection ||
window.navigator.mozConnection ||
window.navigator.webkitConnection
);
const connection = ExecutionEnvironment.canUseDOM &&
(window.navigator.connection ||
window.navigator.mozConnection ||
window.navigator.webkitConnection);
const eventTypes = [ 'change' ];
const eventTypes = ['change'];
const connectionListeners = [];
@@ -28,7 +27,9 @@ const NetInfo = {
addEventListener(type: string, handler: Function): { remove: () => void } {
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
if (!connection) {
console.error('Network Connection API is not supported. Not listening for connection type changes.');
console.error(
'Network Connection API is not supported. Not listening for connection type changes.'
);
return {
remove: () => {}
};
@@ -42,7 +43,9 @@ const NetInfo = {
removeEventListener(type: string, handler: Function): void {
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
if (!connection) { return; }
if (!connection) {
return;
}
connection.removeEventListener(type, handler);
},
@@ -58,10 +61,14 @@ const NetInfo = {
isConnected: {
addEventListener(type: string, handler: Function): { remove: () => void } {
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
invariant(
eventTypes.indexOf(type) !== -1,
'Trying to subscribe to unknown event: "%s"',
type
);
const onlineCallback = () => handler(true);
const offlineCallback = () => handler(false);
connectionListeners.push([ handler, onlineCallback, offlineCallback ]);
connectionListeners.push([handler, onlineCallback, offlineCallback]);
window.addEventListener('online', onlineCallback, false);
window.addEventListener('offline', offlineCallback, false);
@@ -72,11 +79,18 @@ const NetInfo = {
},
removeEventListener(type: string, handler: Function): void {
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
invariant(
eventTypes.indexOf(type) !== -1,
'Trying to subscribe to unknown event: "%s"',
type
);
const listenerIndex = findIndex(connectionListeners, (pair) => pair[0] === handler);
invariant(listenerIndex !== -1, 'Trying to remove NetInfo connection listener for unregistered handler');
const [ , onlineCallback, offlineCallback ] = connectionListeners[listenerIndex];
const listenerIndex = findIndex(connectionListeners, pair => pair[0] === handler);
invariant(
listenerIndex !== -1,
'Trying to remove NetInfo connection listener for unregistered handler'
);
const [, onlineCallback, offlineCallback] = connectionListeners[listenerIndex];
window.removeEventListener('online', onlineCallback, false);
window.removeEventListener('offline', offlineCallback, false);

View File

@@ -4,18 +4,14 @@
* All rights reserved.
*/
"use strict";
'use strict';
var TouchHistoryMath = require('react/lib/TouchHistoryMath');
var TouchHistoryMath = require('react-dom/lib/TouchHistoryMath');
var currentCentroidXOfTouchesChangedAfter =
TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
var currentCentroidYOfTouchesChangedAfter =
TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
var previousCentroidXOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
var previousCentroidYOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
var currentCentroidXOfTouchesChangedAfter = TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
var currentCentroidYOfTouchesChangedAfter = TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
var previousCentroidXOfTouchesChangedAfter = TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
var previousCentroidYOfTouchesChangedAfter = TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
var currentCentroidX = TouchHistoryMath.currentCentroidX;
var currentCentroidY = TouchHistoryMath.currentCentroidY;
@@ -115,7 +111,6 @@ var currentCentroidY = TouchHistoryMath.currentCentroidY;
*/
var PanResponder = {
/**
*
* A graphical explanation of the touch data flow:
@@ -236,8 +231,7 @@ var PanResponder = {
var nextDY = gestureState.dy + (y - prevY);
// TODO: This must be filtered intelligently.
var dt =
(touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo);
var dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
gestureState.vx = (nextDX - gestureState.dx) / dt;
gestureState.vy = (nextDY - gestureState.dy) / dt;
@@ -281,17 +275,19 @@ var PanResponder = {
create: function(config) {
var gestureState = {
// Useful for debugging
stateID: Math.random(),
stateID: Math.random()
};
PanResponder._initializeGestureState(gestureState);
var panHandlers = {
onStartShouldSetResponder: function(e) {
return config.onStartShouldSetPanResponder === undefined ? false :
config.onStartShouldSetPanResponder(e, gestureState);
return config.onStartShouldSetPanResponder === undefined
? false
: config.onStartShouldSetPanResponder(e, gestureState);
},
onMoveShouldSetResponder: function(e) {
return config.onMoveShouldSetPanResponder === undefined ? false :
config.onMoveShouldSetPanResponder(e, gestureState);
return config.onMoveShouldSetPanResponder === undefined
? false
: config.onMoveShouldSetPanResponder(e, gestureState);
},
onStartShouldSetResponderCapture: function(e) {
// TODO: Actually, we should reinitialize the state any time
@@ -300,13 +296,15 @@ var PanResponder = {
if (e.nativeEvent.touches.length === 1) {
PanResponder._initializeGestureState(gestureState);
}
}
else if (e.nativeEvent.originalEvent && e.nativeEvent.originalEvent.type === 'mousedown') {
} else if (
e.nativeEvent.originalEvent && e.nativeEvent.originalEvent.type === 'mousedown'
) {
PanResponder._initializeGestureState(gestureState);
}
gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;
return config.onStartShouldSetPanResponderCapture !== undefined ?
config.onStartShouldSetPanResponderCapture(e, gestureState) : false;
return config.onStartShouldSetPanResponderCapture !== undefined
? config.onStartShouldSetPanResponderCapture(e, gestureState)
: false;
},
onMoveShouldSetResponderCapture: function(e) {
@@ -318,8 +316,9 @@ var PanResponder = {
return false;
}
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
return config.onMoveShouldSetPanResponderCapture ?
config.onMoveShouldSetPanResponderCapture(e, gestureState) : false;
return config.onMoveShouldSetPanResponderCapture
? config.onMoveShouldSetPanResponderCapture(e, gestureState)
: false;
},
onResponderGrant: function(e) {
@@ -329,8 +328,9 @@ var PanResponder = {
gestureState.dy = 0;
config.onPanResponderGrant && config.onPanResponderGrant(e, gestureState);
// TODO: t7467124 investigate if this can be removed
return config.onShouldBlockNativeResponder === undefined ? true :
config.onShouldBlockNativeResponder();
return config.onShouldBlockNativeResponder === undefined
? true
: config.onShouldBlockNativeResponder();
},
onResponderReject: function(e) {
@@ -368,18 +368,18 @@ var PanResponder = {
},
onResponderTerminate: function(e) {
config.onPanResponderTerminate &&
config.onPanResponderTerminate(e, gestureState);
config.onPanResponderTerminate && config.onPanResponderTerminate(e, gestureState);
PanResponder._initializeGestureState(gestureState);
},
onResponderTerminationRequest: function(e) {
return config.onPanResponderTerminationRequest === undefined ? true :
config.onPanResponderTerminationRequest(e, gestureState);
},
return config.onPanResponderTerminationRequest === undefined
? true
: config.onPanResponderTerminationRequest(e, gestureState);
}
};
return {panHandlers: panHandlers};
},
return { panHandlers: panHandlers };
}
};
module.exports = PanResponder;

View File

@@ -1,6 +1,6 @@
const Platform = {
OS: 'web',
select: (obj: Object) => obj.web
select: (obj: Object) => 'web' in obj ? obj.web : obj.default
};
module.exports = Platform;

View File

@@ -0,0 +1,137 @@
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import generateCss from './generateCss';
import hash from './hash';
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
import staticCss from './staticCss';
const emptyObject = {};
const STYLE_ELEMENT_ID = 'react-native-stylesheet';
const createClassName = (prop, value) => {
const hashed = hash(prop + value);
return process.env.NODE_ENV !== 'production' ? `rn-${prop}-${hashed}` : `rn-${hashed}`;
};
const createCssRule = (className, prop, value) => {
const css = generateCss({ [prop]: value });
const selector = `.${className}`;
return `${selector}{${css}}`;
};
const pointerEvents = {
auto: createClassName('pointerEvents', 'auto'),
boxNone: createClassName('pointerEvents', 'box-none'),
boxOnly: createClassName('pointerEvents', 'box-only'),
none: createClassName('pointerEvents', 'none')
};
const pointerEventsCss = `.${pointerEvents.auto}{pointer-events:auto;}\n` +
`.${pointerEvents.boxNone}{pointer-events:none;}\n` +
`.${pointerEvents.boxNone} *{pointer-events:auto;}\n` +
`.${pointerEvents.boxOnly}{pointer-events:auto;}\n` +
`.${pointerEvents.boxOnly} *{pointer-events:none;}\n` +
`.${pointerEvents.none}{pointer-events:none;}`;
class StyleManager {
constructor() {
// custom pointer event values are implemented using descendent selectors,
// so we manually create the CSS and pre-register the declarations
const pointerEventsPropName = 'pointerEvents';
this.cache = {
byClassName: {
[pointerEvents.auto]: { prop: pointerEventsPropName, value: 'auto' },
[pointerEvents.boxNone]: {
prop: pointerEventsPropName,
value: 'box-none'
},
[pointerEvents.boxOnly]: {
prop: pointerEventsPropName,
value: 'box-only'
},
[pointerEvents.none]: { prop: pointerEventsPropName, value: 'none' }
},
byProp: {
pointerEvents: {
auto: pointerEvents.auto,
'box-none': pointerEvents.boxNone,
'box-only': pointerEvents.boxOnly,
none: pointerEvents.none
}
}
};
// on the client we check for an existing style sheet before injecting style sheets
if (canUseDOM) {
const prerenderedStyleSheet = document.getElementById(STYLE_ELEMENT_ID);
if (prerenderedStyleSheet) {
this.mainSheet = prerenderedStyleSheet;
} else {
document.head.insertAdjacentHTML('afterbegin', this.getStyleSheetHtml());
this.mainSheet = document.getElementById(STYLE_ELEMENT_ID);
}
}
}
getClassName(prop, value) {
const cache = this.cache.byProp;
return cache[prop] && cache[prop].hasOwnProperty(value) && cache[prop][value];
}
getDeclaration(className) {
const cache = this.cache.byClassName;
return cache[className] || emptyObject;
}
getStyleSheetHtml() {
const cache = this.cache.byProp;
const mainSheetTextContext = Object.keys(cache)
.reduce(
(rules, prop) => {
if (prop !== 'pointerEvents') {
Object.keys(cache[prop]).forEach(value => {
const className = this.getClassName(prop, value);
rules.push(createCssRule(className, prop, value));
});
}
return rules;
},
[]
)
.join('\n');
const staticSheet = `<style id="react-native-stylesheet-static">\n${staticCss}\n${pointerEventsCss}\n</style>`;
const mainSheet = `<style id="${STYLE_ELEMENT_ID}">\n${mainSheetTextContext}\n</style>`;
return `${staticSheet}\n${mainSheet}`;
}
setDeclaration(prop, value) {
let className = this.getClassName(prop, value);
if (!className) {
className = createClassName(prop, value);
this._addToCache(className, prop, value);
if (canUseDOM) {
requestAnimationFrame(() => {
const sheet = this.mainSheet.sheet;
// avoid injecting if the rule already exists (e.g., server rendered, hot reload)
if (this.mainSheet.textContent.indexOf(className) === -1) {
const rule = createCssRule(className, prop, value);
sheet.insertRule(rule, sheet.cssRules.length);
}
});
}
}
return className;
}
_addToCache(className, prop, value) {
const cache = this.cache;
if (!cache.byProp[prop]) {
cache.byProp[prop] = {};
}
cache.byProp[prop][value] = className;
cache.byClassName[className] = { prop, value };
}
}
module.exports = StyleManager;

View File

@@ -0,0 +1,162 @@
/**
* WARNING: changes to this file in particular can cause significant changes to
* the results of render performance benchmarks.
*/
import createReactDOMStyle from './createReactDOMStyle';
import flattenArray from '../../modules/flattenArray';
import flattenStyle from './flattenStyle';
import I18nManager from '../I18nManager';
import mapKeyValue from '../../modules/mapKeyValue';
import prefixInlineStyles from './prefixInlineStyles';
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
import StyleManager from './StyleManager';
const createCacheKey = id => {
const prefix = I18nManager.isRTL ? 'rtl' : 'ltr';
return `${prefix}-${id}`;
};
const classListToString = list => list.join(' ').trim();
class StyleRegistry {
constructor() {
this.cache = {};
this.styleManager = new StyleManager();
}
getStyleSheetHtml() {
return this.styleManager.getStyleSheetHtml();
}
/**
* Registers and precaches a React Native style object to HTML class names
*/
register(flatStyle) {
const id = ReactNativePropRegistry.register(flatStyle);
const key = createCacheKey(id);
const style = createReactDOMStyle(flatStyle);
const classList = mapKeyValue(style, (prop, value) => {
if (value != null) {
return this.styleManager.setDeclaration(prop, value);
}
});
const className = classList.join(' ').trim();
this.cache[key] = { classList, className };
return id;
}
/**
* Resolves a React Native style object to DOM attributes
*/
resolve(reactNativeStyle) {
if (!reactNativeStyle) {
return undefined;
}
// fast and cachable
if (typeof reactNativeStyle === 'number') {
const key = createCacheKey(reactNativeStyle);
return this._resolveStyleIfNeeded(key, reactNativeStyle);
}
// resolve a plain RN style object
if (!Array.isArray(reactNativeStyle)) {
return this._resolveStyle(reactNativeStyle);
}
// flatten the style array
// cache resolved props when all styles are registered
// otherwise fallback to resolving
const flatArray = flattenArray(reactNativeStyle);
let isArrayOfNumbers = true;
for (let i = 0; i < flatArray.length; i++) {
if (typeof flatArray[i] !== 'number') {
isArrayOfNumbers = false;
break;
}
}
const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
return this._resolveStyleIfNeeded(key, flatArray);
}
/**
* Resolves a React Native style object to DOM attributes, accounting for
* the existing styles applied to the DOM node
*/
resolveStateful(reactNativeStyle, domClassList) {
const previousReactNativeStyle = {};
const preservedClassNames = [];
// Convert the existing classList to a React Native style and preserve any
// unrecognized classNames.
domClassList.forEach(className => {
const { prop, value } = this.styleManager.getDeclaration(className);
if (prop) {
previousReactNativeStyle[prop] = value;
} else {
preservedClassNames.push(className);
}
});
// Resolve the two React Native styles.
const { classList, style = {} } = this.resolve([previousReactNativeStyle, reactNativeStyle]);
// Because this is used in stateful operations we need to remove any
// existing inline styles that would override the classNames.
classList.forEach(className => {
const { prop } = this.styleManager.getDeclaration(className);
style[prop] = null;
});
classList.push(preservedClassNames);
const className = classListToString(classList);
return { className, style };
}
/**
* Resolves a React Native style object
*/
_resolveStyle(reactNativeStyle) {
const domStyle = createReactDOMStyle(flattenStyle(reactNativeStyle));
const props = Object.keys(domStyle).reduce((props, styleProp) => {
const value = domStyle[styleProp];
if (value != null) {
const className = this.styleManager.getClassName(styleProp, value);
if (className) {
props.classList.push(className);
} else {
if (!props.style) {
props.style = {};
}
// 4x slower render
props.style[styleProp] = value;
}
}
return props;
}, { classList: [] });
props.className = classListToString(props.classList);
if (props.style) {
props.style = prefixInlineStyles(props.style);
}
return props;
}
/**
* Caching layer over 'resolveStyle'
*/
_resolveStyleIfNeeded(key, style) {
if (key) {
if (!this.cache[key]) {
// slow: convert style object to props and cache
this.cache[key] = this._resolveStyle(style);
}
return this.cache[key];
}
return this._resolveStyle(style);
}
}
module.exports = StyleRegistry;

View File

@@ -7,13 +7,14 @@
* @flow
*/
import { PropTypes } from 'react'
import ImageStylePropTypes from '../../components/Image/ImageStylePropTypes'
import ReactPropTypeLocations from 'react/lib/ReactPropTypeLocations'
import ReactPropTypesSecret from 'react/lib/ReactPropTypesSecret'
import TextStylePropTypes from '../../components/Text/TextStylePropTypes'
import ViewStylePropTypes from '../../components/View/ViewStylePropTypes'
import warning from 'fbjs/lib/warning'
import { PropTypes } from 'react';
import ImageStylePropTypes from '../../components/Image/ImageStylePropTypes';
import ReactPropTypeLocations from 'react-dom/lib/ReactPropTypeLocations';
import ReactPropTypesSecret from 'react-dom/lib/ReactPropTypesSecret';
import TextInputStylePropTypes from '../../components/TextInput/TextInputStylePropTypes';
import TextStylePropTypes from '../../components/Text/TextStylePropTypes';
import ViewStylePropTypes from '../../components/View/ViewStylePropTypes';
import warning from 'fbjs/lib/warning';
class StyleSheetValidation {
static validateStyleProp(prop, style, caller) {
@@ -57,25 +58,29 @@ class StyleSheetValidation {
var styleError = function(message1, style, caller?, message2?) {
warning(
false,
message1 + '\n' + (caller || '<<unknown>>') + ': ' +
JSON.stringify(style, null, ' ') + (message2 || '')
message1 +
'\n' +
(caller || '<<unknown>>') +
': ' +
JSON.stringify(style, null, ' ') +
(message2 || '')
);
};
var allStylePropTypes = {};
StyleSheetValidation.addValidStylePropTypes(ImageStylePropTypes)
StyleSheetValidation.addValidStylePropTypes(TextStylePropTypes)
StyleSheetValidation.addValidStylePropTypes(ViewStylePropTypes)
StyleSheetValidation.addValidStylePropTypes(ImageStylePropTypes);
StyleSheetValidation.addValidStylePropTypes(TextStylePropTypes);
StyleSheetValidation.addValidStylePropTypes(TextInputStylePropTypes);
StyleSheetValidation.addValidStylePropTypes(ViewStylePropTypes);
StyleSheetValidation.addValidStylePropTypes({
appearance: PropTypes.string,
clear: PropTypes.string,
cursor: PropTypes.string,
display: PropTypes.string,
float: PropTypes.oneOf([ 'left', 'none', 'right' ]),
font: PropTypes.string, /* @private */
float: PropTypes.oneOf(['left', 'none', 'right']),
font: PropTypes.string /* @private */,
listStyle: PropTypes.string,
WebkitOverflowScrolling: PropTypes.string /* @private */
})
pointerEvents: PropTypes.string
});
module.exports = StyleSheetValidation
module.exports = StyleSheetValidation;

View File

@@ -0,0 +1,31 @@
/* eslint-env jasmine, jest */
import StyleManager from '../StyleManager';
let styleManager;
describe('apis/StyleSheet/StyleManager', () => {
beforeEach(() => {
styleManager = new StyleManager();
});
test('getClassName', () => {
expect(styleManager.getClassName('pointerEvents', 'box-only')).toMatchSnapshot();
const className = styleManager.setDeclaration('width', '100px');
expect(styleManager.getClassName('width', '100px')).toEqual(className);
});
test('getDeclaration', () => {
const className = styleManager.setDeclaration('width', '100px');
expect(styleManager.getDeclaration(className)).toEqual({
prop: 'width',
value: '100px'
});
});
test('getStyleSheetHtml', () => {
expect(styleManager.getStyleSheetHtml()).toMatchSnapshot();
styleManager.setDeclaration('width', '100px');
expect(styleManager.getStyleSheetHtml()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,76 @@
/* eslint-env jasmine, jest */
import StyleRegistry from '../StyleRegistry';
let styleRegistry;
describe('apis/StyleSheet/StyleRegistry', () => {
beforeEach(() => {
styleRegistry = new StyleRegistry();
});
test('register', () => {
const style = { opacity: 0 };
const id = styleRegistry.register(style);
expect(typeof id === 'number').toBe(true);
});
describe('resolve', () => {
const styleA = { borderWidth: 0, borderColor: 'red', width: 100 };
const styleB = {
position: 'absolute',
left: 50,
pointerEvents: 'box-only'
};
const styleC = { width: 200 };
const testResolve = (a, b, c) => {
// no common properties, different resolving order, same result
const resolve1 = styleRegistry.resolve([a, b]);
expect(resolve1).toMatchSnapshot();
const resolve2 = styleRegistry.resolve([b, a]);
expect(resolve1).toEqual(resolve2);
// common properties, different resolving order, different result
const resolve3 = styleRegistry.resolve([a, b, c]);
expect(resolve3).toMatchSnapshot();
const resolve4 = styleRegistry.resolve([c, a, b]);
expect(resolve4).toMatchSnapshot();
expect(resolve3).not.toEqual(resolve4);
};
test('with register, resolves to className', () => {
const a = styleRegistry.register(styleA);
const b = styleRegistry.register(styleB);
const c = styleRegistry.register(styleC);
testResolve(a, b, c);
});
test('with register, resolves to mixed', () => {
const a = styleA;
const b = styleRegistry.register(styleB);
const c = styleRegistry.register(styleC);
testResolve(a, b, c);
});
test('without register, resolves to inline styles', () => {
testResolve(styleA, styleB, styleC);
});
});
test('resolveStateful', () => {
// generate a classList to act as pre-existing DOM state
const mockStyle = styleRegistry.register({
borderWidth: 0,
borderColor: 'red',
width: 100
});
const { classList: domClassList } = styleRegistry.resolve(mockStyle);
domClassList.unshift('external-className');
expect(domClassList).toMatchSnapshot();
// test result
const result = styleRegistry.resolveStateful({ borderWidth: 1, opacity: 1 }, domClassList);
expect(result).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,43 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/StyleManager getClassName 1`] = `"rn-pointerEvents-ah5dr5"`;
exports[`apis/StyleSheet/StyleManager getStyleSheetHtml 1`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto;}
.rn-pointerEvents-12vffkv{pointer-events:none;}
.rn-pointerEvents-12vffkv *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
</style>"
`;
exports[`apis/StyleSheet/StyleManager getStyleSheetHtml 2`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto;}
.rn-pointerEvents-12vffkv{pointer-events:none;}
.rn-pointerEvents-12vffkv *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
.rn-width-b8lwoo{width:100px}
</style>"
`;

View File

@@ -0,0 +1,226 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to className 1`] = `
Object {
"classList": Array [
"rn-borderTopColor-1gxhl28",
"rn-borderRightColor-knoah9",
"rn-borderBottomColor-1ani3fp",
"rn-borderLeftColor-ribj9x",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-position-u8s1d",
"rn-width-b8lwoo",
],
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d rn-width-b8lwoo",
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to className 2`] = `
Object {
"classList": Array [
"rn-borderTopColor-1gxhl28",
"rn-borderRightColor-knoah9",
"rn-borderBottomColor-1ani3fp",
"rn-borderLeftColor-ribj9x",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-position-u8s1d",
"rn-width-l0gwng",
],
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d rn-width-l0gwng",
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to className 3`] = `
Object {
"classList": Array [
"rn-borderTopColor-1gxhl28",
"rn-borderRightColor-knoah9",
"rn-borderBottomColor-1ani3fp",
"rn-borderLeftColor-ribj9x",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-position-u8s1d",
"rn-width-b8lwoo",
],
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d rn-width-b8lwoo",
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to mixed 1`] = `
Object {
"classList": Array [
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-position-u8s1d",
],
"className": "rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
"borderLeftColor": "red",
"borderLeftWidth": "0px",
"borderRightColor": "red",
"borderRightWidth": "0px",
"borderTopColor": "red",
"borderTopWidth": "0px",
"width": "100px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to mixed 2`] = `
Object {
"classList": Array [
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-position-u8s1d",
"rn-width-l0gwng",
],
"className": "rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d rn-width-l0gwng",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
"borderLeftColor": "red",
"borderLeftWidth": "0px",
"borderRightColor": "red",
"borderRightWidth": "0px",
"borderTopColor": "red",
"borderTopWidth": "0px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to mixed 3`] = `
Object {
"classList": Array [
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-position-u8s1d",
],
"className": "rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
"borderLeftColor": "red",
"borderLeftWidth": "0px",
"borderRightColor": "red",
"borderRightWidth": "0px",
"borderTopColor": "red",
"borderTopWidth": "0px",
"width": "100px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve without register, resolves to inline styles 1`] = `
Object {
"classList": Array [
"rn-pointerEvents-ah5dr5",
],
"className": "rn-pointerEvents-ah5dr5",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
"borderLeftColor": "red",
"borderLeftWidth": "0px",
"borderRightColor": "red",
"borderRightWidth": "0px",
"borderTopColor": "red",
"borderTopWidth": "0px",
"left": "50px",
"position": "absolute",
"width": "100px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve without register, resolves to inline styles 2`] = `
Object {
"classList": Array [
"rn-pointerEvents-ah5dr5",
],
"className": "rn-pointerEvents-ah5dr5",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
"borderLeftColor": "red",
"borderLeftWidth": "0px",
"borderRightColor": "red",
"borderRightWidth": "0px",
"borderTopColor": "red",
"borderTopWidth": "0px",
"left": "50px",
"position": "absolute",
"width": "200px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve without register, resolves to inline styles 3`] = `
Object {
"classList": Array [
"rn-pointerEvents-ah5dr5",
],
"className": "rn-pointerEvents-ah5dr5",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
"borderLeftColor": "red",
"borderLeftWidth": "0px",
"borderRightColor": "red",
"borderRightWidth": "0px",
"borderTopColor": "red",
"borderTopWidth": "0px",
"left": "50px",
"position": "absolute",
"width": "100px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful 1`] = `
Array [
"external-className",
"rn-borderTopColor-1gxhl28",
"rn-borderRightColor-knoah9",
"rn-borderBottomColor-1ani3fp",
"rn-borderLeftColor-ribj9x",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-width-b8lwoo",
]
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful 2`] = `
Object {
"className": "rn-borderBottomColor-1ani3fp rn-borderBottomWidth-ndvcnb rn-borderLeftColor-ribj9x rn-borderLeftWidth-gxnn5r rn-borderRightColor-knoah9 rn-borderRightWidth-fnigne rn-borderTopColor-1gxhl28 rn-borderTopWidth-13yce4e rn-width-b8lwoo external-className",
"style": Object {
"borderBottomColor": null,
"borderBottomWidth": null,
"borderLeftColor": null,
"borderLeftWidth": null,
"borderRightColor": null,
"borderRightWidth": null,
"borderTopColor": null,
"borderTopWidth": null,
"opacity": 1,
"width": null,
},
}
`;

View File

@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/createReactDOMStyle converts ReactNative style to ReactDOM style 1`] = `
Object {
"borderBottomWidth": "1px",
"borderLeftWidth": "1px",
"borderRightWidth": "1px",
"borderTopWidth": "1px",
"borderWidthLeft": "2px",
"borderWidthRight": "3px",
"boxShadow": "1px 1px 1px 1px #000, 1px 2px 0px red",
"display": "flex",
"flexShrink": 0,
"marginBottom": "0px",
"marginTop": "0px",
"opacity": 0,
}
`;

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/expandStyle shortform -> longform 1`] = `
Object {
"borderBottomColor": "white",

View File

@@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/flattenStyle should merge style objects 1`] = `
Object {
"opacity": 1,
"order": 2,
}
`;
exports[`apis/StyleSheet/flattenStyle should override style properties 1`] = `
Object {
"backgroundColor": "#023c69",
"order": null,
}
`;
exports[`apis/StyleSheet/flattenStyle should overwrite properties with \`undefined\` 1`] = `
Object {
"backgroundColor": undefined,
}
`;
exports[`apis/StyleSheet/flattenStyle should recursively flatten arrays 1`] = `
Object {
"opacity": 1,
"order": 3,
}
`;

View File

@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/generateCss generates correct css 1`] = `"-webkit-transition-duration:0.1s;border-width-left:2px;border-width-right:3px;box-shadow:1px 1px 1px 1px #000;position:absolute;transition-duration:0.1s"`;

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/i18nStyle LTR mode does not auto-flip 1`] = `
Object {
"borderBottomLeftRadius": 20,
@@ -21,34 +23,6 @@ Object {
"height": 10,
"width": "1rem",
},
"writingDirection": "ltr",
}
`;
exports[`apis/StyleSheet/i18nStyle LTR mode normalizes properties 1`] = `
Object {
"borderBottomLeftRadius": 20,
"borderBottomRightRadius": "2rem",
"borderLeftColor": "red",
"borderLeftStyle": "solid",
"borderLeftWidth": 5,
"borderRightColor": "blue",
"borderRightStyle": "dotted",
"borderRightWidth": 6,
"borderTopLeftRadius": 10,
"borderTopRightRadius": "1rem",
"left": 1,
"marginLeft": 7,
"marginRight": 8,
"paddingLeft": 9,
"paddingRight": 10,
"right": 2,
"textAlign": "left",
"textShadowOffset": Object {
"height": 10,
"width": "1rem",
},
"writingDirection": "ltr",
}
`;
@@ -75,33 +49,5 @@ Object {
"height": 10,
"width": "-1rem",
},
"writingDirection": "rtl",
}
`;
exports[`apis/StyleSheet/i18nStyle RTL mode normalizes properties 1`] = `
Object {
"borderBottomLeftRadius": 20,
"borderBottomRightRadius": "2rem",
"borderLeftColor": "red",
"borderLeftStyle": "solid",
"borderLeftWidth": 5,
"borderRightColor": "blue",
"borderRightStyle": "dotted",
"borderRightWidth": 6,
"borderTopLeftRadius": 10,
"borderTopRightRadius": "1rem",
"left": 1,
"marginLeft": 7,
"marginRight": 8,
"paddingLeft": 9,
"paddingRight": 10,
"right": 2,
"textAlign": "left",
"textShadowOffset": Object {
"height": 10,
"width": "-1rem",
},
"writingDirection": "ltr",
}
`;

View File

@@ -1,10 +1,25 @@
exports[`apis/StyleSheet resolve 1`] = `
Object {
"className": "test __style_df __style_pebn",
"style": Object {
"display": null,
"opacity": 1,
"pointerEvents": null,
},
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet renderToString 1`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto;}
.rn-pointerEvents-12vffkv{pointer-events:none;}
.rn-pointerEvents-12vffkv *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
.rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-position-u8s1d{position:absolute}
.rn-right-zchlnj{right:0px}
.rn-top-ipm5af{top:0px}
</style>"
`;

View File

@@ -0,0 +1,28 @@
/* eslint-env jasmine, jest */
import createReactDOMStyle from '../createReactDOMStyle';
const reactNativeStyle = {
boxShadow: '1px 1px 1px 1px #000',
borderWidthLeft: 2,
borderWidth: 1,
borderWidthRight: 3,
display: 'flex',
marginVertical: 0,
opacity: 0,
shadowColor: 'red',
shadowOffset: { width: 1, height: 2 },
resizeMode: 'contain'
};
describe('apis/StyleSheet/createReactDOMStyle', () => {
test('converts ReactNative style to ReactDOM style', () => {
expect(createReactDOMStyle(reactNativeStyle)).toMatchSnapshot();
});
test('noop on DOM styles', () => {
const firstStyle = createReactDOMStyle(reactNativeStyle);
const secondStyle = createReactDOMStyle(firstStyle);
expect(firstStyle).toEqual(secondStyle);
});
});

View File

@@ -1,12 +0,0 @@
/* eslint-env jasmine, jest */
import createReactStyleObject from '../createReactStyleObject';
describe('apis/StyleSheet/createReactStyleObject', () => {
test('converts ReactNative style to ReactDOM style', () => {
const reactNativeStyle = { display: 'flex', marginVertical: 0, opacity: 0 };
const expectedStyle = { display: 'flex', marginTop: '0px', marginBottom: '0px', opacity: 0 };
expect(createReactStyleObject(reactNativeStyle)).toEqual(expectedStyle);
});
});

View File

@@ -45,4 +45,37 @@ describe('apis/StyleSheet/expandStyle', () => {
expect(expandStyle(initial)).toEqual(expected);
});
test('flex', () => {
expect(expandStyle({ display: 'flex' })).toEqual({
display: 'flex',
flexShrink: 0
});
expect(expandStyle({ display: 'flex', flex: 1 })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 1,
flexBasis: 'auto'
});
expect(expandStyle({ display: 'flex', flex: 10 })).toEqual({
display: 'flex',
flexGrow: 10,
flexShrink: 1,
flexBasis: 'auto'
});
expect(expandStyle({ display: 'flex', flexShrink: 1 })).toEqual({
display: 'flex',
flexShrink: 1
});
expect(expandStyle({ display: 'flex', flex: 1, flexShrink: 2 })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 2,
flexBasis: 'auto'
});
});
});

View File

@@ -9,14 +9,11 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
import flattenStyle from '..';
import flattenStyle from '../flattenStyle';
describe('modules/flattenStyle', () => {
describe('apis/StyleSheet/flattenStyle', () => {
test('should merge style objects', () => {
const style = flattenStyle([
{ opacity: 1 },
{ order: 2 }
]);
const style = flattenStyle([{ opacity: 1 }, { order: 2 }]);
expect(style).toMatchSnapshot();
});
@@ -29,24 +26,16 @@ describe('modules/flattenStyle', () => {
});
test('should overwrite properties with `undefined`', () => {
const style = flattenStyle([
{ backgroundColor: '#000' },
{ backgroundColor: undefined }
]);
const style = flattenStyle([{ backgroundColor: '#000' }, { backgroundColor: undefined }]);
expect(style).toMatchSnapshot();
});
test('should not fail on falsy values', () => {
expect(() => flattenStyle([ null, false, undefined ])).not.toThrow();
expect(() => flattenStyle([null, false, undefined])).not.toThrow();
});
test('should recursively flatten arrays', () => {
const style = flattenStyle([
null,
[],
[ { order: 2 }, { opacity: 1 } ],
{ order: 3 }
]);
const style = flattenStyle([null, [], [{ order: 2 }, { opacity: 1 }], { order: 3 }]);
expect(style).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,16 @@
/* eslint-env jasmine, jest */
import generateCss from '../generateCss';
describe('apis/StyleSheet/generateCss', () => {
test('generates correct css', () => {
const style = {
boxShadow: '1px 1px 1px 1px #000',
borderWidthLeft: 2,
borderWidthRight: 3,
position: 'absolute',
transitionDuration: '0.1s'
};
expect(generateCss(style)).toMatchSnapshot();
});
});

View File

@@ -21,16 +21,9 @@ const style = {
paddingRight: 10,
right: 2,
textAlign: 'left',
textShadowOffset: { width: '1rem', height: 10 },
writingDirection: 'ltr'
textShadowOffset: { width: '1rem', height: 10 }
};
const styleNoI18n = Object.keys(style).reduce((acc, prop) => {
const newProp = `${prop}$noI18n`;
acc[newProp] = style[prop];
return acc;
}, {});
describe('apis/StyleSheet/i18nStyle', () => {
describe('LTR mode', () => {
beforeEach(() => {
@@ -44,9 +37,6 @@ describe('apis/StyleSheet/i18nStyle', () => {
test('does not auto-flip', () => {
expect(i18nStyle(style)).toMatchSnapshot();
});
test('normalizes properties', () => {
expect(i18nStyle(styleNoI18n)).toMatchSnapshot();
});
});
describe('RTL mode', () => {
@@ -61,8 +51,5 @@ describe('apis/StyleSheet/i18nStyle', () => {
test('does auto-flip', () => {
expect(i18nStyle(style)).toMatchSnapshot();
});
test('normalizes properties', () => {
expect(i18nStyle(styleNoI18n)).toMatchSnapshot();
});
});
});

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