Compare commits

..

49 Commits

Author SHA1 Message Date
Nicolas Gallagher
dad80d5718 0.0.84 2017-04-20 10:00:49 -07:00
Nicolas Gallagher
d8e93058da Fix publish step 2017-04-20 10:00:39 -07:00
Nicolas Gallagher
4ae894313f Add 'styletron' to benchmarks 2017-04-20 09:16:02 -07:00
Nicolas Gallagher
438f398022 Standardize styles for benchmark View implementations 2017-04-20 09:14:41 -07:00
Nicolas Gallagher
630ee24fdd 0.0.83 2017-04-19 16:53:15 -07:00
Nicolas Gallagher
ae13873c2c [change] move 'a', 'button', 'ul' style resets to createDOMProps
Custom styles resets for the 'a', 'button', and 'ul' DOM elements are
now conditionally applied by 'createDOMProps'. This reduces the number
of classes on most Views and ensures that 'createDOMElement' (not just
'View' or 'Text') generates views with their styles reset.
2017-04-19 16:41:07 -07:00
Nicolas Gallagher
7705f521c8 [change] new accessibility features and docs
* Change 'accessible' to align with React Native.
* Add support for 'importantForAccessibility'.
* Stop event propagation for keyboard-activated Touchables (nested
  Touchables now respond the same as when touch-activated).
* Fix whitespace layout of nested Text elements.
* Use 'div' for Text to improve TalkBack grouping.
* Rewrite accessibility docs.

Close #382
Fix #408
2017-04-19 16:41:01 -07:00
Nicolas Gallagher
cbd98a8bd7 [fix] accessibilityLiveRegion values 2017-04-18 21:11:34 -07:00
Nicolas Gallagher
1f80e4c105 [change] render Image 'source' immediately if previously loaded
Maintain a record of loaded images. If an image has already been loaded,
bypass the JS loading logic and render it immediately. This prevents
flashes of placeholder state when moving between screens or items in a
virtualized list.
2017-04-18 20:50:48 -07:00
Nicolas Gallagher
dbc8f31be6 Update benchmark dependencies 2017-04-18 14:59:00 -07:00
Nicolas Gallagher
ed994dc670 Update 'getting-started' docs 2017-04-15 09:12:08 -07:00
Joe Cortopassi
a57e58607a Change to using 'prepare' npm-script
Avoids excessive rebuilding of modules during development tasks

Close #429
2017-04-14 13:34:24 -07:00
Nicolas Gallagher
03ea259d70 Update documentation
Close #417
2017-04-14 09:03:38 -07:00
Nicolas Gallagher
e39b58fd04 Update LayoutPropTypes
* Consolidates certain style props under LayoutPropTypes.
* Adds 'direction' style prop.
* Adds 'scroll' to 'overflow' style prop.
* Filter out 'aspectRatio' for now.

Ref #420
2017-04-14 08:28:06 -07:00
Nicolas Gallagher
ab45211401 [change] remove TextInput autogrow behaviour
This is non-standard. Removes 'maxNumberOfLines' too.

Ref #287
2017-04-13 20:46:28 -07:00
Nicolas Gallagher
32183bb92a Update performance dependencies 2017-04-13 20:37:08 -07:00
Nicolas Gallagher
761c42301d Update prettier 2017-04-13 19:26:01 -07:00
Nicolas Gallagher
0863894f40 [change] make react-dom a peer dependency 2017-04-13 19:24:43 -07:00
Nicolas Gallagher
8f736ddefe Update enzyme and don't use react-test-renderer 2017-04-13 19:22:47 -07:00
Nicolas Gallagher
ab686e2a07 Update webpack 2017-04-13 18:30:37 -07:00
Nicolas Gallagher
2c14bdab2e Update inline-style-prefixer 2017-04-13 17:33:54 -07:00
Nicolas Gallagher
0b8b064757 Update babel packages 2017-04-13 17:32:37 -07:00
Nicolas Gallagher
93eadb734b Update eslint plugins 2017-04-13 17:24:57 -07:00
Nicolas Gallagher
8d561d7309 Update prettier and eslint 2017-04-13 17:18:54 -07:00
Nicolas Gallagher
cdca9e1e2b Update 'modality' implementation 2017-04-13 15:57:58 -07:00
Nicolas Gallagher
170bab659d [change] use 'prop-types' and 'create-react-class'
Preparation for React 15.5
2017-04-11 22:20:39 -07:00
Nicolas Gallagher
941c628445 Remove dependency on most react-dom internals 2017-04-09 19:20:08 -07:00
Nicolas Gallagher
547c375bd6 Add more comparative benchmarks
Add "aphrodite", "react-jss", and "reactxp" renderers.

"react-addons-perf" is required due to:
https://github.com/Microsoft/reactxp/issues/11
2017-04-08 18:52:15 -07:00
Sunil Pai
aa85876eb2 Improve performance of glamor benchmark renderer 2017-04-08 18:48:16 -07:00
Nicolas Gallagher
50b168cc41 Add note about Safari flexbox performance 2017-04-05 14:48:11 -07:00
Nicolas Gallagher
25a11e673d 0.0.81 2017-04-05 14:03:07 -07:00
Nicolas Gallagher
e846054f4e Add 'Tweet' to performance benchmarks 2017-04-05 14:02:17 -07:00
Nicolas Gallagher
d6854abd7d [fix] accessibilityLiveRegion values 2017-04-02 16:16:41 -07:00
Nicolas Gallagher
1b172319b9 [change] use 'aria-level' to determine DOM heading tag
Fix #401
Close #402
2017-03-30 09:25:13 -07:00
Nicolas Gallagher
e81394c26e Add 'platform' benchmark
The "platform" benchmark relies on no intermediate layer. All the static
CSS it requires is inlined in the HTML page.
2017-03-25 09:11:23 -07:00
Nicolas Gallagher
d33aa3eee2 [change] Touchable no default 'accessibilityRole' 2017-03-23 15:42:20 -07:00
Nicolas Gallagher
5d78c73e8c [add] export 'ViewPropTypes'
See https://github.com/reactjs/react-codemod/pull/99
2017-03-23 12:06:24 -07:00
Nicolas Gallagher
7735d304ef [fix] export 'processColor' 2017-03-23 11:57:14 -07:00
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
191 changed files with 5811 additions and 2620 deletions

View File

@@ -14,7 +14,6 @@
"prettier/react"
],
"plugins": [
"jsx-a11y",
"promise",
"react"
],
@@ -119,28 +118,6 @@
// promise
"promise/param-names": 2,
// jsx accessibility
"jsx-a11y/aria-props": 2,
"jsx-a11y/aria-proptypes": 2,
"jsx-a11y/aria-role": 2,
"jsx-a11y/aria-unsupported-elements": 2,
"jsx-a11y/heading-has-content": 2,
"jsx-a11y/href-no-hash": 2,
"jsx-a11y/html-has-lang": 2,
"jsx-a11y/img-has-alt": 2,
"jsx-a11y/img-redundant-alt": 2,
"jsx-a11y/label-has-for": 2,
"jsx-a11y/mouse-events-have-key-events": 2,
"jsx-a11y/no-access-key": 2,
"jsx-a11y/no-marquee": 2,
"jsx-a11y/no-onchange": 0,
"jsx-a11y/onclick-has-focus": 2,
"jsx-a11y/onclick-has-role": 2,
"jsx-a11y/role-has-required-aria-props": 2,
"jsx-a11y/role-supports-aria-props": 2,
"jsx-a11y/scope": 2,
"jsx-a11y/tabindex-no-positive": 2,
// react
"react/display-name": 0,
"react/jsx-handler-names": [2, {

View File

@@ -20,17 +20,17 @@ touch handling to the Web. [Read more](#why).
Browse the UI Explorer to see React Native [examples running on
Web](https://necolas.github.io/react-native-web/storybook/). Or try it out
online with [React Native for Web: Playground](http://codepen.io/necolas/pen/PZzwBR).
online with [React Native for Web: Playground](https://www.webpackbin.com/bins/-KgucwxRbn7HRU-V-3Bc).
## Quick start
To install in your app:
```
npm install --save react@15.4 react-native-web
npm install --save react@15.4 react-dom@15.4 react-native-web
```
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
Read the [Getting Started](docs/guides/getting-started.md) guide.
Alternatively, you can quickly setup a local project
using [create-react-app](https://github.com/facebookincubator/create-react-app)
@@ -41,12 +41,11 @@ using [create-react-app](https://github.com/facebookincubator/create-react-app)
Guides:
* [Getting started](docs/guides/getting-started.md)
* [Accessibility](docs/guides/accessibility.md)
* [Client and server rendering](docs/guides/rendering.md)
* [Direct manipulation](docs/guides/direct-manipulation.md)
* [Internationalization](docs/guides/internationalization.md)
* [Known issues](docs/guides/known-issues.md)
* [React Native](docs/guides/react-native.md)
* [Style](docs/guides/style.md)
Exported modules:
@@ -143,9 +142,8 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
* [reactxp](https://github.com/microsoft/reactxp)
* [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

@@ -4,19 +4,19 @@
[...View props](./View.md)
**animating**: bool = true
**animating**: boolean = true
Whether to show the indicator or hide it.
**color**: string = '#1976D2'
**color**: ?color = '#1976D2'
The foreground color of the spinner.
**hidesWhenStopped**: bool = true
**hidesWhenStopped**: ?boolean = true
Whether the indicator should hide when not animating.
**size**: oneOf('small, 'large') | number = 'small'
**size**: ?enum('small, 'large') | number = 'small'
Size of the indicator. Small has a height of `20`, large has a height of `36`.

View File

@@ -6,23 +6,27 @@ build your own custom button using `TouchableOpacity` or
## Props
**accessibilityLabel**: string
**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 a screen reader when the user interacts
with the element.
**color**: string
**color**: ?string
Background color of the button.
**disabled**: bool = false
**disabled**: ?boolean
If true, disable all interactions for this component
If `true`, disable all interactions for this element.
**onPress**: function
This function is called on press.
testID: ?string
Used to locate this view in end-to-end tests.
**title**: string
Text to display inside the button.

View File

@@ -9,65 +9,66 @@ Unsupported React Native props:
## Props
**accessibilityLabel**: string
**accessibilityLabel**: ?string
The text that's read by a screenreader when someone interacts with the image.
**accessible**: bool
**accessible**: ?boolean
When `false`, the view is hidden from screenreaders. Default: `true`.
When `true`, indicates the image is an accessibility element.
**children**: any
**children**: ?any
Content to display over the image.
**defaultSource**: { uri: string }
**defaultSource**: ?object
An image to display as a placeholder while downloading the final image off the network.
An image to display as a placeholder while downloading the final image off the
network. `{ uri: string, width, height }`
**onError**: function
**onError**: ?function
Invoked on load error with `{nativeEvent: {error}}`.
**onLayout**: function
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onLoad**: function
**onLoad**: ?function
Invoked when load completes successfully.
**onLoadEnd**: function
**onLoadEnd**: ?function
Invoked when load either succeeds or fails,
**onLoadStart**: function
**onLoadStart**: ?function
Invoked on load start.
**resizeMode**: oneOf('center', 'contain', 'cover', 'none', 'repeat', 'stretch') = 'cover'
**resizeMode**: ?enum('center', 'contain', 'cover', 'none', 'repeat', 'stretch') = 'cover'
Determines how to resize the image when the frame doesn't match the raw image
dimensions.
**source**: { uri: string }
**source**: ?object
`uri` is a string representing the resource identifier for the image, which
could be an http address or a base64 encoded image.
could be an http address or a base64 encoded image. `{ uri: string, width, height }`
**style**: style
**style**: ?style
+ ...[View#style](./View.md)
+ `resizeMode`
**testID**: string
**testID**: ?string
Used to locate a view in end-to-end tests.
## Properties
static **resizeMode**: Object
static **resizeMode**: object
Example usage:

View File

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

View File

@@ -9,17 +9,17 @@ view directly (discouraged) or make sure all parent views have bounded height
[...View props](./View.md)
**contentContainerStyle**: style
**contentContainerStyle**: ?style
These styles will be applied to the scroll view content container which wraps
all of the child views.
**horizontal**: bool = false
**horizontal**: ?boolean = false
When true, the scroll view's children are arranged horizontally in a row
When `true`, the scroll view's children are arranged horizontally in a row
instead of vertically in a column.
**keyboardDismissMode**: oneOf('none', 'on-drag') = 'none'
**keyboardDismissMode**: ?enum('none', 'on-drag') = 'none'
Determines whether the keyboard gets dismissed in response to a scroll drag.
@@ -27,13 +27,13 @@ Determines whether the keyboard gets dismissed in response to a scroll drag.
* `on-drag`, the keyboard is dismissed when a drag begins.
* `interactive` (not supported on web; same as `none`)
**onContentSizeChange**: function
**onContentSizeChange**: ?function
Called when scrollable content view of the `ScrollView` changes. It's
implemented using the `onLayout` handler attached to the content container
which this `ScrollView` renders.
**onScroll**: function
**onScroll**: ?function
Fires at most once per frame during scrolling. The frequency of the events can
be contolled using the `scrollEventThrottle` prop.
@@ -50,18 +50,18 @@ Invoked on scroll with the following event:
}
```
**refreshControl**: element
**refreshControl**: ?element
TODO
A [RefreshControl](../RefreshControl) component, used to provide
pull-to-refresh functionality for the `ScrollView`.
**scrollEnabled**: bool = true
**scrollEnabled**: ?boolean = true
When false, the content does not scroll.
**scrollEventThrottle**: number = 0
**scrollEventThrottle**: ?number = 0
This controls how often the scroll event will be fired while scrolling (as a
time interval in ms). A lower number yields better accuracy for code that is

View File

@@ -9,31 +9,31 @@ supplied `value` prop instead of the expected result of any user actions.
[...View props](./View.md)
**disabled**: bool = false
**disabled**: ?boolean = false
If `true` the user won't be able to interact with the switch.
**onValueChange**: func
**onValueChange**: ?function
Invoked with the new value when the value changes.
**value**: bool = false
**value**: ?boolean = false
The value of the switch. If `true` the switch will be turned on.
(web) **activeThumbColor**: color = #009688
(web) **activeThumbColor**: ?color = #009688
The color of the thumb grip when the switch is turned on.
(web) **activeTrackColor**: color = #A3D3CF
(web) **activeTrackColor**: ?color = #A3D3CF
The color of the track when the switch is turned on.
(web) **thumbColor**: color = #FAFAFA
(web) **thumbColor**: ?color = #FAFAFA
The color of the thumb grip when the switch is turned off.
(web) **trackColor**: color = #939393
(web) **trackColor**: ?color = #939393
The color of the track when the switch is turned off.

View File

@@ -16,49 +16,62 @@ Unsupported React Native props:
NOTE: `Text` will transfer all other props to the rendered HTML element.
(web) **accessibilityLabel**: string
(web) **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 a screen reader when the user interacts
with the element. (This is implemented using `aria-label`.)
(web) **accessibilityRole**: oneOf(roles)
See the [Accessibility guide](../guides/accessibility) for more information.
(web) **accessibilityRole**: ?oneOf(roles)
Allows assistive technologies to present and support interaction with the view
in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an `accessibilityRole` of
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
Note: Avoid changing `accessibilityRole` values over time or after user
actions. Generally, accessibility APIs do not provide a means of notifying
assistive technologies of a `role` value change.
See the [Accessibility guide](../guides/accessibility) for more information.
**accessible**: bool = true
**accessible**: ?boolean
When `false`, the text is hidden from assistive technologies. (This is
implemented using `aria-hidden`.)
When `true`, indicates that the view is an accessibility element (i.e.,
focusable) and groups its child content. By default, all the touchable elements
and elements with `accessibilityRole` of `button` and `link` are accessible.
(This is implemented using `tabindex`.)
**children**: any
See the [Accessibility guide](../guides/accessibility) for more information.
**children**: ?any
Child content.
**numberOfLines**: number
**importantForAccessibility**: ?enum('auto', 'no-hide-descendants', 'yes')
A value of `no` will remove the element from the tab flow.
A value of `no-hide-descendants` will hide the element and its children from
assistive technologies. (This is implemented using `aria-hidden`.)
See the [Accessibility guide](../guides/accessibility) for more information.
**numberOfLines**: ?number
Truncates the text with an ellipsis after this many lines. Currently only supports `1`.
**onLayout**: function
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onPress**: function
**onPress**: ?function
This function is called on press.
**selectable**: bool = true
**selectable**: ?boolean
Lets the user select the text.
When `false`, the text is not selectable.
**style**: style
**style**: ?style
+ ...[View#style](View.md)
+ `color`
@@ -85,7 +98,7 @@ Lets the user select the text.
‡ web only.
**testID**: string
**testID**: ?string
Used to locate this view in end-to-end tests.

View File

@@ -18,7 +18,7 @@ Unsupported React Native props:
[...View props](./View.md)
**autoCapitalize**: oneOf('characters', 'none', 'sentences', 'words') = 'sentences'
**autoCapitalize**: ?enum('characters', 'none', 'sentences', 'words') = 'sentences'
Automatically capitalize certain characters (only available in Chrome and iOS Safari).
@@ -27,21 +27,21 @@ Automatically capitalize certain characters (only available in Chrome and iOS Sa
* `sentences`: Automatically capitalize the first letter of sentences.
* `words`: Automatically capitalize the first letter of words.
(web) **autoComplete**: string
(web) **autoComplete**: ?string
Indicates whether the value of the control can be automatically completed by
the browser. [Accepted values](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
**autoCorrect**: bool = true
**autoCorrect**: ?boolean = true
Automatically correct spelling mistakes (only available in iOS Safari).
**autoFocus**: bool = false
**autoFocus**: ?boolean = false
If `true`, focuses the input on `componentDidMount`. Only the first form element
in a document with `autofocus` is focused.
**blurOnSubmit**: bool
**blurOnSubmit**: ?boolean
If `true`, the text field will blur when submitted. The default value is `true`
for single-line fields and `false` for multiline fields. Note, for multiline
@@ -49,110 +49,104 @@ fields setting `blurOnSubmit` to `true` means that pressing return will blur
the field and trigger the `onSubmitEditing` event instead of inserting a
newline into the field.
**clearTextOnFocus**: bool = false
**clearTextOnFocus**: ?boolean = false
If `true`, clears the text field automatically when focused.
**defaultValue**: string
**defaultValue**: ?string
Provides an initial value that will change when the user starts typing. Useful
for simple use-cases where you don't want to deal with listening to events and
updating the `value` prop to keep the controlled state in sync.
**editable**: bool = true
**editable**: ?boolean = true
If `false`, text is not editable (i.e., read-only).
**keyboardType**: oneOf('default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search') = 'default'
**keyboardType**: enum('default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search') = 'default'
Determines which keyboard to open. (NOTE: Safari iOS requires an ancestral
`<form action>` element to display the `search` keyboard).
(Not available when `multiline` is `true`.)
**maxLength**: number
**maxLength**: ?number
Limits the maximum number of characters that can be entered.
(web) **maxNumberOfLines**: number = numberOfLines
Limits the maximum number of lines for a multiline `TextInput`.
(Requires `multiline` to be `true`.)
**multiline**: bool = false
**multiline**: ?boolean = false
If true, the text input can be multiple lines.
**numberOfLines**: number = 2
**numberOfLines**: ?number = 2
Sets the initial number of lines for a multiline `TextInput`.
Sets the number of lines for a multiline `TextInput`.
(Requires `multiline` to be `true`.)
**onBlur**: function
**onBlur**: ?function
Callback that is called when the text input is blurred.
**onChange**: function
**onChange**: ?function
Callback that is called when the text input's text changes.
**onChangeText**: function
**onChangeText**: ?function
Callback that is called when the text input's text changes. The text is passed
as an argument to the callback handler.
**onFocus**: function
**onFocus**: ?function
Callback that is called when the text input is focused.
**onKeyPress**: function
**onKeyPress**: ?function
Callback that is called when a key is pressed. Pressed key value is passed as
an argument to the callback handler. Fires before `onChange` callbacks.
**onSelectionChange**: function
**onSelectionChange**: ?function
Callback that is called when the text input's selection changes. This will be called with
`{ nativeEvent: { selection: { start, end } } }`.
**onSubmitEditing**: function
**onSubmitEditing**: ?function
Callback that is called when the keyboard's submit button is pressed.
**placeholder**: string
**placeholder**: ?string
The string that will be rendered in an empty `TextInput` before text has been
entered.
**secureTextEntry**: bool = false
**secureTextEntry**: ?boolean = false
If true, the text input obscures the text entered so that sensitive text like
passwords stay secure.
(Not available when `multiline` is `true`.)
**selection**: { start: number, end: ?number }
**selection**: ?{ start: number, end: ?number }
The start and end of the text input's selection. Set start and end to the same value to position the cursor.
**selectTextOnFocus**: bool = false
**selectTextOnFocus**: ?boolean = false
If `true`, all text will automatically be selected on focus.
**style**: style
**style**: ?style
+ ...[Text#style](./Text.md)
+ `resize`
‡ web only.
**testID**: string
**testID**: ?string
Used to locate this view in end-to-end tests.
**value**: string
**value**: ?string
The value to show for the text input. `TextInput` is a controlled component,
which means the native `value` will be forced to match this prop if provided.

View File

@@ -11,60 +11,33 @@ several child components, wrap them in a View.
[...View props](./View.md)
**accessibilityLabel**: string
Overrides the text that's read by the screen reader when the user interacts
with the element.
(web) **accessibilityRole**: oneOf(roles) = 'button'
Allows assistive technologies to present and support interaction with the view
**accessible**: bool = true
When `false`, the view is hidden from screenreaders.
**children**: View
**delayLongPress**: number
**delayLongPress**: ?number
Delay in ms, from `onPressIn`, before `onLongPress` is called.
**delayPressIn**: number
**delayPressIn**: ?number
Delay in ms, from the start of the touch, before `onPressIn` is called.
**delayPressOut**: number
**delayPressOut**: ?number
Delay in ms, from the release of the touch, before `onPressOut` is called.
**disabled**: bool
**disabled**: ?boolean
If true, disable all interactions for this component.
If `true`, disable all interactions for this component.
**hitSlop**: `{top: number, left: number, bottom: number, right: number}`
**onLongPress**: ?function
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.
**onLayout**: function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onLongPress**: function
**onPress**: function
**onPress**: ?function
Called when the touch is released, but not if cancelled (e.g. by a scroll that steals the responder lock).
**onPressIn**: function
**onPressIn**: ?function
**onPressOut**: function
**onPressOut**: ?function
**pressRetentionOffset**: `{top: number, left: number, bottom: number, right: number}`
**pressRetentionOffset**: ?`{top: number, left: number, bottom: number, right: number}`
When the scroll view is disabled, this defines how far your touch may move off
of the button, before deactivating the button. Once deactivated, try moving it

View File

@@ -7,21 +7,20 @@ inside another `View` and has 0-to-many children of any type.
Also, refer to React Native's documentation about the [Gesture Responder
System](http://facebook.github.io/react-native/releases/0.22/docs/gesture-responder-system.html).
Unsupported React Native props:
`onAccessibilityTap`,
`hitSlop`,
`onMagicTap`
Unsupported React Native props: `collapsable`, `onAccessibilityTap`, `onMagicTap`.
## Props
NOTE: `View` will transfer all other props to the rendered HTML element.
**accessibilityLabel**: string
**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 a screen reader when the user interacts
with the element. (This is implemented using `aria-label`.)
**accessibilityLiveRegion**: oneOf('assertive', 'off', 'polite') = 'off'
See the [Accessibility guide](../guides/accessibility) for more information.
**accessibilityLiveRegion**: ?enum('assertive', 'none', 'polite')
Indicates to assistive technologies whether to notify the user when the view
changes. The values of this attribute are expressed in degrees of importance.
@@ -30,55 +29,105 @@ priority. When regions are specified as `assertive`, assistive technologies
will interrupt and immediately notify the user. (This is implemented using
[`aria-live`](http://www.w3.org/TR/wai-aria/states_and_properties#aria-live).)
(web) **accessibilityRole**: oneOf(roles)
See the [Accessibility guide](../guides/accessibility) for more information.
(web) **accessibilityRole**: ?enum(roles)
Allows assistive technologies to present and support interaction with the view
in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an `accessibilityRole` of
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
Note: Avoid changing `accessibilityRole` values over time or after user
actions. Generally, accessibility APIs do not provide a means of notifying
assistive technologies of a `role` value change.
See the [Accessibility guide](../guides/accessibility) for more information.
**accessible**: bool = true
**accessible**: ?boolean
When `false`, the view is hidden from assistive technologies. (This is
implemented using `aria-hidden`.)
When `true`, indicates that the view is an accessibility element (i.e.,
focusable) and groups its child content. By default, all the touchable elements
and elements with `accessibilityRole` of `button` and `link` are accessible.
(This is implemented using `tabindex`.)
**onLayout**: function
See the [Accessibility guide](../guides/accessibility) for more information.
**children**: ?element
Child content.
**hitSlop**: ?object
This defines how far a touch event can start away from the view (in pixels).
Typical interface guidelines recommend touch targets that are at least 30 - 40
points/density-independent pixels.
For example, if a touchable view has a height of `20` the touchable height can
be extended to `40` with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}`.
**importantForAccessibility**: ?enum('auto', 'no', 'no-hide-descendants', 'yes')
A value of `no` will remove the element from the tab flow.
A value of `no-hide-descendants` will hide the element and its children from
assistive technologies. (This is implemented using `aria-hidden`.)
See the [Accessibility guide](../guides/accessibility) for more information.
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onMoveShouldSetResponder**: function
**onMoveShouldSetResponder**: ?function => boolean
**onMoveShouldSetResponderCapture**: function
Does this view want to "claim" touch responsiveness? This is called for every
touch move on the `View` when it is not the responder.
**onResponderGrant**: function
**onMoveShouldSetResponderCapture**: ?function => boolean
For most touch interactions, you'll simply want to wrap your component in
`TouchableHighlight` or `TouchableOpacity`.
If a parent `View` wants to prevent a child `View` from becoming responder on a
move, it should have this handler return `true`.
**onResponderMove**: function
**onResponderGrant**: ?function
**onResponderReject**: function
The `View` is now responding to touch events. This is the time to highlight and
show the user what is happening. For most touch interactions, you'll simply
want to wrap your component in `TouchableHighlight` or `TouchableOpacity`.
**onResponderRelease**: function
**onResponderMove**: ?function
**onResponderTerminate**: function
The user is moving their finger.
**onResponderTerminationRequest**: function
**onResponderReject**: ?function
**onStartShouldSetResponder**: function
Another responder is already active and will not release it to the `View`
asking to be the responder.
**onStartShouldSetResponderCapture**: function
**onResponderRelease**: ?function
**pointerEvents**: oneOf('auto', 'box-only', 'box-none', 'none') = 'auto'
Fired at the end of the touch.
Configure the `pointerEvents` of the view. The enhanced `pointerEvents` modes
provided are not part of the CSS spec, therefore, `pointerEvents` is excluded
from `style`.
**onResponderTerminate**: ?function
The responder has been taken from the `View`.
**onResponderTerminationRequest**: ?function
Some other `View` wants to become responder and is asking this `View` to
release its responder. Returning `true` allows its release.
**onStartShouldSetResponder**: ?function => boolean
Does this view want to become responder on the start of a touch?
**onStartShouldSetResponderCapture**: ?function => boolean
If a parent `View` wants to prevent a child `View` from becoming the responder
on a touch start, it should have this handler return `true`.
**pointerEvents**: ?enum('auto', 'box-only', 'box-none', 'none') = 'auto'
Controls whether the View can be the target of touch events. The enhanced
`pointerEvents` modes provided are not part of the CSS spec, therefore,
`pointerEvents` is excluded from `style`.
`box-none` is the equivalent of:
@@ -94,7 +143,7 @@ from `style`.
.box-only * { pointer-events: none }
```
**style**: style
**style**: ?style
+ `alignContent`
+ `alignItems`
@@ -140,7 +189,7 @@ from `style`.
+ `boxShadow`
+ `boxSizing`
+ `cursor`
+ `display`
+ `display`
+ `flex` (number)
+ `flexBasis`
+ `flexDirection`
@@ -213,7 +262,7 @@ Default:
(See [facebook/css-layout](https://github.com/facebook/css-layout)).
**testID**: string
**testID**: ?string
Used to locate this view in end-to-end tests.

View File

@@ -1,21 +1,51 @@
# Accessibility
On the Web, assistive technologies derive useful information about the
structure, purpose, and interactivity of apps from their [HTML
elements][html-accessibility-url], attributes, and [ARIA in
HTML][aria-in-html-url].
On the Web, assistive technologies (e.g., VoiceOver, TalkBack screen readers)
derive useful information about the structure, purpose, and interactivity of
apps from their [HTML elements][html-accessibility-url], attributes, and [ARIA
in HTML][aria-in-html-url]. React Native for Web includes APIs designed to
provide developers with support for making apps more accessible. The most
common and best supported accessibility features of the Web are exposed as the
props: `accessible`, `accessibilityLabel`, `accessibilityLiveRegion`,
`accessibilityRole`, and `importantForAccessibility`.
The most common and best supported accessibility features of the Web are
exposed as the props: `accessible`, `accessibilityLabel`,
`accessibilityLiveRegion`, and `accessibilityRole`.
## Accessibility properties
React Native for Web does not provide a way to directly control the type of the
rendered HTML element. The `accessibilityRole` prop is used to infer an
[analogous HTML element][html-aria-url] to use in addition to the resulting
ARIA `role`, where possible. While this may contradict some ARIA
recommendations, it also helps avoid certain HTML5 conformance errors and
accessibility anti-patterns (e.g., giving a `heading` role to a `button`
element).
### accessible
When `true`, indicates that the view is an accessibility element. When a view
is an accessibility element, it groups its children into a single focusable
component. By default, all touchable elements, buttons, and links are
"accessible". Prefer using `accessibilityRole` (e.g., `button`, `link`) to
create focusable HTML elements wherever possible. On web, `accessible={true}`
is implemented using `tabIndex`.
### accessibilityLabel
When a view is marked as `accessible`, it is a good practice to set an
`accessibilityLabel` on the view, so that people who use screen readers know
what element they have selected. On web, `accessibilityLabel` is implemented
using `aria-label`.
```
<TouchableOpacity accessibilityLabel={'Tap me!'} accessible={true} onPress={this._onPress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Press me!</Text>
</View>
</TouchableOpacity>
```
### accessibilityRole
In some cases, we also want to alert the end user of the type of selected
component (i.e., that it is a “button”). To provide more context to screen
readers, you should specify the `accessibilityRole` property.
The `accessibilityRole` prop is used to infer an [analogous HTML
element][html-aria-url] to use in addition to the resulting ARIA `role`, where
possible. While this may contradict some ARIA recommendations, it also helps
avoid certain HTML5 conformance errors and accessibility anti-patterns (e.g.,
giving a `heading` role to a `button` element).
For example:
@@ -25,8 +55,62 @@ For example:
* `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`.
* `<View accessibilityRole='main' />` => `<main role='main' />`.
Other ARIA properties should be set via [direct
manipulation](./direct-manipulation.md).
In the example below, the `TouchableWithoutFeedback` is announced by screen
readers as a native Button.
```
<TouchableWithoutFeedback accessibilityRole="button" onPress={this._onPress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Press me!</Text>
</View>
</TouchableWithoutFeedback>
```
Note: Avoid changing `accessibilityRole` values over time or after user
actions. Generally, accessibility APIs do not provide a means of notifying
assistive technologies of a `role` value change.
### accessibilityLiveRegion
When components dynamically change we may need to inform the user. The
`accessibilityLiveRegion` property serves this purpose and can be set to
`none`, `polite` and `assertive`. On web, `accessibilityLiveRegion` is
implemented using `aria-live`.
* `none`: Accessibility services should not announce changes to this view.
* `polite`: Accessibility services should announce changes to this view.
* `assertive`: Accessibility services should interrupt ongoing speech to immediately announce changes to this view.
```
<TouchableWithoutFeedback onPress={this._addOne}>
<View style={styles.embedded}>
<Text>Click me</Text>
</View>
</TouchableWithoutFeedback>
<Text accessibilityLiveRegion="polite">
Clicked {this.state.count} times
</Text>
```
In the above example, method `_addOne` changes the `state.count` variable. As
soon as an end user clicks the `TouchableWithoutFeedback`, screen readers
announce text in the `Text` view because of its
`accessibilityLiveRegion="polite"` property.
### importantForAccessibility
The `importantForAccessibility` property controls if a view appears in the
accessibility tree and if it is reported to accessibility services. On web, a
value of `no` will remove a focusable element from the tab flow, and a value of
`no-hide-descendants` will also hide the entire subtree from assistive
technologies (this is implemented using `aria-hidden`).
### Other
Other ARIA properties can be set via [direct
manipulation](./direct-manipulation.md) or props (this may change in the
future).
[aria-in-html-url]: https://w3c.github.io/aria-in-html/
[html-accessibility-url]: http://www.html5accessibility.com/

View File

@@ -0,0 +1,160 @@
# Getting started
## Webpack and Babel
[Webpack](webpack.js.org) is a popular build tool for web apps. Below is an
example of how to configure a build that uses [Babel](https://babeljs.io/) to
compile your JavaScript for the web.
```js
// webpack.config.js
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
test: /\.js$/,
// Add every directory that needs to be compiled by Babel during the build
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules/react-native-uncompiled')
],
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// The 'react-native' preset is recommended
presets: ['react-native']
}
}
};
// This is needed for webpack to import static images in JavaScript files
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]'
}
}
};
module.exports = {
// ...the rest of your config
module: {
rules: [
babelLoaderConfiguration,
imageLoaderConfiguration
]
},
plugins: [
// `process.env.NODE_ENV === 'production'` must be `true` for production
// builds to eliminate development checks and reduce build size.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
},
resolve: {
// Maps the 'react-native' import to 'react-native-web'.
alias: {
'react-native': 'react-native-web'
},
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
extensions: [ '.web.js', '.js' ]
}
}
```
Please refer to the Webpack documentation for more information.
## Jest
[Jest](https://facebook.github.io/jest/) also needs to map `react-native` to `react-native-web`.
```
"jest": {
"moduleNameMapper": {
"react-native": "<rootDir>/node_modules/react-native-web"
}
}
```
Please refer to the Jest documentation for more information.
## Web-specific code
Minor platform differences can use the `Platform` module.
```js
import { AppRegistry, Platform } from 'react-native'
AppRegistry.registerComponent('MyApp', () => MyApp)
if (Platform.OS === 'web') {
AppRegistry.runApplication('MyApp', {
rootTag: document.getElementById('react-root')
});
}
```
## Client-side rendering
Rendering using `ReactNative`:
```js
import React from 'react'
import ReactNative from 'react-native'
// component that renders the app
const AppHeaderContainer = (props) => { /* ... */ }
ReactNative.render(<AppHeaderContainer />, document.getElementById('react-app-header'))
```
Rendering using `AppRegistry`:
```js
import React from 'react'
import ReactNative, { AppRegistry } from 'react-native'
// component that renders the app
const AppContainer = (props) => { /* ... */ }
// register the app
AppRegistry.registerComponent('App', () => AppContainer)
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: document.getElementById('react-app')
})
```
Rendering within `ReactDOM.render` also works when introduce `react-native-web`
to an existing web app, but it is not recommended oherwise.
## Server-side rendering
Rendering using the `AppRegistry`:
```js
import ReactDOMServer from 'react-dom/server'
import ReactNative, { AppRegistry } from 'react-native'
// component that renders the app
const AppContainer = (props) => { /* ... */ }
// register the app
AppRegistry.registerComponent('App', () => AppContainer)
// prerender the app
const { element, stylesheet } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
```

View File

@@ -1,10 +1,18 @@
# Known issues
## Missing modules and Views
## Safari flexbox performance
This is an initial release of React Native for Web, therefore, not all of the
views present on iOS/Android are released on Web. We are very much interested in
the community's feedback on the next set of modules and views.
Safari version prior to 10.1 can suffer from extremely [poor flexbox
performance](https://bugs.webkit.org/show_bug.cgi?id=150445). The recommended
way to work around this issue (as used on mobile.twitter.com) is to set
`display:block` on Views in your element hierarchy that you know don't need
flexbox layout.
## Missing modules and components
Not all of the views present on iOS/Android are currently available on Web. We
are very much interested in the community's feedback on the next set of modules
and views.
Not all the modules or views for iOS/Android can be implemented on Web. In some
cases it will be necessary to use a Web counterpart or to guard the use of a

View File

@@ -1,119 +0,0 @@
# React Native
Use a module loader that supports package aliases (e.g., webpack), and alias
`react-native` to `react-native-web`.
```js
// webpack.config.js
module.exports = {
// ...
resolve: {
alias: {
'react-native': 'react-native-web'
}
}
}
```
## Image assets
In order to require image assets (e.g. `require('assets/myimage.png')`), add
the `url-loader` to the webpack config:
```js
// webpack.config.js
module.exports = {
// ...
module: {
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.
```js
import { AppRegistry, Platform } from 'react-native'
AppRegistry.registerComponent('MyApp', () => MyApp)
if (Platform.OS === 'web') {
AppRegistry.runApplication('MyApp', {
rootTag: document.getElementById('react-root')
});
}
```
More substantial Web-specific implementation code should be written in files
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
Production builds can benefit from dead-code elimination by defining the
following variables:
```js
// webpack.config.js
module.exports = {
// ...
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
}
}
```

View File

@@ -1,89 +0,0 @@
# Client and Server rendering
It's recommended that you use a module loader that supports package aliases
(e.g., webpack), and alias `react-native` to `react-native-web`.
```js
// webpack.config.js
module.exports = {
// ...other configuration
resolve: {
alias: {
'react-native': 'react-native-web'
}
}
}
```
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
module.exports = {
// ...other configuration
resolve: {
alias: {
'react-native': 'react-native-web/core'
}
}
}
```
## Client-side rendering
Rendering without using the `AppRegistry`:
```js
import React from 'react'
import ReactNative from 'react-native'
// component that renders the app
const AppHeaderContainer = (props) => { /* ... */ }
ReactNative.render(<AppHeaderContainer />, document.getElementById('react-app-header'))
```
Rendering using the `AppRegistry`:
```js
import React from 'react'
import ReactNative, { AppRegistry } from 'react-native'
// component that renders the app
const AppContainer = (props) => { /* ... */ }
// register the app
AppRegistry.registerComponent('App', () => AppContainer)
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: document.getElementById('react-app')
})
```
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'
// component that renders the app
const AppContainer = (props) => { /* ... */ }
// register the app
AppRegistry.registerComponent('App', () => AppContainer)
// prerender the app
const { element, stylesheet } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
```

View File

@@ -23,8 +23,7 @@ module.exports = {
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.__REACT_NATIVE_DEBUG_ENABLED__': DEV
}),
new webpack.optimize.OccurenceOrderPlugin()
})
],
resolve: {
alias: {

View File

@@ -1,5 +1,6 @@
'use strict';
import createReactClass from 'create-react-class';
import { storiesOf, action } from '@kadira/storybook';
var React = require('react');
@@ -12,7 +13,7 @@ var {
var CIRCLE_SIZE = 80;
var PanResponderExample = React.createClass({
var PanResponderExample = createReactClass({
_panResponder: {},
_previousLeft: 0,
_previousTop: 0,

View File

@@ -1,3 +1,4 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import { ActivityIndicator, StyleSheet, View } from 'react-native'
@@ -26,7 +27,7 @@ import TimerMixin from 'react-timer-mixin';
* @flow
*/
const ToggleAnimatingActivityIndicator = React.createClass({
const ToggleAnimatingActivityIndicator = createReactClass({
mixins: [TimerMixin],
getInitialState() {

View File

@@ -1,3 +1,4 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf, action, addDecorator } from '@kadira/storybook';
import { ActivityIndicator, Image, Platform, StyleSheet, Text, View } from 'react-native'
@@ -31,7 +32,7 @@ var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACS
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({
var NetworkImageCallbackExample = createReactClass({
getInitialState: function() {
return {
events: [],
@@ -88,7 +89,7 @@ var NetworkImageCallbackExample = React.createClass({
}
});
var NetworkImageExample = React.createClass({
var NetworkImageExample = createReactClass({
getInitialState: function() {
return {
error: false,
@@ -116,7 +117,7 @@ var NetworkImageExample = React.createClass({
}
});
var ImageSizeExample = React.createClass({
var ImageSizeExample = createReactClass({
getInitialState: function() {
return {
width: 0,
@@ -150,7 +151,7 @@ var ImageSizeExample = React.createClass({
});
/*
var MultipleSourcesExample = React.createClass({
var MultipleSourcesExample = createReactClass({
getInitialState: function() {
return {
width: 30,

View File

@@ -1,3 +1,4 @@
import createReactClass from 'create-react-class';
import { ProgressBar, StyleSheet, View } from 'react-native'
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
@@ -26,7 +27,7 @@ import TimerMixin from 'react-timer-mixin';
* @flow
*/
var ProgressBarExample = React.createClass({
var ProgressBarExample = createReactClass({
mixins: [TimerMixin],
getInitialState() {

View File

@@ -1,3 +1,4 @@
import createReactClass from 'create-react-class';
import { Platform, Switch, Text, View } from 'react-native'
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
@@ -25,7 +26,7 @@ import { storiesOf, action } from '@kadira/storybook';
* @flow
*/
var BasicSwitchExample = React.createClass({
var BasicSwitchExample = createReactClass({
getInitialState() {
return {
trueSwitchIsOn: true,
@@ -49,7 +50,7 @@ var BasicSwitchExample = React.createClass({
}
});
var DisabledSwitchExample = React.createClass({
var DisabledSwitchExample = createReactClass({
render() {
return (
<View>
@@ -65,7 +66,7 @@ var DisabledSwitchExample = React.createClass({
},
});
var ColorSwitchExample = React.createClass({
var ColorSwitchExample = createReactClass({
getInitialState() {
return {
colorTrueSwitchIsOn: true,
@@ -95,7 +96,7 @@ var ColorSwitchExample = React.createClass({
},
});
var EventSwitchExample = React.createClass({
var EventSwitchExample = createReactClass({
getInitialState() {
return {
eventSwitchIsOn: false,
@@ -132,7 +133,7 @@ var EventSwitchExample = React.createClass({
}
});
var SizeSwitchExample = React.createClass({
var SizeSwitchExample = createReactClass({
getInitialState() {
return {
trueSwitchIsOn: true,

View File

@@ -1,3 +1,4 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import { Image, StyleSheet, Text, View } from 'react-native'
@@ -25,7 +26,7 @@ import { Image, StyleSheet, Text, View } from 'react-native'
* @flow
*/
var Entity = React.createClass({
var Entity = createReactClass({
render: function() {
return (
<Text style={{fontWeight: '500', color: '#527fe4'}}>
@@ -35,7 +36,7 @@ var Entity = React.createClass({
}
});
var AttributeToggler = React.createClass({
var AttributeToggler = createReactClass({
getInitialState: function() {
return {fontWeight: 'bold', fontSize: 15};
},

View File

@@ -1,3 +1,4 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import {
@@ -112,7 +113,7 @@ const examples = [
},
}];
var TextOnPressBox = React.createClass({
var TextOnPressBox = createReactClass({
getInitialState: function() {
return {
timesPressed: 0,
@@ -148,7 +149,7 @@ var TextOnPressBox = React.createClass({
}
});
var TouchableFeedbackEvents = React.createClass({
var TouchableFeedbackEvents = createReactClass({
getInitialState: function() {
return {
eventLog: [],
@@ -187,7 +188,7 @@ var TouchableFeedbackEvents = React.createClass({
},
});
var TouchableDelayEvents = React.createClass({
var TouchableDelayEvents = createReactClass({
getInitialState: function() {
return {
eventLog: [],
@@ -226,7 +227,7 @@ var TouchableDelayEvents = React.createClass({
},
});
var ForceTouchExample = React.createClass({
var ForceTouchExample = createReactClass({
getInitialState: function() {
return {
force: 0,
@@ -260,7 +261,7 @@ var ForceTouchExample = React.createClass({
},
});
var TouchableHitSlop = React.createClass({
var TouchableHitSlop = createReactClass({
getInitialState: function() {
return {
timesPressed: 0,
@@ -302,7 +303,7 @@ var TouchableHitSlop = React.createClass({
}
});
var TouchableDisabled = React.createClass({
var TouchableDisabled = createReactClass({
render: function() {
return (
<View>

View File

@@ -1,3 +1,4 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'
@@ -50,7 +51,7 @@ var styles = StyleSheet.create({
},
});
var ViewBorderStyleExample = React.createClass({
var ViewBorderStyleExample = createReactClass({
getInitialState() {
return {
showBorder: true
@@ -91,7 +92,7 @@ var ViewBorderStyleExample = React.createClass({
}
});
var ZIndexExample = React.createClass({
var ZIndexExample = createReactClass({
getInitialState() {
return {
flipped: false

View File

@@ -1,3 +1,4 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import { Animated, StyleSheet, Text, View } from 'react-native'
@@ -24,7 +25,7 @@ import { Animated, StyleSheet, Text, View } from 'react-native'
* @flow
*/
var Flip = React.createClass({
var Flip = createReactClass({
getInitialState() {
return {
theta: new Animated.Value(45),

View File

@@ -16,6 +16,7 @@
*/
'use strict';
import createReactClass from 'create-react-class';
var React = require('react');
var ReactNative = require('react-native');
var {
@@ -94,7 +95,7 @@ class Board {
}
}
var Cell = React.createClass({
var Cell = createReactClass({
cellStyle() {
switch (this.props.player) {
case 1:
@@ -144,7 +145,7 @@ var Cell = React.createClass({
}
});
var GameEndOverlay = React.createClass({
var GameEndOverlay = createReactClass({
render() {
var board = this.props.board;
@@ -177,7 +178,7 @@ var GameEndOverlay = React.createClass({
}
});
var TicTacToeApp = React.createClass({
var TicTacToeApp = createReactClass({
getInitialState() {
return { board: new Board(), player: 1 };
},

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.0.78",
"version": "0.0.84",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
@@ -17,7 +17,7 @@
"examples": "start-storybook -p 9001 -c ./examples/.storybook --dont-track",
"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",
"release": "npm run build && npm run build:umd && npm publish",
"test": "npm run lint && npm run test:jest",
"test:jest": "jest",
"test:watch": "npm run test:jest -- --watch"
@@ -26,44 +26,46 @@
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"babel-runtime": "^6.23.0",
"create-react-class": "^15.5.2",
"debounce": "^1.0.0",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.8",
"hyphenate-style-name": "^1.0.2",
"inline-style-prefixer": "^3.0.0",
"inline-style-prefixer": "^3.0.2",
"normalize-css-color": "^1.0.2",
"react-dom": "~15.4.1",
"react-textarea-autosize": "^4.0.4",
"prop-types": "^15.5.8",
"react-timer-mixin": "^0.13.3"
},
"devDependencies": {
"@kadira/storybook": "^2.5.1",
"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-cli": "^6.24.1",
"babel-core": "^6.24.1",
"babel-eslint": "^7.2.2",
"babel-loader": "^6.4.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.0",
"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",
"enzyme": "^2.8.2",
"enzyme-to-json": "^1.5.1",
"eslint": "^3.19.0",
"eslint-config-prettier": "^1.6.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^6.10.3",
"file-loader": "^0.11.1",
"jest": "^19.0.2",
"node-libs-browser": "^0.5.3",
"prettier": "^0.19.0",
"prettier": "^1.1.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": "^2.2.1"
"react-dom": "~15.4.1",
"react-test-renderer": "^15.5.4",
"url-loader": "^0.5.8",
"webpack": "^2.3.3",
"webpack-bundle-analyzer": "^2.4.0"
},
"peerDependencies": {
"react": "~15.4.1"
"react": "15.4.x || 15.5.x",
"react-dom": "15.4.x || 15.5.x"
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause",
@@ -82,6 +84,9 @@
],
"jest": {
"testEnvironment": "jsdom",
"timers": "fake"
"timers": "fake",
"snapshotSerializers": [
"<rootDir>/node_modules/enzyme-to-json/serializer"
]
}
}

View File

@@ -13,15 +13,27 @@ 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.
`react-native-web/stylesheet` is a comparative baseline that implements a
simple `View` without much of React Native's functionality.
## Benchmark results
Typical render timings*: mean ± two standard deviations
| Implementation | Deep tree (ms) | Wide tree (ms) |
| Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `87.68` `±13.29` | `171.96` `±14.91` | |
| `react-native-web/stylesheet@0.0.81` | `90.59` `±12.03` | `190.37` `±19.65` | |
| `react-native-web@0.0.81` | `105.20` `±17.86` | `226.54` `±29.50` | `12.12` `±6.94ms` |
Other libraries
| 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` |
| `aphrodite@1.2.0` | `101.25` `±18.78` | `224.59` 22.28` |
| `glamor@3.0.0-1` | `143.39` `±23.05` | `275.21` `±21.10` |
| `react-jss@5.4.1` | `142.27` 16.62` | `318.62` `±26.19` |
| `reactxp@0.34.3` | `221.36` 23.35` | `472.61` 40.86` |
| `styled-components@2.0.0-7` | `301.92` `±39.43` | `647.80` `±102.1` |
*MacBook Pro (13-inch, Early 2011); 2.7 GHz Intel Core i7; 16 GB 1600 MHz DDR3. Google Chrome 56.

View File

@@ -23,7 +23,7 @@ const median = values => {
}
const numbers = [...values].sort((a, b) => a - b);
return (numbers[numbers.length - 1 >> 1] + numbers[numbers.length >> 1]) / 2;
return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2;
};
const standardDeviation = values => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<title>Performance tests</title>
</head>
<body>
<div class="root"></div>

View File

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

View File

@@ -2,13 +2,19 @@
"name": "performance",
"private": true,
"dependencies": {
"aphrodite": "^1.2.0",
"classnames": "^2.2.5",
"glamor": "3.0.0-1",
"marky": "^1.1.3",
"styled-components": "2.0.0-7"
"marky": "^1.2.0",
"react-jss": "^6.1.1",
"reactxp": "^0.34.3",
"styled-components": "2.0.0-15",
"styletron-client": "^2.5.1",
"styletron-utils": "^2.5.4"
},
"devDependencies": {
"css-loader": "^0.26.2",
"style-loader": "^0.13.2"
"css-loader": "^0.28.0",
"react-addons-perf": "^15.4.2",
"style-loader": "^0.16.1"
}
}

View File

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

View File

@@ -0,0 +1,107 @@
import theme from '../theme';
import React, { PropTypes, PureComponent } from 'react';
import { StyleSheet, Text } from 'react-native';
class AppText extends PureComponent {
static displayName = 'AppText';
static propTypes = {
align: PropTypes.oneOf(['center', 'left', 'right']),
color: PropTypes.oneOf(['blue', 'deepGray', 'normal', 'red', 'white']),
fontStyle: PropTypes.oneOf(['normal', 'italic']),
size: PropTypes.oneOf(['small', 'normal', 'large']),
uppercase: PropTypes.bool,
weight: PropTypes.oneOf(['normal', 'bold'])
};
render() {
const { align, color, fontStyle, size, uppercase, weight, ...other } = this.props;
const style = [
styles.root,
align && alignStyles[align],
color && colorStyles[color],
fontStyle && fontStyles[fontStyle],
size && sizeStyles[size],
weight && weightStyles[weight],
uppercase === true && styles.uppercase
];
return <Text {...other} style={style} />;
}
}
const styles = StyleSheet.create({
root: {
fontFamily: theme.fontFamily,
fontSize: theme.fontSize.normal,
fontWeight: 'normal',
lineHeight: theme.createLength(theme.lineHeight),
wordWrap: 'break-word'
},
uppercase: {
textTransform: 'uppercase'
}
});
const alignStyles = StyleSheet.create({
center: {
textAlign: 'center'
},
left: {
textAlign: 'left'
},
right: {
textAlign: 'right'
}
});
const colorStyles = StyleSheet.create({
blue: {
color: theme.colors.blue
},
deepGray: {
color: theme.colors.deepGray
},
normal: {
color: theme.colors.textBlack
},
red: {
color: theme.colors.red
},
white: {
color: theme.colors.white
}
});
const fontStyles = StyleSheet.create({
normal: {
fontStyle: 'normal'
},
italic: {
fontStyle: 'italic'
}
});
const sizeStyles = StyleSheet.create({
small: {
fontSize: theme.fontSize.small
},
normal: {
fontSize: theme.fontSize.normal
},
large: {
fontSize: theme.fontSize.large
}
});
const weightStyles = StyleSheet.create({
normal: {
fontWeight: '400'
},
bold: {
fontWeight: 'bold'
}
});
export default AppText;

View File

@@ -0,0 +1,40 @@
import React, { PureComponent, PropTypes } from 'react';
import { StyleSheet, View } from 'react-native';
class AspectRatio extends PureComponent {
static displayName = 'AspectRatio';
static propTypes = {
children: PropTypes.any,
ratio: PropTypes.number,
style: PropTypes.object
};
static defaultProps = {
ratio: 1
};
render() {
const { children, ratio, style } = this.props;
const percentage = 100 / ratio;
return (
<View style={[styles.root, style]}>
<View style={[styles.shim, { paddingBottom: `${percentage}%` }]} />
<View style={StyleSheet.absoluteFill}>{children}</View>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
overflow: 'hidden'
},
shim: {
display: 'block',
width: '100%'
}
});
export default AspectRatio;

View File

@@ -0,0 +1,49 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from '../View/aphrodite';
import { StyleSheet } from 'aphrodite';
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

@@ -1,7 +1,7 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import View from '../View';
import View from '../View/css-modules';
import styles from './styles.css';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (

View File

@@ -1,7 +1,6 @@
/* eslint-disable react/prop-types */
import { css } from 'glamor';
import React from 'react';
import View from '../View';
import View from '../View/glamor';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
@@ -16,34 +15,34 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
);
const styles = {
outer: css({
outer: {
padding: 4
}),
row: css({
},
row: {
flexDirection: 'row'
}),
color0: css({
},
color0: {
backgroundColor: '#222'
}),
color1: css({
},
color1: {
backgroundColor: '#666'
}),
color2: css({
},
color2: {
backgroundColor: '#999'
}),
color3: css({
},
color3: {
backgroundColor: 'blue'
}),
color4: css({
},
color4: {
backgroundColor: 'orange'
}),
color5: css({
},
color5: {
backgroundColor: 'red'
}),
fixed: css({
},
fixed: {
width: 20,
height: 20
})
}
};
module.exports = Box;

View File

@@ -0,0 +1,50 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import injectSheet from 'react-jss';
import React from 'react';
import View from '../View/jss';
const Box = ({ classes, color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
className={classnames({
[classes[`color${color}`]]: true,
[classes.fixed]: fixed,
[classes.row]: layout === 'row',
[classes.outer]: outer
})}
/>
);
const styles = {
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 = injectSheet(styles)(Box);

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/prop-types */
import React from 'react';
import StyleSheet from 'react-native/apis/StyleSheet';
import View from '../View';
import View from '../View/react-native-stylesheet';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View

View File

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

View File

@@ -1,5 +1,5 @@
import styled from 'styled-components';
import View from '../View';
import View from '../View/styled-components';
const getColor = color => {
switch (color) {
@@ -21,10 +21,10 @@ const getColor = color => {
};
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'};
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)};
`;

View File

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

View File

@@ -0,0 +1,52 @@
import { StyleSheet, View } from 'react-native';
import React, { Component, PropTypes } from 'react';
import theme from '../theme';
class GridView extends Component {
static displayName = 'GridView';
static propTypes = {
children: PropTypes.node,
hasGap: PropTypes.bool,
style: PropTypes.object
};
render() {
const { children, hasGap, style, ...other } = this.props;
return (
<View {...other} style={[style, styles.root, hasGap && styles.hasGap]}>
{React.Children.map(children, child => {
return (
child &&
React.cloneElement(child, {
style: [child.props.style, styles.column, hasGap && styles.hasGapColumn]
})
);
})}
</View>
);
}
}
const styles = StyleSheet.create({
root: {
flexDirection: 'row'
},
/**
* 1. Distribute all space (rather than extra space)
* 2. Prevent wide content from forcing wider flex columns
*/
column: {
flexBasis: 0, // 1
minWidth: 0 // 2
},
hasGap: {
marginHorizontal: theme.createLength(theme.spaceX * -0.5, 'rem')
},
hasGapColumn: {
marginHorizontal: theme.createLength(theme.spaceX * 0.5, 'rem')
}
});
export default GridView;

View File

@@ -0,0 +1,19 @@
import { createDOMElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconDirectMessage = props =>
createDOMElement('svg', {
children: (
<g>
<path d="M43.34 14H12.66L28 27.946z" />
<path d="M51.392 14.789L30.018 34.22c-.009.008-.028.006-.039.012-.563.5-1.266.768-1.98.768-.72 0-1.442-.258-2.017-.78L4.609 14.79A3.957 3.957 0 0 0 3 18v37a1.998 1.998 0 0 0 2 2c.464 0 .924-.162 1.292-.473L19 46h30c2.243 0 4-1.757 4-4V18a3.96 3.96 0 0 0-1.608-3.211z" />
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 56 72'
});
IconDirectMessage.metadata = { height: 72, width: 56 };
export default IconDirectMessage;

View File

@@ -0,0 +1,18 @@
import { createDOMElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconHeart = props =>
createDOMElement('svg', {
children: (
<g>
<path d="M38.723 12c-7.187 0-11.16 7.306-11.723 8.131C26.437 19.306 22.504 12 15.277 12 8.791 12 3.533 18.163 3.533 24.647 3.533 39.964 21.891 55.907 27 56c5.109-.093 23.467-16.036 23.467-31.353C50.467 18.163 45.209 12 38.723 12z" />
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 54 72'
});
IconHeart.metadata = { height: 72, width: 54 };
export default IconHeart;

View File

@@ -0,0 +1,18 @@
import { createDOMElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconReply = props =>
createDOMElement('svg', {
children: (
<g>
<path d="M41 31h-9V19a2.999 2.999 0 0 0-4.817-2.386l-21 16a3 3 0 0 0-.001 4.773l21 16a3.006 3.006 0 0 0 3.15.301A2.997 2.997 0 0 0 32 51V39h9c5.514 0 10 4.486 10 10a4 4 0 0 0 8 0c0-9.925-8.075-18-18-18z" />
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 62 72'
});
IconReply.metadata = { height: 72, width: 62 };
export default IconReply;

View File

@@ -0,0 +1,18 @@
import { createDOMElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconRetweet = props =>
createDOMElement('svg', {
children: (
<g>
<path d="M70.676 36.644A3 3 0 0 0 68 35h-7V19a4 4 0 0 0-4-4H34a4 4 0 0 0 0 8h18a1 1 0 0 1 1 .998V35h-7a3.001 3.001 0 0 0-2.419 4.775l11 15a3.003 3.003 0 0 0 4.839-.001l11-15a3.001 3.001 0 0 0 .256-3.13zM40.001 48H22a.995.995 0 0 1-.992-.96L21.001 36h7a3.001 3.001 0 0 0 2.419-4.775l-11-15a3.003 3.003 0 0 0-4.839.001l-11 15A3 3 0 0 0 6.001 36h7l.011 16.003a4 4 0 0 0 4 3.997h22.989a4 4 0 0 0 0-8z" />
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 74 72'
});
IconRetweet.metadata = { height: 72, width: 74 };
export default IconRetweet;

View File

@@ -0,0 +1,15 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
icon: {
display: 'inline-block',
fill: 'currentcolor',
height: '1.25em',
maxWidth: '100%',
position: 'relative',
userSelect: 'none',
verticalAlign: 'text-bottom'
}
});
export default styles;

View File

@@ -17,17 +17,16 @@ class DeepTree extends Component {
<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}
/>
))}
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++) {

View File

@@ -0,0 +1,144 @@
import AspectRatio from '../AspectRatio';
import GridView from '../GridView';
import TweetActionsBar from '../TweetActionsBar';
import TweetText from '../TweetText';
import UserAvatar from '../UserAvatar';
import UserNames from '../UserNames';
import { Image, StyleSheet, Text, View } from 'react-native';
import React, { Component, PropTypes } from 'react';
import theme from '../theme';
export class Tweet extends Component {
static displayName = 'Tweet';
static propTypes = {
tweet: PropTypes.object.isRequired
};
render() {
const { tweet } = this.props;
const { id, lang, media, textParts, timestamp, user } = tweet;
const { fullName, profileImageUrl, screenName } = user;
return (
<View accessibilityRole="article" accessible style={styles.root}>
<GridView hasGap>
<View style={styles.avatarColumn}>
<View
accessibilityRole="link"
accessible
href={`/${screenName}`}
style={styles.avatarLink}
>
<UserAvatar style={styles.avatar} uri={profileImageUrl} />
</View>
</View>
<View style={styles.bodyColumn}>
<View style={styles.body}>
<View style={styles.row}>
<Text
accessibilityRole="link"
children={timestamp}
href={`/${screenName}/status/${id}`}
style={styles.timestamp}
/>
<UserNames fullName={fullName} screenName={screenName} />
</View>
<View accessibilityRole="heading" aria-level="4">
<TweetText displayMode={'links'} lang={lang} textParts={textParts} />
</View>
{media
? <View style={styles.richContent}>
<AspectRatio ratio={16 / 9}>
<Image
resizeMode={Image.resizeMode.cover}
source={media.source}
style={styles.media}
/>
</AspectRatio>
</View>
: null}
</View>
<TweetActionsBar
actions={[
{ name: 'reply', label: 'Reply' },
{
name: 'retweet',
label: 'Retweet',
count: tweet.retweet_count,
highlighted: tweet.retweeted
},
{
name: 'like',
label: 'Like',
count: tweet.favorite_count,
highlighted: tweet.favorited
},
{ name: 'directMessage', label: 'Direct Message' }
]}
style={styles.actionBar}
/>
</View>
</GridView>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
paddingVertical: theme.createLength(theme.spaceY * 0.75, 'rem'),
paddingHorizontal: theme.createLength(theme.spaceX, 'rem')
},
avatarColumn: {
flexGrow: 1,
minWidth: 32
},
bodyColumn: {
flexGrow: 7
},
row: {
flexDirection: 'row',
justifyContent: 'space-between'
},
avatarLink: {
display: 'block',
flexShrink: 1,
flexGrow: 0,
width: '100%'
},
avatar: {
width: '100%'
},
body: {
marginTop: '-0.15rem'
},
timestamp: {
color: theme.colors.deepGray,
marginLeft: theme.createLength(theme.spaceX, 'rem'),
order: 1,
textDecorationLine: 'none',
whiteSpace: 'nowrap'
},
actionBar: {
marginTop: theme.createLength(theme.spaceY * 0.5, 'rem')
},
richContent: {
borderRadius: '0.35rem',
marginTop: theme.createLength(theme.spaceY * 0.5, 'rem'),
overflow: 'hidden'
},
media: {
...StyleSheet.absoluteFillObject,
margin: 'auto',
width: 'auto',
height: 'auto'
}
});
export default Tweet;

View File

@@ -0,0 +1,77 @@
import IconReply from '../Icons/Reply';
import IconHeart from '../Icons/Heart';
import IconRetweet from '../Icons/Retweet';
import IconDirectMessage from '../Icons/DirectMessage';
import { Text, View, StyleSheet } from 'react-native';
import React, { PropTypes } from 'react';
import theme from '../theme';
const getIcon = (icon, highlighted) => {
switch (icon) {
case 'like':
return <IconHeart />;
case 'reply':
return <IconReply />;
case 'retweet':
return <IconRetweet />;
case 'directMessage':
return <IconDirectMessage />;
default:
return null;
}
};
export default class TweetAction extends React.Component {
static displayName = 'TweetAction';
static propTypes = {
count: PropTypes.number,
displayMode: PropTypes.oneOf(['like', 'reply', 'retweet', 'directMessage']),
highlighted: PropTypes.bool,
onPress: PropTypes.func,
style: PropTypes.object
};
render() {
const { count, displayMode, highlighted, onPress, style } = this.props;
return (
<View accessibilityRole="button" onPress={onPress} style={[styles.root, style]}>
<Text
style={[
styles.inner,
displayMode === 'like' && highlighted && styles.likedColor,
displayMode === 'retweet' && highlighted && styles.retweetedColor
]}
>
{getIcon(displayMode, highlighted)}
{count > 0 ? <Text style={styles.count}>{count}</Text> : null}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
minHeight: theme.createLength(theme.lineHeight, 'rem'),
overflow: 'visible',
userSelect: 'none',
whiteSpace: 'nowrap'
},
inner: {
alignItems: 'center',
color: theme.colors.deepGray,
display: 'flex',
flexDirection: 'row'
},
count: {
marginLeft: '0.25em'
},
retweetedColor: {
color: theme.colors.green
},
likedColor: {
color: theme.colors.red
}
});

View File

@@ -0,0 +1,51 @@
import TweetAction from '../TweetAction';
import { View, StyleSheet } from 'react-native';
import React, { PropTypes, PureComponent } from 'react';
const actionNames = ['reply', 'retweet', 'like', 'directMessage'];
export default class TweetActionsBar extends PureComponent {
static propTypes = {
actions: PropTypes.arrayOf(
PropTypes.shape({
count: PropTypes.number,
label: PropTypes.string,
highlighted: PropTypes.bool,
name: PropTypes.oneOf(actionNames).isRequired,
onPress: PropTypes.func
})
),
style: PropTypes.object
};
render() {
const { actions, style } = this.props;
/* eslint-disable react/jsx-handler-names */
return (
<View style={[styles.root, style]}>
{actions.map((action, i) => (
<TweetAction
accessibilityLabel={actions.label}
count={action.count}
displayMode={action.name}
highlighted={action.highlighted}
key={i}
onPress={action.onPress}
style={styles.action}
/>
))}
</View>
);
}
}
const styles = StyleSheet.create({
root: {
flexDirection: 'row'
},
action: {
display: 'block',
marginRight: '10%'
}
});

View File

@@ -0,0 +1,28 @@
import AppText from '../AppText';
import TweetTextPart from '../TweetTextPart';
import React, { PropTypes } from 'react';
class TweetText extends React.Component {
static displayName = 'TweetText';
static propTypes = {
displayMode: TweetTextPart.propTypes.displayMode,
lang: PropTypes.string,
numberOfLines: PropTypes.number,
textParts: PropTypes.array.isRequired
};
render() {
const { displayMode, lang, numberOfLines, textParts, ...other } = this.props;
return (
<AppText {...other} lang={lang} numberOfLines={numberOfLines}>
{textParts.map((part, i) => (
<TweetTextPart displayMode={displayMode} key={i} part={part} />
))}
</AppText>
);
}
}
export default TweetText;

View File

@@ -0,0 +1,112 @@
/* eslint-disable react/prop-types */
import { Image, StyleSheet, Text } from 'react-native';
import React, { PropTypes } from 'react';
import theme from '../theme';
const createTextEntity = ({ part }) => <Text>{`${part.prefix}${part.text}`}</Text>;
const createTwemojiEntity = ({ part }) => (
<Image
accessibilityLabel={part.text}
draggable={false}
source={{ uri: part.emoji }}
style={styles.twemoji}
/>
);
// @mention, #hashtag, $cashtag
const createSymbolEntity = ({ displayMode, part }) => {
const links = displayMode === 'links';
return (
<Text accessibilityRole={links ? 'link' : null} href={part.url} style={[links && styles.link]}>
{`${part.prefix}${part.text}`}
</Text>
);
};
// internal links
const createLinkEntity = ({ displayMode, part }) => {
const { displayUrl, linkRelation, url } = part;
const links = displayMode === 'links';
return (
<Text
accessibilityRole={links ? 'link' : null}
href={url}
rel={links ? linkRelation : null}
style={[links && styles.link]}
>
{displayUrl}
</Text>
);
};
// external links
const createExternalLinkEntity = ({ displayMode, part }) => {
const { displayUrl, linkRelation, url } = part;
const links = displayMode === 'links';
return (
<Text
accessibilityRole={links ? 'link' : null}
href={url}
rel={links ? linkRelation : null}
style={[links && styles.link]}
target="_blank"
>
{displayUrl}
</Text>
);
};
class TweetTextPart extends React.Component {
static displayName = 'TweetTextPart';
static propTypes = {
displayMode: PropTypes.oneOf(['links', 'no-links']),
part: PropTypes.object
};
static defaultProps = {
displayMode: 'links'
};
render() {
let renderer;
const { isEmoji, isEntity, isHashtag, isMention, isMedia, isUrl } = this.props.part;
if (isEmoji || isEntity || isUrl || isMedia) {
if (isUrl) {
renderer = createExternalLinkEntity;
} else if (isHashtag || isMention) {
renderer = createSymbolEntity;
} else if (isEmoji) {
renderer = createTwemojiEntity;
} else {
renderer = createLinkEntity;
}
} else {
renderer = createTextEntity;
}
return renderer(this.props);
}
}
const styles = StyleSheet.create({
link: {
color: theme.colors.blue,
textDecorationLine: 'none',
unicodeBidi: 'embed'
},
twemoji: {
display: 'inline-block',
height: '1.25em',
width: '1.25em',
paddingRight: '0.05em',
paddingLeft: '0.1em',
verticalAlign: '-0.2em'
}
});
export default TweetTextPart;

View File

@@ -0,0 +1,64 @@
import AspectRatio from '../AspectRatio';
import { Image, StyleSheet } from 'react-native';
import React, { PropTypes, PureComponent } from 'react';
import theme from '../theme';
class UserAvatar extends PureComponent {
static displayName = 'UserAvatar';
static propTypes = {
accessibilityLabel: PropTypes.string,
circle: PropTypes.bool,
style: PropTypes.object,
uri: PropTypes.string
};
static defaultProps = {
circle: false
};
render() {
const { accessibilityLabel, circle, style, uri } = this.props;
return (
<AspectRatio ratio={1} style={[styles.root, style]}>
{uri
? <Image
accessibilityLabel={accessibilityLabel}
onLoad={this._handleLoad}
ref={this._setImageRef}
source={{ uri }}
style={[styles.image, circle && styles.circle]}
/>
: null}
</AspectRatio>
);
}
_handleLoad = () => {
this._imageRef && this._imageRef.setNativeProps(nativeProps);
};
_setImageRef = component => {
this._imageRef = component;
};
}
const nativeProps = { style: { backgroundColor: '#fff' } };
const styles = StyleSheet.create({
root: {
borderRadius: '0.35rem'
},
circle: {
borderRadius: '9999px'
},
image: {
backgroundColor: theme.colors.fadedGray,
display: 'block',
height: '100%',
width: '100%'
}
});
export default UserAvatar;

View File

@@ -0,0 +1,49 @@
import AppText from '../AppText';
import { StyleSheet } from 'react-native';
import React, { PropTypes, PureComponent } from 'react';
class UserNames extends PureComponent {
static displayName = 'UserNames';
static propTypes = {
fullName: PropTypes.string,
layout: PropTypes.oneOf(['nowrap', 'stack']),
onPress: PropTypes.func,
screenName: PropTypes.string,
style: PropTypes.object
};
static defaultProps = {
layout: 'nowrap'
};
render() {
const { fullName, layout, onPress, screenName, style, ...other } = this.props;
return (
<AppText
{...other}
color="deepGray"
numberOfLines={layout === 'nowrap' ? 1 : null}
onPress={onPress}
style={[styles.root, style]}
>
<AppText color="normal" weight="bold">{fullName}</AppText>
{layout === 'stack' ? ' \u000A' : ' '}
<AppText color="deepGray" style={styles.screenName}>{`@${screenName}`}</AppText>
</AppText>
);
}
}
const styles = StyleSheet.create({
root: {
display: 'inline-block'
},
screenName: {
unicodeBidi: 'embed',
writingDirection: 'ltr'
}
});
export default UserNames;

View File

@@ -0,0 +1,26 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { css, StyleSheet } from 'aphrodite';
const View = ({ style, ...other }) => <div {...other} className={css(styles.root, style)} />;
const styles = StyleSheet.create({
root: {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
}
});
module.exports = View;

View File

@@ -2,7 +2,7 @@
import { css } from 'glamor';
import React from 'react';
const View = props => <div {...props} className={css(viewStyle, props.style)} />;
const View = ({ style, ...other }) => <div {...other} className={css(viewStyle, ...style)} />;
const viewStyle = {
alignItems: 'stretch',
@@ -16,14 +16,6 @@ const viewStyle = {
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

View File

@@ -0,0 +1,29 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import injectSheet from 'react-jss';
import React from 'react';
const View = ({ classes, className, ...other }) => (
<div {...other} className={classnames(classes.root, className)} />
);
const styles = {
root: {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
}
};
module.exports = injectSheet(styles)(View);

View File

@@ -0,0 +1,30 @@
/* eslint-disable react/prop-types */
import React from 'react';
import StyleSheet from 'react-native/apis/StyleSheet';
import registry from 'react-native/apis/StyleSheet/registry';
import createDOMProps from 'react-native/modules/createDOMProps';
const View = props => (
<div {...createDOMProps(props, style => registry.resolve([styles.root, style]))} />
);
const styles = StyleSheet.create({
root: {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
}
});
module.exports = View;

View File

@@ -12,12 +12,6 @@ const View = styled.div`
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

@@ -10,12 +10,6 @@
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,28 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import Styletron from 'styletron-client';
import { injectStylePrefixed } from 'styletron-utils';
import React from 'react';
export const styletron = new Styletron();
const View = ({ style, ...other }) => <div {...other} className={classnames(viewStyle, ...style)} />;
const viewStyle = injectStylePrefixed(styletron, {
alignItems: 'stretch',
borderWidth: '0px',
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: '0',
margin: '0px',
padding: '0px',
position: 'relative',
// fix flexbox bugs
minHeight: '0px',
minWidth: '0px'
});
export default View;

View File

@@ -0,0 +1,37 @@
const colors = {
blue: '#1B95E0',
lightBlue: '#71C9F8',
green: '#17BF63',
orange: '#F45D22',
purple: '#794BC4',
red: '#E0245E',
white: '#FFFFFF',
yellow: '#FFAD1F',
deepGray: '#657786',
fadedGray: '#E6ECF0',
faintGray: '#F5F8FA',
gray: '#AAB8C2',
lightGray: '#CCD6DD',
textBlack: '#14171A'
};
const fontSize = {
root: '14px',
// font scale
small: '0.85rem',
normal: '1rem',
large: '1.25rem'
};
module.exports = {
colors,
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif, ' +
'"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', // emoji fonts
fontSize,
lineHeight: 1.3125,
spaceX: 0.6,
spaceY: 1.3125,
createLength(num, unit) {
return `${num}${unit}`;
}
};

View File

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

View File

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

7
performance/src/jss.js Normal file
View File

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

View File

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

9
performance/src/react-native.js vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -10,17 +10,23 @@ module.exports = {
filename: 'performance.bundle.js'
},
module: {
loaders: [
rules: [
{
test: /\.css$/,
loader: 'style-loader!css-loader?module&localIdentName=[hash:base64:8]'
use: [
'style-loader',
{
loader: 'css-loader',
options: { module: true, localIdentName: '[hash:base64:8]' }
}
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
cacheDirectory: true
use: {
loader: 'babel-loader',
options: { cacheDirectory: true }
}
}
]
@@ -33,13 +39,11 @@ module.exports = {
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
warnings: false
}
})
],

View File

@@ -14,16 +14,30 @@ ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
aphrodite@^1.2.0:
version "1.2.0"
resolved "https://artifactory.twitter.biz:443/api/npm/js-virtual/aphrodite/-/aphrodite-1.2.0.tgz#c2f30bd1cdf6a550f4a29a0f1cf22ed10e825764"
dependencies:
asap "^2.0.3"
inline-style-prefixer "^3.0.1"
string-hash "^1.1.3"
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:
asap@^2.0.3, asap@~2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"
assert@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
dependencies:
util "0.10.3"
autoprefixer@^6.3.1:
version "6.7.5"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.5.tgz#50848f39dc08730091d9495023487e7cc21f518d"
@@ -62,7 +76,7 @@ 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:
bowser@^1.0.0, bowser@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.6.0.tgz#37fc387b616cb6aef370dab4d6bd402b74c5c54d"
@@ -175,9 +189,15 @@ 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"
css-in-js-utils@^1.0.3:
version "1.0.3"
resolved "https://artifactory.twitter.biz:443/api/npm/js-virtual/css-in-js-utils/-/css-in-js-utils-1.0.3.tgz#9ac7e02f763cf85d94017666565ed68a5b5f3215"
dependencies:
hyphenate-style-name "^1.0.2"
css-loader@^0.28.0:
version "0.28.0"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.0.tgz#417cfa9789f8cde59a30ccbf3e4da7a806889bad"
dependencies:
babel-code-frame "^6.11.0"
css-selector-tokenizer "^0.7.0"
@@ -208,14 +228,20 @@ css-selector-tokenizer@^0.7.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"
css-to-react-native@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.0.3.tgz#7d3a11409ac283acef447a13d3bbd09980c68a4f"
dependencies:
css-color-keywords "^1.0.0"
fbjs "^0.8.5"
postcss-value-parser "^3.3.0"
css-vendor@^0.3.8:
version "0.3.8"
resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-0.3.8.tgz#6421cfd3034ce664fe7673972fd0119fc28941fa"
dependencies:
is-in-browser "^1.0.2"
cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
@@ -302,7 +328,7 @@ 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:
fbjs@^0.8.4, 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:
@@ -330,14 +356,6 @@ glamor@3.0.0-1:
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"
@@ -354,11 +372,15 @@ has@^1.0.1:
dependencies:
function-bind "^1.0.2"
hoist-non-react-statics@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
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:
hyphenate-style-name@^1.0.1, hyphenate-style-name@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b"
@@ -374,17 +396,32 @@ ieee754@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
ifvisible.js@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/ifvisible.js/-/ifvisible.js-1.0.6.tgz#52eb151ce89c56f15316226462e892d1f8451261"
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:
inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
inline-style-prefixer@^2.0.1, 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"
inline-style-prefixer@^3.0.1:
version "3.0.2"
resolved "https://artifactory.twitter.biz:443/api/npm/js-virtual/inline-style-prefixer/-/inline-style-prefixer-3.0.2.tgz#989865e0c5de7a946acbea71e16e02741efe0dd7"
dependencies:
bowser "^1.6.0"
css-in-js-utils "^1.0.3"
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"
@@ -393,6 +430,10 @@ is-function@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
is-in-browser@1.0.2, is-in-browser@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.0.2.tgz#f688bea8f1e5aadc3244ebc870d188cfb9b613cf"
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"
@@ -447,6 +488,71 @@ json5@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
jss-camel-case@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jss-camel-case/-/jss-camel-case-4.0.0.tgz#39ef2a137aaa1e2f160ab826845305f8efabcfd5"
jss-compose@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/jss-compose/-/jss-compose-3.0.1.tgz#0ac07f20baf1d523c211016d383dab08dcfe4186"
dependencies:
warning "^3.0.0"
jss-default-unit@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jss-default-unit/-/jss-default-unit-6.1.0.tgz#ea6ca838de119c17adbce597b21ba9c20f7f4d84"
jss-expand@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jss-expand/-/jss-expand-3.0.0.tgz#ce22bf8f9d99afa822738d82dcc3bdbf32766b1d"
jss-extend@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jss-extend/-/jss-extend-4.0.0.tgz#1c377d4efd67f34c997b699aa8bc1dab1c7edf95"
dependencies:
warning "^3.0.0"
jss-global@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/jss-global/-/jss-global-1.0.0.tgz#ba87850cd56d32e0623c3878fd5ef376a61e6f65"
jss-nested@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jss-nested/-/jss-nested-4.0.0.tgz#70d8aaaeb0ae1c0ca6ac74b329255b71e58bf72e"
dependencies:
warning "^3.0.0"
jss-preset-default@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/jss-preset-default/-/jss-preset-default-2.0.0.tgz#0368d99626b31067e8d04ab3c7cb17ba8354d422"
dependencies:
jss-camel-case "^4.0.0"
jss-compose "^3.0.0"
jss-default-unit "^6.0.0"
jss-expand "^3.0.0"
jss-extend "^4.0.0"
jss-global "^1.0.0"
jss-nested "^4.0.0"
jss-props-sort "^4.0.0"
jss-vendor-prefixer "^5.0.0"
jss-props-sort@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jss-props-sort/-/jss-props-sort-4.0.0.tgz#ef4239c6795ca304c18dce9e0395a1789ed9f78a"
jss-vendor-prefixer@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/jss-vendor-prefixer/-/jss-vendor-prefixer-5.1.0.tgz#59b73544de81e6b2efd71f6d3b744671826865a2"
dependencies:
css-vendor "^0.3.8"
jss@^7.0.0:
version "7.0.3"
resolved "https://registry.yarnpkg.com/jss/-/jss-7.0.3.tgz#87a4ff5c9398f7ee7ddc06a6b02255a4c74d9e1b"
dependencies:
is-in-browser "1.0.2"
warning "3.0.0"
loader-utils@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.0.2.tgz#a9f923c865a974623391a8602d031137fad74830"
@@ -467,6 +573,10 @@ lodash.uniq@^4.3.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
lodash@^4.17.1:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
loose-envify@^1.0.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
@@ -477,9 +587,9 @@ 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"
marky@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.0.tgz#9617ed647bbbea8f45d19526da33dec70606df42"
math-expression-evaluator@^1.2.14:
version "1.2.16"
@@ -771,6 +881,12 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
prop-types@^15.5.4, prop-types@^15.5.8:
version "15.5.8"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394"
dependencies:
fbjs "^0.8.9"
q@^1.1.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
@@ -782,6 +898,36 @@ query-string@^4.1.0:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
react-addons-perf@^15.4.2:
version "15.4.2"
resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.4.2.tgz#110bdcf5c459c4f77cb85ed634bcd3397536383b"
dependencies:
fbjs "^0.8.4"
object-assign "^4.1.0"
react-jss@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-6.1.1.tgz#01a548e6531b691186c3e8d8250980fb2938f1fe"
dependencies:
hoist-non-react-statics "^1.2.0"
jss "^7.0.0"
jss-preset-default "^2.0.0"
prop-types "^15.5.8"
reactxp@^0.34.3:
version "0.34.3"
resolved "https://registry.yarnpkg.com/reactxp/-/reactxp-0.34.3.tgz#6481992e57758ae6ffffbfde3d497527bb495032"
dependencies:
assert "^1.3.0"
ifvisible.js "^1.0.6"
lodash "^4.17.1"
rebound "^0.0.13"
synctasks "^0.2.9"
rebound@^0.0.13:
version "0.0.13"
resolved "https://registry.yarnpkg.com/rebound/-/rebound-0.0.13.tgz#4a225254caf7da756797b19c5817bf7a7941fac1"
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"
@@ -852,35 +998,55 @@ 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"
string-hash@^1.1.3:
version "1.1.3"
resolved "https://artifactory.twitter.biz:443/api/npm/js-virtual/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
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"
style-loader@^0.16.1:
version "0.16.1"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.16.1.tgz#50e325258d4e78421dd9680636b41e8661595d10"
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"
styled-components@2.0.0-15:
version "2.0.0-15"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.0.0-15.tgz#77843c9f5267c60a97e28c97719d1ee89ea28be1"
dependencies:
buffer "^5.0.3"
css-to-react-native "2.0.1"
css-to-react-native "^2.0.3"
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"
prop-types "^15.5.4"
stylis "^2.0.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"
styletron-client@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/styletron-client/-/styletron-client-2.5.1.tgz#df0b6fd65965b035d2ff58df61b422aa80e23577"
dependencies:
styletron-core "^2.5.1"
styletron-core@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/styletron-core/-/styletron-core-2.5.1.tgz#bf9e8aebc41461b81fdd22b1062f6e25862286fd"
styletron-utils@^2.5.4:
version "2.5.4"
resolved "https://registry.yarnpkg.com/styletron-utils/-/styletron-utils-2.5.4.tgz#f08cca7d58ee0338ce85e408cb32900e65620240"
dependencies:
inline-style-prefixer "^2.0.1"
stylis@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-2.0.3.tgz#054b0ad1f636181664246c103adf506c84b502e7"
supports-color@^2.0.0:
version "2.0.0"
@@ -904,6 +1070,10 @@ svgo@^0.7.0:
sax "~1.2.1"
whet.extend "~0.9.9"
synctasks@^0.2.9:
version "0.2.17"
resolved "https://registry.yarnpkg.com/synctasks/-/synctasks-0.2.17.tgz#38852f008878de2e941b6e458ddf552245268da1"
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"
@@ -922,10 +1092,22 @@ uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
util@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
dependencies:
inherits "2.0.1"
vendors@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
warning@3.0.0, warning@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
dependencies:
loose-envify "^1.0.0"
whatwg-fetch@>=0.10.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e"

View File

@@ -1,12 +1,13 @@
import StyleSheet from '../StyleSheet';
import View from '../../components/View';
import React, { Component, PropTypes } from 'react';
import { any, object } from 'prop-types';
import React, { Component } from 'react';
class ReactNativeApp extends Component {
static propTypes = {
initialProps: PropTypes.object,
rootComponent: PropTypes.any.isRequired,
rootTag: PropTypes.any
initialProps: object,
rootComponent: any.isRequired,
rootTag: any
};
render() {

View File

@@ -1,5 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
"<style id=\"react-native-stylesheet-static\">
"<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;}
@@ -13,15 +15,21 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\"react-native-stylesheet\">
<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-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
.rn-backgroundColor-wib322{background-color:transparent}
.rn-color-homxoj{color:inherit}
.rn-font-1lw9tu2{font:inherit}
.rn-textAlign-1ttztb7{text-align:inherit}
.rn-textDecoration-bauka4{text-decoration:none}
.rn-listStyle-1ebb2ja{list-style:none}
.rn-alignItems-1oszu61{-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
.rn-borderTopStyle-1efd50x{border-top-style:solid}
.rn-borderRightStyle-14skgim{border-right-style:solid}
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
@@ -31,13 +39,10 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.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}
@@ -48,7 +53,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.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-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
</style>"
`;

View File

@@ -7,7 +7,7 @@
*/
import invariant from 'fbjs/lib/invariant';
import { render } from 'react-dom/lib/ReactMount';
import { render } from 'react-dom';
import ReactNativeApp from './ReactNativeApp';
import StyleSheet from '../../apis/StyleSheet';
import React, { Component } from 'react';

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

@@ -84,11 +84,10 @@ class AsyncStorage {
static multiGet(keys: Array<string>) {
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)
);
}
/**

View File

@@ -10,7 +10,8 @@ import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import findIndex from 'array-find-index';
import invariant from 'fbjs/lib/invariant';
const connection = ExecutionEnvironment.canUseDOM &&
const connection =
ExecutionEnvironment.canUseDOM &&
(window.navigator.connection ||
window.navigator.mozConnection ||
window.navigator.webkitConnection);

View File

@@ -6,12 +6,14 @@
'use strict';
var TouchHistoryMath = require('react-dom/lib/TouchHistoryMath');
var TouchHistoryMath = require('../../vendor/TouchHistoryMath');
var currentCentroidXOfTouchesChangedAfter = TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
var currentCentroidYOfTouchesChangedAfter = TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
var previousCentroidXOfTouchesChangedAfter = TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
var previousCentroidYOfTouchesChangedAfter = TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
var previousCentroidXOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
var previousCentroidYOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
var currentCentroidX = TouchHistoryMath.currentCentroidX;
var currentCentroidY = TouchHistoryMath.currentCentroidY;

View File

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

View File

@@ -25,7 +25,8 @@ const pointerEvents = {
none: createClassName('pointerEvents', 'none')
};
const pointerEventsCss = `.${pointerEvents.auto}{pointer-events:auto;}\n` +
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` +
@@ -86,18 +87,15 @@ class StyleManager {
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;
},
[]
)
.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>`;

View File

@@ -120,23 +120,30 @@ class StyleRegistry {
_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 {
// 4x slower render
props.style[styleProp] = value;
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: [], style: {} });
return props;
},
{ classList: [] }
);
const style = prefixInlineStyles(props.style);
props.className = classListToString(props.classList);
props.style = style;
if (props.style) {
props.style = prefixInlineStyles(props.style);
}
return props;
}

View File

@@ -7,21 +7,22 @@
* @flow
*/
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 ReactPropTypeLocationNames from '../../vendor/ReactPropTypeLocationNames';
import ReactPropTypesSecret from '../../vendor/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';
import { oneOf, string } from 'prop-types';
class StyleSheetValidation {
static validateStyleProp(prop, style, caller) {
if (process.env.NODE_ENV !== 'production') {
if (allStylePropTypes[prop] === undefined) {
var message1 = '"' + prop + '" is not a valid style property.';
var message2 = '\nValid style props: ' +
var message2 =
'\nValid style props: ' +
JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' ');
styleError(message1, style, caller, message2);
} else {
@@ -29,7 +30,7 @@ class StyleSheetValidation {
style,
prop,
caller,
ReactPropTypeLocations.prop,
ReactPropTypeLocationNames.prop,
null,
ReactPropTypesSecret
);
@@ -74,13 +75,13 @@ StyleSheetValidation.addValidStylePropTypes(TextStylePropTypes);
StyleSheetValidation.addValidStylePropTypes(TextInputStylePropTypes);
StyleSheetValidation.addValidStylePropTypes(ViewStylePropTypes);
StyleSheetValidation.addValidStylePropTypes({
appearance: PropTypes.string,
clear: PropTypes.string,
cursor: PropTypes.string,
float: PropTypes.oneOf(['left', 'none', 'right']),
font: PropTypes.string /* @private */,
listStyle: PropTypes.string,
pointerEvents: PropTypes.string
appearance: string,
clear: string,
cursor: string,
float: oneOf(['left', 'none', 'right']),
font: string /* @private */,
listStyle: string,
pointerEvents: string
});
module.exports = StyleSheetValidation;

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