Compare commits

...

58 Commits

Author SHA1 Message Date
Nicolas Gallagher
bdaeac964c 0.0.107 2017-06-26 10:50:59 -07:00
Nicolas Gallagher
20257afe88 [fix] TextInput onSubmitEditing event 2017-06-26 10:38:53 -07:00
Nicolas Gallagher
99348eaedb [fix] StyleSheet: server-side rendering of styles
Problem:

The content of style sheets was being set as a child of 'style', which
causes React to escape the content. This meant that the pointer-events
selectors were broken (">" became ">") and pointer-events were
disabled for the entire server-rendered document.

Solution:

Use 'dangerouslySetInnerHTML' to avoid the CSS text being escaped.

Fix #546
2017-06-24 10:23:14 -07:00
Peter Ruibal
6cb16d059d Fix @providesModule for a few APIs
Close #542
2017-06-24 07:42:24 -07:00
Nicolas Gallagher
3c660e2ad7 [fix] SSR of Image renders source
When rendered on the server, images now include the 'src' and will be
downloaded by the browser rather than waiting for the client-side JS to
initiate image loading.

Fix #543
2017-06-24 07:37:29 -07:00
Nicolas Gallagher
e9101abefe [change] TextInput 'onKeyPress' includes modifier keys
Include modifier keys in the 'nativeEvent'. This allows keyboard
shortcuts such as "Shift+Enter" to be implemented for submiting text
input content when the modality is keyboard + mouse rather than touch.
2017-06-23 14:48:34 -07:00
Nicolas Gallagher
dfa8087f9a 0.0.106 2017-06-22 18:06:39 -07:00
Nicolas Gallagher
eaccd8799d Fix yarn.lock 2017-06-22 18:06:25 -07:00
Kenneth Lynne
85b2afc313 [fix] TextInput 'onKeyPress' return values
Match the current React Native API for 'onKeyPress'.

Fix #518
Close #537
2017-06-22 18:00:54 -07:00
Nicolas Gallagher
4865c7bcce [change] Clipboard.isSupported -> Clipboard.isAvailable
Rename this method to be consistent with AppState.isAvailable
2017-06-20 16:10:29 -07:00
Nicolas Gallagher
9e9ab78130 0.0.105 2017-06-20 14:09:16 -07:00
Nicolas Gallagher
00b795a87e [change] Nested Text renders as span 2017-06-20 14:05:30 -07:00
Nicolas Gallagher
1edf5241a1 Update webpack and babel-loader 2017-06-20 10:39:09 -07:00
Nicolas Gallagher
4cfcdef264 [fix] AppRegistry container layout
Absolute fill positioning provides better default layout for full-screen
apps.

Fix #528
2017-06-20 10:17:55 -07:00
Nicolas Gallagher
a264c0b956 0.0.104 2017-06-19 07:31:00 -07:00
Nicolas Gallagher
0d8aa24ff3 [fix] 'flex' shorthand sets 'flexBasis' to '0%'
The previous attempt at a fix (88ddeca0c6)
didn't account for 'View' handling the 'flexBasis' reset (as it used to
with 'flexShrink'). This patch moves flex style resolution into the DOM
style resolver.

Fix #426
2017-06-19 07:28:25 -07:00
Nicolas Gallagher
1b77ac4b2f Remove unused file 2017-06-18 16:00:31 -07:00
Nicolas Gallagher
44b185ed4c Add 'unimplemented view' stubs 2017-06-18 15:27:24 -07:00
Nicolas Gallagher
d1d570268a Add note about React Dev Tools to Style guide 2017-06-18 14:36:39 -07:00
Nicolas Gallagher
997c92f841 Update eslint and flow packages 2017-06-18 14:36:39 -07:00
Nicolas Gallagher
8e60690877 Update build tools 2017-06-18 14:36:39 -07:00
Nicolas Gallagher
7bab19ae6c Update inline-style-prefixer 2017-06-18 14:36:33 -07:00
Nicolas Gallagher
c7f287b207 Update React packages 2017-06-18 13:10:58 -07:00
Nicolas Gallagher
02cfbf8987 Add @providesModule annotations and update copyright notices
'providesModule' is necessary for haste support
2017-06-18 12:59:40 -07:00
Nicolas Gallagher
6203a3fec6 Misc flow and lint fixes 2017-06-18 12:59:19 -07:00
Nicolas Gallagher
d1d5461b29 Move 'hash' to 'vendor' directory 2017-06-18 12:24:04 -07:00
Nicolas Gallagher
b0ff4489a9 [fix] Switch compatibility with React Native
Add compatibility support for React Native's 'Switch' props
2017-06-18 12:14:46 -07:00
Nicolas Gallagher
635fda8d63 [fix] ProgressBar indicator overflow
This allows ProgressBar to correctly render provided styles like
'border-radius' without the indicator bar overflowing.
2017-06-17 08:48:37 -07:00
Nicolas Gallagher
5a5eb5425f Allow component styles to be editable in React Dev Tools 2017-06-15 19:30:18 -07:00
Nicolas Gallagher
44d59f4996 Use yarn to run scripts 2017-06-15 19:29:56 -07:00
Nicolas Gallagher
868ab55bac 0.0.103 2017-06-15 08:46:09 -07:00
Paul Armstrong
65d5a89040 [fix] Remove requestAnimationFrame from StyleManager
Using requestAnimationFrame while registering styles/classes slows down
iOS/Mobile Webkit. By removing it, it's possible that we add a little
bit of overhead, slowing down current cycles, but should be mostly
unnoticed.

Fix #517
2017-06-15 08:44:13 -07:00
Nicolas Gallagher
deb0a85440 [change] AppRegistry.getApplication returns React elements
This changes the return value of 'getApplication' so that the
application element and stylesheets are all available as React elements.
Also changes StyleSheet's 'renderToString' to 'getStyleSheets'.

Fix #504
2017-06-14 10:41:20 -07:00
Nicolas Gallagher
19381da37f 0.0.102 2017-06-13 15:43:40 -07:00
Nicolas Gallagher
47ba46780c [add] 'View' support for 'backgroundBlendMode' CSS
Fix #476
2017-06-13 14:22:52 -07:00
Nicolas Gallagher
88ddeca0c6 [fix] 'flex' shorthand sets 'flexBasis' to '0%'
Ref #426
2017-06-13 14:22:44 -07:00
Nicolas Gallagher
a61f71133e 0.0.101 2017-06-12 16:36:55 -07:00
Nicolas Gallagher
dad2888f7e [add] Text support for inline View and Image
When 'View' or 'Image' are within a 'Text', lay them out as
'inline-flex'.

Fix #472
2017-06-12 16:32:33 -07:00
Nicolas Gallagher
5e9e81eafe 0.0.100 2017-06-11 16:18:45 -07:00
Nicolas Gallagher
7ae2a5e188 Update README 2017-06-11 16:18:23 -07:00
Nicolas Gallagher
d13f78622b [fix] pointerEvents CSS
Fix #513
2017-06-11 14:42:01 -07:00
Nicolas Gallagher
6ae68e948f [fix] ScrollView passive event listener warning
'touchstart' and 'touchmove' listeners added to the document will
default to 'passive:true' (so that calls to 'preventDefault' will be
ignored). Source https://www.chromestatus.com/features/5093566007214080

To support 'scrollEnabled', listeners are bound to the underlying
ScrollView DOM node to avoid being passive.

Fix #477
2017-06-11 14:39:42 -07:00
Nicolas Gallagher
b1b70a420d [fix] Image support for inline SVG data
SVG data may contain characters that need escaping when applied as a CSS
background image.

Fix #512
2017-06-10 14:44:50 -07:00
Nicolas Gallagher
43d297bf59 [change] use ES2015 modules
Ref #102
2017-06-10 13:51:53 -07:00
Nicolas Gallagher
060d96b42d Fix docs deploy 2017-06-09 16:52:14 -07:00
Nicolas Gallagher
dd5f8cf641 0.0.99 2017-06-09 14:58:30 -07:00
Nicolas Gallagher
7f256c6423 Update documentation examples 2017-06-09 14:55:08 -07:00
Nicolas Gallagher
05069253a1 Add Calculator example app 2017-06-09 14:51:32 -07:00
Nicolas Gallagher
f10ac058b6 Add UIExplorer component for docs 2017-06-09 14:51:12 -07:00
Nicolas Gallagher
6aa2ac1f70 Remove ListView docs 2017-06-09 14:50:03 -07:00
Nicolas Gallagher
79e6dbaab5 Move 'NativeMethods' docs to direct manipulation guide 2017-06-09 14:49:15 -07:00
Nicolas Gallagher
fc86c876e0 Update formatter and linter 2017-06-09 12:59:39 -07:00
Nicolas Gallagher
1f25ef82ae Update jest 2017-06-09 11:56:14 -07:00
Nicolas Gallagher
5b60dcf0ff Update React packages and inline-style-prefixer 2017-06-09 11:53:43 -07:00
Nicolas Gallagher
1cf152e8a0 Update build tools 2017-06-09 11:52:38 -07:00
Nicolas Gallagher
d034a0c6ec [change] Linking.getInitialURL returns initial URL
Close #486
2017-06-09 10:44:32 -07:00
Fabrizio Moscon
33d1cdf193 Fix webpack documentation link 2017-06-09 10:28:30 -07:00
Maxime Thirouin
483a76cb5c Fix incorrect links in View documentation 2017-06-09 15:21:10 +02:00
178 changed files with 3718 additions and 2133 deletions

View File

@@ -3,6 +3,7 @@
.*/benchmarks/.*
.*/docs/.*
.*/node_modules/animated/*
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
[include]

View File

@@ -28,25 +28,25 @@ yarn
To run flow:
```
npm run flow
yarn flow
```
To run the unit tests:
```
npm run jest
yarn jest
```
…in watch mode:
```
npm run jest:watch
yarn jest:watch
```
To run all automated tests:
```
npm test
yarn test
```
## Visual tests
@@ -54,19 +54,19 @@ npm test
To run the interactive storybook:
```
npm run docs:start
yarn docs:start
```
To generate a static build of the storybook:
```
npm run docs:build
yarn docs:build
```
To run the performance benchmarks in a browser (opening `./benchmarks/index.html`):
```
npm run benchmarks
yarn benchmarks
```
## Compile and build
@@ -74,13 +74,13 @@ npm run benchmarks
To compile the source code to `dist`:
```
npm run compile
yarn compile
```
To create a UMD bundle of the library:
```
npm run build
yarn build
```
### Pre-commit
@@ -88,14 +88,14 @@ npm run build
To format and lint code before commit:
```
npm run precommit
yarn precommit
```
To format and lint the entire project:
```
npm run fmt
npm run lint
yarn fmt
yarn lint
```
### New Features

View File

@@ -16,7 +16,7 @@ Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
## Overview
"React Native for Web" is a project to bring React Native's building blocks and
touch handling to the Web. [Read more](#why).
touch handling to the Web.
Browse the [UI Explorer](https://necolas.github.io/react-native-web/storybook/)
to see React Native examples running on Web. Or try it out online with [React
@@ -27,9 +27,11 @@ Native for Web: Playground](https://www.webpackbin.com/bins/-KgucwxRbn7HRU-V-3Bc
To install in your app:
```
npm install --save react@15.5 react-dom@15.5 react-native-web
npm install --save react@15.6 react-dom@15.6 react-native-web
```
NOTE: React Native for Web supports React/ReactDOM 15.4, 15.5, or 15.6.
Read the [Getting Started](docs/guides/getting-started.md) guide.
## Documentation
@@ -50,7 +52,6 @@ Exported modules:
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
* [`Button`](docs/components/Button.md)
* [`Image`](docs/components/Image.md)
* [`ListView`](docs/components/ListView.md)
* [`ProgressBar`](docs/components/ProgressBar.md)
* [`ScrollView`](docs/components/ScrollView.md)
* [`Switch`](docs/components/Switch.md)
@@ -68,7 +69,6 @@ Exported modules:
* [`Clipboard`](docs/apis/Clipboard.md)
* [`Dimensions`](docs/apis/Dimensions.md)
* [`I18nManager`](docs/apis/I18nManager.md)
* [`NativeMethods`](docs/apis/NativeMethods.md)
* [`NetInfo`](docs/apis/NetInfo.md)
* [`PanResponder`](http://facebook.github.io/react-native/releases/0.20/docs/panresponder.html#content) (mirrors React Native)
* [`PixelRatio`](docs/apis/PixelRatio.md)
@@ -76,24 +76,6 @@ Exported modules:
* [`StyleSheet`](docs/apis/StyleSheet.md)
* [`Vibration`](docs/apis/Vibration.md)
<span id="#why"></span>
## Why?
There are many different teams at Twitter building web applications with React.
We want to share React components, libraries, and APIs between teams…much like
the OSS community tries to do. At our scale, this involves dealing with
multiple, inter-related problems including: component styles, animation, touch
interactions, layout adaptation, accessibility, RTL layout, theming, and build-
or server-rendering.
This is hard to do with React DOM, as the components are essentially the same
low-level building blocks that the browser provides. However, React Native
avoids, solves, or can solve almost all these problems. Central to this is
React Native's JavaScript style API (not strictly "CSS-in-JS") which avoids the
key [problems with CSS](https://speakerdeck.com/vjeux/react-css-in-js) by
giving up some of the complexity of CSS.
## Example code
```js
@@ -135,11 +117,12 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
## Related projects
* [react-primitives](https://github.com/lelandrichardson/react-primitives/)
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
* [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack)
* [reactxp](https://github.com/microsoft/reactxp)
* [react-web](https://github.com/taobaofed/react-web)
* [reactxp](https://github.com/microsoft/reactxp)
## License

View File

@@ -3,7 +3,7 @@ import React from 'react';
import View from '../View/aphrodite';
import { StyleSheet } from 'aphrodite';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
<View
{...other}
style={[
@@ -12,8 +12,7 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
/>;
const styles = StyleSheet.create({
outer: {

View File

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

View File

@@ -2,7 +2,7 @@
import React from 'react';
import View from '../View/glamor';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
<View
{...other}
style={[
@@ -11,8 +11,7 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
/>;
const styles = {
outer: {

View File

@@ -4,7 +4,7 @@ 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 }) => (
const Box = ({ classes, color, fixed = false, layout = 'column', outer = false, ...other }) =>
<View
{...other}
className={classnames({
@@ -13,8 +13,7 @@ const Box = ({ classes, color, fixed = false, layout = 'column', outer = false,
[classes.row]: layout === 'row',
[classes.outer]: outer
})}
/>
);
/>;
const styles = {
outer: {

View File

@@ -3,7 +3,7 @@ import React from 'react';
import StyleSheet from 'react-native/apis/StyleSheet';
import View from '../View/react-native-stylesheet';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
<View
{...other}
style={[
@@ -12,8 +12,7 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
/>;
const styles = StyleSheet.create({
outer: {

View File

@@ -2,7 +2,7 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
<View
{...other}
style={[
@@ -11,8 +11,7 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
/>;
const styles = StyleSheet.create({
outer: {

View File

@@ -2,7 +2,7 @@
import React from 'react';
import { Styles, View } from 'reactxp';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
<View
{...other}
style={[
@@ -11,8 +11,7 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
/>;
const styles = {
outer: Styles.createViewStyle({

View File

@@ -3,7 +3,7 @@ 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 }) => (
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
<View
{...other}
style={[
@@ -12,8 +12,7 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
/>;
const styles = {
outer: injectStylePrefixed(styletron, {

View File

@@ -17,7 +17,7 @@ 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) => (
Array.from({ length: breadth }).map((el, i) =>
<DeepTree
breadth={breadth}
components={components}
@@ -26,7 +26,7 @@ class DeepTree extends Component {
key={i}
wrap={wrap}
/>
))}
)}
</Box>
);
for (let i = 0; i < wrap; i++) {

View File

@@ -24,7 +24,7 @@ export default class TweetActionsBar extends PureComponent {
/* eslint-disable react/jsx-handler-names */
return (
<View style={[styles.root, style]}>
{actions.map((action, i) => (
{actions.map((action, i) =>
<TweetAction
accessibilityLabel={actions.label}
count={action.count}
@@ -34,7 +34,7 @@ export default class TweetActionsBar extends PureComponent {
onPress={action.onPress}
style={styles.action}
/>
))}
)}
</View>
);
}

View File

@@ -17,9 +17,9 @@ class TweetText extends React.Component {
return (
<AppText {...other} lang={lang} numberOfLines={numberOfLines}>
{textParts.map((part, i) => (
{textParts.map((part, i) =>
<TweetTextPart displayMode={displayMode} key={i} part={part} />
))}
)}
</AppText>
);
}

View File

@@ -5,14 +5,13 @@ import theme from '../theme';
const createTextEntity = ({ part }) => <Text>{`${part.prefix}${part.text}`}</Text>;
const createTwemojiEntity = ({ part }) => (
const createTwemojiEntity = ({ part }) =>
<Image
accessibilityLabel={part.text}
draggable={false}
source={{ uri: part.emoji }}
style={styles.twemoji}
/>
);
/>;
// @mention, #hashtag, $cashtag
const createSymbolEntity = ({ displayMode, part }) => {

View File

@@ -25,8 +25,9 @@ const fontSize = {
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
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,

View File

@@ -14,8 +14,7 @@ into `runApplication`. These should always be used as a pair.
(web) static **getApplication**(appKey:string, appParameters: object)
Returns the given application element. Use this for server-side rendering.
Return object is of type `{ element: ReactElement; stylesheet: ReactElement }`.
It's recommended that you use `sheetsheet` to render the style sheet in an app
Return object is of type `{ element: ReactElement; stylesheets: [ ReactElement ] }`.
static **registerConfig**(config: Array<AppConfig>)
@@ -41,6 +40,9 @@ Runs the application that was registered under `appKey`. The `appParameters`
must include the `rootTag` into which the application is rendered, and
optionally any `initialProps`.
On web, if the `rootTag` is a sub-section of your application it should be
styled as `position:relative` and given an explicit height.
static **unmountApplicationComponentAtRootTag**(rootTag: HTMLElement)
To "stop" an application when a view should be destroyed, call

View File

@@ -1,42 +0,0 @@
# NativeMethods
React Native for Web provides several methods to directly access the underlying
DOM node. This can be useful in cases when you want to focus a view or measure
its on-screen dimensions, for example.
The methods described are available on most of the default components provided
by React Native for Web. Note, however, that they are *not* available on the
composite components that you define in your own app. For more information, see
[Direct Manipulation](../guides/direct-manipulation.md).
## Methods
**blur**()
Removes focus from an input or view. This is the opposite of `focus()`.
**focus**()
Requests focus for the given input or view. The exact behavior triggered will
depend the type of view.
**measure**(callback: (x, y, width, height, pageX, pageY) => void)
For a given view, `measure` determines the offset relative to the parent view,
width, height, and the offset relative to the viewport. Returns the values via
an async callback.
Note that these measurements are not available until after the rendering has
been completed.
**measureLayout**(relativeToNativeNode: DOMNode, onSuccess: (x, y, width, height) => void)
Like `measure`, but measures the view relative to another view, specified as
`relativeToNativeNode`. This means that the returned `x`, `y` are relative to
the origin `x`, `y` of the ancestor view.
**setNativeProps**(nativeProps: Object)
This function sends props straight to the underlying DOM node. See the [direct
manipulation](../guides/direct-manipulation.md) guide for cases where
`setNativeProps` should be used.

View File

@@ -16,9 +16,10 @@ Each key of the object passed to `create` must define a style object.
Flattens an array of styles into a single style object.
(web) **renderToString**: function
(web) **getStyleSheets**: function
Returns a string of the stylesheet for use in server-side rendering.
Returns an array of stylesheets (`{ id, textContent }`). Useful for
compile-time or server-side rendering.
## Properties

View File

@@ -1,34 +0,0 @@
# ListView
TODO
## Props
[...ScrollView props](./ScrollView.md)
**children**: any
Content to display over the image.
**style**: style
+ ...[View#style](View.md)
## Examples
```js
import React, { Component, PropTypes } from 'react'
import { ListView } from 'react-native'
export default class ListViewExample extends Component {
static propTypes = {}
static defaultProps = {}
render() {
return (
<ListView />
)
}
}
```

View File

@@ -103,8 +103,11 @@ Callback that is called when the text input is focused.
**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.
Callback that is called when a key is pressed. This will be called with `{
nativeEvent: { key: keyValue } }` where keyValue is 'Enter` or 'Backspace' for
respective keys and the typed-in character otherwise including ' ' for space.
Modifier keys are also included in the nativeEvent. Fires before onChange
callbacks.
**onSelectionChange**: ?function

View File

@@ -18,7 +18,7 @@ NOTE: `View` will transfer all other props to the rendered HTML element.
Overrides the text that's read by a screen reader when the user interacts
with the element. (This is implemented using `aria-label`.)
See the [Accessibility guide](../guides/accessibility) for more information.
See the [Accessibility guide](../guides/accessibility.md) for more information.
**accessibilityLiveRegion**: ?enum('assertive', 'none', 'polite')
@@ -29,7 +29,7 @@ priority. When regions are specified as `assertive`, assistive technologies
will interrupt and immediately notify the user. (This is implemented using
[`aria-live`](http://www.w3.org/TR/wai-aria/states_and_properties#aria-live).)
See the [Accessibility guide](../guides/accessibility) for more information.
See the [Accessibility guide](../guides/accessibility.md) for more information.
(web) **accessibilityRole**: ?enum(roles)
@@ -38,7 +38,7 @@ in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an `accessibilityRole` of
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
See the [Accessibility guide](../guides/accessibility) for more information.
See the [Accessibility guide](../guides/accessibility.md) for more information.
**accessible**: ?boolean
@@ -47,7 +47,7 @@ focusable) and groups its child content. By default, all the touchable elements
and elements with `accessibilityRole` of `button` and `link` are accessible.
(This is implemented using `tabindex`.)
See the [Accessibility guide](../guides/accessibility) for more information.
See the [Accessibility guide](../guides/accessibility.md) for more information.
**children**: ?element
@@ -69,7 +69,7 @@ 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.
See the [Accessibility guide](../guides/accessibility.md) for more information.
**onLayout**: ?function
@@ -132,15 +132,15 @@ Controls whether the View can be the target of touch events. The enhanced
`box-none` is the equivalent of:
```css
.box-none { pointer-events: none }
.box-none * { pointer-events: auto }
.box-none { pointer-events: none !important; }
.box-none > * { pointer-events: auto; }
```
`box-only` is the equivalent of:
```css
.box-only { pointer-events: auto }
.box-only * { pointer-events: none }
.box-only { pointer-events: auto !important; }
.box-only > * { pointer-events: none; }
```
**style**: ?style
@@ -158,6 +158,7 @@ Controls whether the View can be the target of touch events. The enhanced
+ `animationTimingFunction`
+ `backfaceVisibility`
+ `backgroundAttachment`
+ `backgroundBlendMode`
+ `backgroundClip`
+ `backgroundColor`
+ `backgroundImage`
@@ -279,7 +280,7 @@ Default:
};
```
(See [facebook/css-layout](https://github.com/facebook/css-layout)).
(See [facebook/yoga](https://github.com/facebook/yoga)).
**testID**: ?string

View File

@@ -1,27 +1,68 @@
# Direct manipulation
It is sometimes necessary to make changes directly to a component without using
state/props to trigger a re-render of the entire subtree in the browser, this
is done by directly modifying a DOM node. `setNativeProps` is the React Native
equivalent to setting properties directly on a DOM node. Use direct
manipulation when frequent re-rendering creates a performance bottleneck. Direct
manipulation will not be a tool that you reach for frequently.
React Native for Web provides several methods to directly access the underlying
DOM node. This can be useful when you need to make changes directly to a
component without using state/props to trigger a re-render of the entire
subtree, or when you want to focus a view or measure its on-screen dimensions.
## `setNativeProps` and `shouldComponentUpdate`
The methods described are available on most of the default components provided
by React Native for Web. Note, however, that they are *not* available on the
composite components that you define in your own app.
## Instance methods
**blur**()
Removes focus from an input or view. This is the opposite of `focus()`.
**focus**()
Requests focus for the given input or view. The exact behavior triggered will
depend the type of view.
**measure**(callback: (x, y, width, height, pageX, pageY) => void)
For a given view, `measure` determines the offset relative to the parent view,
width, height, and the offset relative to the viewport. Returns the values via
an async callback.
Note that these measurements are not available until after the rendering has
been completed.
**measureLayout**(relativeToNativeNode: DOMNode, onSuccess: (x, y, width, height) => void)
Like `measure`, but measures the view relative to another view, specified as
`relativeToNativeNode`. This means that the returned `x`, `y` are relative to
the origin `x`, `y` of the ancestor view.
**setNativeProps**(nativeProps: Object)
This function sends props straight to the underlying DOM node. See the [direct
manipulation](../guides/direct-manipulation.md) guide for cases where
`setNativeProps` should be used.
## About `setNativeProps`
`setNativeProps` is the React Native equivalent to setting properties directly
on a DOM node. Use direct manipulation when frequent re-rendering creates a
performance bottleneck. Direct manipulation will not be a tool that you reach
for frequently.
### `setNativeProps` and `shouldComponentUpdate`
`setNativeProps` is imperative and stores state in the native layer (DOM,
UIView, etc.) and not within your React components, which makes your code more
difficult to reason about. Before you use it, try to solve your problem with
`setState` and `shouldComponentUpdate`.
## Avoiding conflicts with the render function
### Avoiding conflicts with the render function
If you update a property that is also managed by the render function, you might
end up with some unpredictable and confusing bugs because anytime the component
re-renders and that property changes, whatever value was previously set from
`setNativeProps` will be completely ignored and overridden.
## Why use `setNativeProps` on Web?
### Why use `setNativeProps` on Web?
Using `setNativeProps` in web-specific code is required when making changes to
`className` or `style`, as these properties are controlled by React Native for
@@ -35,7 +76,7 @@ setOpacityTo(value) {
}
```
## Composite components and `setNativeProps`
### Composite components and `setNativeProps`
Composite components are not backed by a DOM node, so you cannot call
`setNativeProps` on them. Consider this example:
@@ -63,7 +104,7 @@ prop on it and have that work - you would need to pass the style prop down to a
child, unless you are wrapping a native component. Similarly, we are going to
forward `setNativeProps` to a native-backed child component.
## Forward `setNativeProps` to a child
### Forward `setNativeProps` to a child
All we need to do is provide a `setNativeProps` method on our component that
calls `setNativeProps` on the appropriate child with the given arguments.
@@ -86,7 +127,7 @@ class MyButton extends React.Component {
You can now use `MyButton` inside of `TouchableOpacity`!
## `setNativeProps` to clear `TextInput` value
### `setNativeProps` to clear `TextInput` value
Another very common use case of `setNativeProps` is to clear the value of a
`TextInput`. For example, the following code demonstrates clearing the input

View File

@@ -14,7 +14,7 @@ polyfill.
## Webpack and Babel
[Webpack](webpack.js.org) is a popular build tool for web apps. Below is an
[Webpack](https://webpack.js.org) is a popular build tool for web apps. Below is an
example of how to configure a build that uses [Babel](https://babeljs.io/) to
compile your JavaScript for the web.
@@ -187,15 +187,16 @@ const AppContainer = (props) => { /* ... */ }
AppRegistry.registerComponent('App', () => AppContainer)
// prerender the app
const { element, stylesheet } = AppRegistry.getApplication('App', { initialProps });
const { element, stylesheets } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
const initialStyles = stylesheets.map((sheet) => ReactDOMServer.renderToStaticMarkup(sheet)).join('\n');
// construct HTML document
const document = `
<!DOCTYPE html>
<html>
<head>
${stylesheet}
${initialStyles}
</head>
<body>
${initialHTML}

View File

@@ -225,7 +225,8 @@ User Agent styles from (pseudo-)elements beyond the reach of React (e.g.,
`html`, `body`) or inline styles (e.g., `::-moz-focus-inner`). The rest is
handled at the component-level.
### What about using DevTools?
### What about using Dev Tools?
It's recommended that you rely more on React DevTools and live/hot-reloading
rather than inspecting and editing the DOM directly.
React Dev Tools supports inspecting and editing of React Native styles. It's
recommended that you rely more on React Dev Tools and live/hot-reloading rather
than inspecting and editing the DOM directly.

View File

@@ -3,15 +3,13 @@ import { StyleSheet, View } from 'react-native';
const styles = StyleSheet.create({
root: {
alignItems: 'center',
height: '100vh',
justifyContent: 'center'
minHeight: '100vh'
}
});
export default function(renderStory) {
return (
<View style={[StyleSheet.absoluteFill, styles.root]}>
<View style={styles.root}>
{renderStory()}
</View>
);

View File

@@ -0,0 +1,84 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
/**
* Examples explorer
*/
const AppText = ({ style, ...rest }) => <Text {...rest} style={[styles.baseText, style]} />;
const Link = ({ uri }) =>
<AppText accessibilityRole="link" href={uri} style={styles.link} target="_blank">
API documentation
</AppText>;
const Title = ({ children }) => <AppText style={styles.title}>{children}</AppText>;
const Description = ({ children }) => <AppText style={styles.description}>{children}</AppText>;
const Example = ({ example: { description, render, title } }) =>
<View style={styles.example}>
<AppText style={styles.exampleTitle}>{title}</AppText>
{description && <AppText style={styles.exampleDescription}>{description}</AppText>}
{render &&
<View style={styles.exampleRenderBox}>
<AppText style={styles.exampleText}>Example</AppText>
<View>{render()}</View>
</View>}
</View>;
const UIExplorer = ({ description, examples, title, url }) =>
<View style={styles.root}>
<Title>{title}</Title>
{description && <Description>{description}</Description>}
{url && <Link uri={url} />}
{examples.map((example, i) => <Example example={example} key={i} />)}
</View>;
const styles = StyleSheet.create({
root: {
padding: '1rem',
flex: 1
},
baseText: {
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif, ' +
'"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', // emoji fonts
lineHeight: '1.3125em'
},
title: {
fontSize: '2rem'
},
description: {
color: '#657786',
fontSize: '1.3125rem',
marginTop: 'calc(0.5 * 1.3125rem)'
},
link: {
color: '#1B95E0',
marginTop: 'calc(0.5 * 1.3125rem)'
},
example: {
marginTop: 'calc(2 * 1.3125rem)'
},
exampleTitle: {
fontSize: '1.3125rem'
},
exampleDescription: {
fontSize: '1rem',
marginTop: 'calc(0.5 * 1.3125rem)'
},
exampleRenderBox: {
borderColor: '#E6ECF0',
borderWidth: 1,
padding: '1.3125rem',
marginTop: '1.3125rem'
},
exampleText: {
color: '#AAB8C2',
fontSize: '0.8rem',
fontWeight: 'bold',
marginBottom: 'calc(0.5 * 1.3125rem)',
textTransform: 'uppercase'
}
});
export default UIExplorer;

View File

@@ -1,34 +1,61 @@
import { Clipboard, Text, TextInput, View } from 'react-native';
import React, { Component } from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer from '../../UIExplorer';
import { Button, Clipboard, StyleSheet, TextInput, View } from 'react-native';
import React, { Component } from 'react';
const styles = StyleSheet.create({
buttonBox: {
maxWidth: 300
},
textInput: {
borderColor: '#AAB8C2',
borderWidth: 1,
height: 200,
marginTop: 20,
padding: 10
}
});
class ClipboardExample extends Component {
render() {
return (
<View style={{ minWidth: 300 }}>
<Text onPress={this._handleSet}>Copy to clipboard</Text>
<View>
<View style={styles.buttonBox}>
<Button onPress={this._handleSet} title="Copy to clipboard" />
</View>
<TextInput
multiline={true}
placeholder={'Try pasting here afterwards'}
style={{ borderWidth: 1, height: 200, marginVertical: 20 }}
style={styles.textInput}
/>
<Text onPress={this._handleGet}>
(Clipboard.getString returns a Promise that always resolves to an empty string on web)
</Text>
</View>
);
}
_handleGet() {
Clipboard.getString().then(value => {
console.log(`Clipboard value: ${value}`);
});
}
_handleSet() {
const success = Clipboard.setString('This text was copied to the clipboard by React Native');
console.log(`Clipboard.setString success? ${success}`);
}
}
storiesOf('api: Clipboard', module).add('setString', () => <ClipboardExample />);
const examples = [
{
title: 'Clipboard.setString',
description:
'(Note that `Clipboard.getString` returns a Promise that always resolves to an empty string on web.)',
render: () => <ClipboardExample />
},
{
title: 'Clipboard.getString',
description:
'Not properly supported on Web. Returns a Promise that always resolves to an empty string on web.'
}
];
storiesOf('APIs', module).add('Clipboard', () =>
<UIExplorer
description="Clipboard gives you an interface for setting to the clipboard."
examples={examples}
title="Clipboard"
/>
);

View File

@@ -1,4 +1,5 @@
import { storiesOf } from '@kadira/storybook';
import UIExplorer from '../../UIExplorer';
import { I18nManager, StyleSheet, TouchableHighlight, Text, View } from 'react-native';
import React, { Component } from 'react';
@@ -14,7 +15,8 @@ class I18nManagerExample extends Component {
LTR/RTL layout example!
</Text>
<Text style={styles.text}>
The writing direction of text is automatically determined by the browser, independent of the global writing direction of the app.
The writing direction of text is automatically determined by the browser, independent of
the global writing direction of the app.
</Text>
<Text style={[styles.text, styles.rtlText]}>
أحب اللغة العربية
@@ -84,4 +86,13 @@ const styles = StyleSheet.create({
}
});
storiesOf('api: I18nManager', module).add('RTL layout', () => <I18nManagerExample />);
const examples = [
{
title: 'RTL toggle',
render: () => <I18nManagerExample />
}
];
storiesOf('APIs', module).add('I18nManager', () =>
<UIExplorer examples={examples} title="I18nManager" />
);

View File

@@ -1,6 +1,7 @@
import { storiesOf } from '@kadira/storybook';
import UIExplorer from '../../UIExplorer';
import { Linking, StyleSheet, Text, View } from 'react-native';
import React, { Component } from 'react';
import { storiesOf } from '@kadira/storybook';
const url = 'https://mathiasbynens.github.io/rel-noopener/malicious.html';
@@ -36,4 +37,11 @@ const styles = StyleSheet.create({
}
});
storiesOf('api: Linking', module).add('Safe linking', () => <LinkingExample />);
const examples = [
{
title: 'Safe external links',
render: () => <LinkingExample />
}
];
storiesOf('APIs', module).add('Linking', () => <UIExplorer examples={examples} title="Linking" />);

View File

@@ -1,8 +1,9 @@
/* eslint-disable react/jsx-no-bind */
/* eslint-disable react/jsx-no-bind, react/prefer-es6-class */
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer from '../../UIExplorer';
import { PanResponder, StyleSheet, View } from 'react-native';
const CIRCLE_SIZE = 80;
@@ -97,12 +98,23 @@ const styles = StyleSheet.create({
height: CIRCLE_SIZE,
borderRadius: CIRCLE_SIZE / 2,
position: 'absolute',
left: 0,
top: 0
},
container: {
flex: 1,
minHeight: 400,
paddingTop: 64
}
});
storiesOf('api: PanResponder', module).add('example', () => <PanResponderExample />);
const examples = [
{
title: 'Draggable circle',
render: () => <PanResponderExample />
}
];
storiesOf('APIs', module).add('PanResponder', () =>
<UIExplorer examples={examples} title="PanResponder" />
);

View File

@@ -1,3 +1,5 @@
/* eslint-disable react/prefer-es6-class, react/prop-types */
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
@@ -24,8 +26,9 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import TimerMixin from 'react-timer-mixin';
import UIExplorer from '../../UIExplorer';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
const ToggleAnimatingActivityIndicator = createReactClass({
mixins: [TimerMixin],
@@ -62,62 +65,59 @@ const ToggleAnimatingActivityIndicator = createReactClass({
const examples = [
{
title: 'Default',
description: 'Renders small and blue',
render() {
return <ActivityIndicator style={[styles.centering]} />;
return (
<View style={styles.horizontal}>
<ActivityIndicator />
</View>
);
}
},
{
title: 'Custom colors',
description: 'Any color value is supported',
render() {
return (
<View style={styles.horizontal}>
<ActivityIndicator color="#0000ff" />
<ActivityIndicator color="#aa00aa" />
<ActivityIndicator color="#aa3300" />
<ActivityIndicator color="#00aa00" />
<ActivityIndicator color="#1DA1F2" style={styles.rightPadding} />
<ActivityIndicator color="#17BF63" style={styles.rightPadding} />
<ActivityIndicator color="#F45D22" style={styles.rightPadding} />
<ActivityIndicator color="#794BC4" style={styles.rightPadding} />
<ActivityIndicator color="#E0245E" style={styles.rightPadding} />
<ActivityIndicator color="#FFAD1F" style={styles.rightPadding} />
</View>
);
}
},
{
title: 'Large',
render() {
return (
<ActivityIndicator color="white" size="large" style={[styles.centering, styles.gray]} />
);
}
},
{
title: 'Large, custom colors',
title: 'Custom sizes',
description:
'There are 2 predefined sizes: "small" and "large". The size can be further customized as a number (pixels) or using scale transforms',
render() {
return (
<View style={styles.horizontal}>
<ActivityIndicator color="#0000ff" size="large" />
<ActivityIndicator color="#aa00aa" size="large" />
<ActivityIndicator color="#aa3300" size="large" />
<ActivityIndicator color="#00aa00" size="large" />
<ActivityIndicator size={20} style={styles.rightPadding} />
<ActivityIndicator size="small" style={styles.rightPadding} />
<ActivityIndicator size={36} style={styles.rightPadding} />
<ActivityIndicator size="large" style={styles.rightPadding} />
<ActivityIndicator size={60} style={styles.rightPadding} />
<ActivityIndicator
size="large"
style={{ marginLeft: 20, transform: [{ scale: 1.75 }] }}
/>
</View>
);
}
},
{
title: 'Start/stop',
title: 'Animation controls (start, pause, hide)',
description: 'The animation can be paused, and the component hidden',
render() {
return (
<View style={[styles.horizontal, styles.centering]}>
<View style={[styles.horizontal]}>
<ToggleAnimatingActivityIndicator hidesWhenStopped={false} style={styles.rightPadding} />
<ToggleAnimatingActivityIndicator />
<ToggleAnimatingActivityIndicator hidesWhenStopped={false} />
</View>
);
}
},
{
title: 'Custom size',
render() {
return (
<View style={[styles.horizontal, styles.centering]}>
<ActivityIndicator size={40} />
<ActivityIndicator size="large" style={{ marginLeft: 20, transform: [{ scale: 1.5 }] }} />
</View>
);
}
@@ -125,21 +125,23 @@ const examples = [
];
const styles = StyleSheet.create({
centering: {
alignItems: 'center',
justifyContent: 'center',
padding: 8
},
gray: {
backgroundColor: '#cccccc'
},
horizontal: {
flexDirection: 'row',
justifyContent: 'space-around',
padding: 8
alignItems: 'center',
flexDirection: 'row'
},
rightPadding: {
paddingRight: 10
}
});
examples.forEach(example => {
storiesOf('component: ActivityIndicator', module).add(example.title, () => example.render());
});
storiesOf('Components', module).add('ActivityIndicator', () =>
<UIExplorer
description="Displays a customizable activity indicator"
examples={examples}
title="ActivityIndicator"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/ActivityIndicator.md"
/>
);

View File

@@ -1,52 +1,66 @@
import React from 'react';
import UIExplorer from '../../UIExplorer';
import { action, storiesOf } from '@kadira/storybook';
import { Button, View } from 'react-native';
import { Button, StyleSheet, View } from 'react-native';
const onButtonPress = action('Button has been pressed!');
const examples = [
{
title: 'Simple Button',
description: 'The title and onPress handler are required. It is ' +
'recommended to set accessibilityLabel to help make your app usable by ' +
'everyone.',
render: function() {
title: 'Default',
description:
'The title and onPress handler are required. It is ' +
'recommended to set "accessibilityLabel" to help make your app usable by ' +
'everyone.',
render() {
return (
<Button
accessibilityLabel="See an informative alert"
onPress={onButtonPress}
title="Press Me"
title="Press me"
/>
);
}
},
{
title: 'Adjusted color',
description: 'Adjusts the color in a way that looks standard on each ' +
'platform. On iOS, the color prop controls the color of the text. On ' +
'Android, the color adjusts the background color of the button.',
render: function() {
title: 'Custom colors',
description:
'Adjusts the color in a way that looks standard on each ' +
'platform. On iOS, the color prop controls the color of the text. On ' +
'Android, the color adjusts the background color of the button.',
render() {
return (
<Button
accessibilityLabel="Learn more about purple"
color="#841584"
onPress={onButtonPress}
title="Press Purple"
/>
<View>
<Button color="#17BF63" onPress={onButtonPress} title="Press me" />
<View style={styles.verticalDivider} />
<Button color="#F45D22" onPress={onButtonPress} title="Press me" />
<View style={styles.verticalDivider} />
<Button color="#794BC4" onPress={onButtonPress} title="Press me" />
<View style={styles.verticalDivider} />
<Button color="#E0245E" onPress={onButtonPress} title="Press me" />
</View>
);
}
},
{
title: 'Disabled',
description: 'All interactions for the component are disabled.',
render() {
return <Button disabled onPress={onButtonPress} title="Disabled button" />;
}
},
{
title: 'Fit to text layout',
description: 'This layout strategy lets the title define the width of the button',
render: function() {
render() {
return (
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<View style={styles.horizontal}>
<Button
accessibilityLabel="This sounds great!"
onPress={onButtonPress}
title="This looks great!"
/>
<View style={styles.horizontalDivider} />
<Button
accessibilityLabel="Ok, Great!"
color="#841584"
@@ -56,23 +70,26 @@ const examples = [
</View>
);
}
},
{
title: 'Disabled Button',
description: 'All interactions for the component are disabled.',
render: function() {
return (
<Button
accessibilityLabel="See an informative alert"
disabled
onPress={onButtonPress}
title="I Am Disabled"
/>
);
}
}
];
examples.forEach(example => {
storiesOf('component: Button', module).add(example.title, () => example.render());
const styles = StyleSheet.create({
horizontalDivider: {
width: '0.6rem'
},
horizontal: {
flexDirection: 'row'
},
verticalDivider: {
height: '1.3125rem'
}
});
storiesOf('Components', module).add('Button', () =>
<UIExplorer
description="A basic button component. Supports a minimal level of customization. You can build your own custom button using &quot;TouchableOpacity&quot; or &quot;TouchableNativeFeedback&quot;"
examples={examples}
title="Button"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/Button.md"
/>
);

View File

@@ -1,4 +1,4 @@
/* eslint-disable react/jsx-no-bind */
/* eslint-disable react/jsx-no-bind, react/prefer-es6-class, react/prop-types */
/**
* Copyright (c) 2013-present, Facebook, Inc.
@@ -23,32 +23,34 @@
* @flow
*/
import createReactClass from 'create-react-class';
import React from 'react';
import UIExplorer from '../../UIExplorer';
import { storiesOf } from '@kadira/storybook';
import { ActivityIndicator, Image, StyleSheet, Text, View } from 'react-native';
const base64Icon =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg==';
const dataSvg =
'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>';
//var ImageCapInsetsExample = require('./ImageCapInsetsExample');
const IMAGE_PREFETCH_URL = 'http://origami.design/public/images/bird-logo.png?r=1&t=' + Date.now();
const prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL);
const NetworkImageCallbackExample = createReactClass({
getInitialState: function() {
return {
class NetworkImageCallbackExample extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
events: [],
startLoadPrefetched: false,
mountTime: new Date()
};
},
}
componentWillMount() {
this.setState({ mountTime: new Date() });
},
}
render: function() {
render() {
const { mountTime } = this.state;
return (
@@ -90,24 +92,21 @@ const NetworkImageCallbackExample = createReactClass({
</Text>
</View>
);
},
_loadEventFired(event) {
this.setState(state => {
return (state.events = [...state.events, event]);
});
}
});
const NetworkImageExample = createReactClass({
getInitialState: function() {
return {
error: false,
loading: false,
progress: 0
};
},
render: function() {
_loadEventFired = event => {
this.setState(state => (state.events = [...state.events, event]));
};
}
class NetworkImageExample extends React.Component {
state = {
error: false,
loading: false,
progress: 0
};
render() {
const loader = this.state.loading
? <View style={styles.progress}>
<Text>{this.state.progress}%</Text>
@@ -130,21 +129,21 @@ const NetworkImageExample = createReactClass({
{loader}
</Image>;
}
});
}
const ImageSizeExample = createReactClass({
getInitialState: function() {
return {
width: 0,
height: 0
};
},
componentDidMount: function() {
class ImageSizeExample extends React.Component {
state = {
width: 0,
height: 0
};
componentDidMount() {
Image.getSize(this.props.source.uri, (width, height) => {
this.setState({ width, height });
});
},
render: function() {
}
render() {
return (
<View>
<Text>
@@ -163,7 +162,7 @@ const ImageSizeExample = createReactClass({
</View>
);
}
});
}
/*
var MultipleSourcesExample = createReactClass({
@@ -226,9 +225,10 @@ var MultipleSourcesExample = createReactClass({
const examples = [
{
title: 'Plain Network Image',
description: 'If the `source` prop `uri` property is prefixed with ' +
'"http", then it will be downloaded from the network.',
title: 'Plain network image',
description:
'If the `source` prop `uri` property is prefixed with ' +
'"http", then it will be downloaded from the network.',
render: function() {
return (
<Image
@@ -243,9 +243,10 @@ const examples = [
}
},
{
title: 'Plain Static Image',
description: 'Static assets should be placed in the source code tree, and ' +
'required in the same way as JavaScript modules.',
title: 'Plain static image',
description:
'Static assets should be placed in the source code tree, and ' +
'required in the same way as JavaScript modules.',
render: function() {
return (
<View style={styles.horizontal}>
@@ -258,8 +259,8 @@ const examples = [
}
},
{
title: 'Image Loading Events',
render: function() {
title: 'Image loading events',
render() {
return (
<NetworkImageCallbackExample
prefetchedSource={{ uri: IMAGE_PREFETCH_URL }}
@@ -269,31 +270,19 @@ const examples = [
}
},
{
title: 'Error Handler',
render: function() {
title: 'Error handler',
render() {
return (
<NetworkImageExample
source={{ uri: 'http://TYPO_ERROR_facebook.github.io/react/img/logo_og.png' }}
/>
);
},
platform: 'ios'
},
{
title: 'Image Download Progress',
render: function() {
return (
<NetworkImageExample
source={{ uri: 'http://origami.design/public/images/bird-logo.png?r=1' }}
/>
);
},
platform: 'ios'
}
},
{
title: 'defaultSource',
description: 'Show a placeholder image when a network image is loading',
render: function() {
render() {
return (
<Image
defaultSource={require('./bunny.png')}
@@ -301,38 +290,51 @@ const examples = [
style={styles.base}
/>
);
},
platform: 'ios'
}
},
{
title: 'Border Color',
render: function() {
title: 'Border color',
render() {
return (
<View style={styles.horizontal}>
<Image
source={smallImage}
style={[styles.base, styles.background, { borderWidth: 3, borderColor: '#f099f0' }]}
style={[
styles.base,
styles.background,
{
borderColor: '#f099f0',
borderWidth: 3
}
]}
/>
</View>
);
}
},
{
title: 'Border Width',
render: function() {
title: 'Border width',
render() {
return (
<View style={styles.horizontal}>
<Image
source={smallImage}
style={[styles.base, styles.background, { borderWidth: 5, borderColor: '#f099f0' }]}
style={[
styles.base,
styles.background,
{
borderColor: '#f099f0',
borderWidth: 5
}
]}
/>
</View>
);
}
},
{
title: 'Border Radius',
render: function() {
title: 'Border radius',
render() {
return (
<View style={styles.horizontal}>
<Image source={fullImage} style={[styles.base, { borderRadius: 5 }]} />
@@ -345,8 +347,8 @@ const examples = [
}
},
{
title: 'Background Color',
render: function() {
title: 'Background color',
render() {
return (
<View style={styles.horizontal}>
<Image source={smallImage} style={styles.base} />
@@ -368,7 +370,7 @@ const examples = [
},
{
title: 'Opacity',
render: function() {
render() {
return (
<View style={styles.horizontal}>
<Image source={fullImage} style={[styles.base, { opacity: 1 }]} />
@@ -383,7 +385,7 @@ const examples = [
},
{
title: 'Nesting',
render: function() {
render() {
return (
<Image source={fullImage} style={{ width: 60, height: 60, backgroundColor: 'transparent' }}>
<Text style={styles.nestedText}>
@@ -393,63 +395,10 @@ const examples = [
);
}
},
/*
{
title: 'Tint Color',
description: 'The `tintColor` style prop changes all the non-alpha ' +
'pixels to the tint color.',
render: function() {
return (
<View>
<View style={styles.horizontal}>
<Image
source={require('./uie_thumb_normal@2x.png')}
style={[styles.icon, {borderRadius: 5, tintColor: '#5ac8fa' }]}
/>
<Image
source={require('./uie_thumb_normal@2x.png')}
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#4cd964' }]}
/>
<Image
source={require('./uie_thumb_normal@2x.png')}
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#ff2d55' }]}
/>
<Image
source={require('./uie_thumb_normal@2x.png')}
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#8e8e93' }]}
/>
</View>
<Text style={styles.sectionText}>
It also works with downloaded images:
</Text>
<View style={styles.horizontal}>
<Image
source={smallImage}
style={[styles.base, {borderRadius: 5, tintColor: '#5ac8fa' }]}
/>
<Image
source={smallImage}
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#4cd964' }]}
/>
<Image
source={smallImage}
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#ff2d55' }]}
/>
<Image
source={smallImage}
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#8e8e93' }]}
/>
</View>
</View>
);
},
},
*/
{
title: 'Resize Mode',
description: 'The `resizeMode` style prop controls how the image is ' +
'rendered within the frame.',
render: function() {
title: 'Resize mode',
description: 'The `resizeMode` style prop controls how the image is rendered within the frame.',
render() {
return (
<View>
{[smallImage, fullImage].map((image, index) => {
@@ -510,42 +459,35 @@ const examples = [
},
{
title: 'Animated GIF',
render: function() {
render() {
return (
<Image
source={{
uri: 'http://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif'
uri:
'http://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif'
}}
style={styles.gif}
/>
);
},
platform: 'ios'
}
},
{
title: 'Base64 image',
render: function() {
render() {
return <Image source={{ uri: base64Icon, scale: 3 }} style={styles.base64} />;
},
platform: 'ios'
}
},
/*
{
title: 'Cap Insets',
title: 'Data SVG',
render() {
return <Image source={{ uri: dataSvg, scale: 3 }} style={styles.base64} />;
}
},
{
title: 'Image dimensions',
description:
'When the image is resized, the corners of the size specified ' +
'by capInsets will stay a fixed size, but the center content and ' +
'borders of the image will be stretched. This is useful for creating ' +
'resizable rounded buttons, shadows, and other resizable assets.',
render: function() {
return <ImageCapInsetsExample />;
},
platform: 'ios',
},
*/
{
title: 'Image Size',
render: function() {
'`Image.getSize` provides the dimensions of an image as soon as they are available (i.e., before loading is complete)',
render() {
return (
<ImageSizeExample
source={{
@@ -555,18 +497,6 @@ const examples = [
);
}
}
/*
{
title: 'MultipleSourcesExample',
description:
'The `source` prop allows passing in an array of uris, so that native to choose which image ' +
'to diplay based on the size of the of the target image',
render: function() {
return <MultipleSourcesExample />;
},
platform: 'android',
},
*/
];
const fullImage = { uri: 'http://facebook.github.io/react/img/logo_og.png' };
@@ -574,12 +504,12 @@ const smallImage = { uri: 'http://facebook.github.io/react/img/logo_small_2x.png
const styles = StyleSheet.create({
base: {
width: 38,
height: 38
height: 38,
width: 38
},
progress: {
flex: 1,
alignItems: 'center',
flex: 1,
flexDirection: 'row',
width: 100
},
@@ -593,24 +523,24 @@ const styles = StyleSheet.create({
marginVertical: 6
},
nestedText: {
marginLeft: 12,
marginTop: 20,
backgroundColor: 'transparent',
color: 'white'
color: 'white',
marginLeft: 12,
marginTop: 20
},
resizeMode: {
width: 90,
height: 60,
borderColor: 'black',
borderWidth: 0.5,
borderColor: 'black'
height: 60,
width: 90
},
resizeModeText: {
fontSize: 11,
marginBottom: 3
},
icon: {
width: 15,
height: 15
height: 15,
width: 15
},
horizontal: {
flexDirection: 'row'
@@ -625,13 +555,16 @@ const styles = StyleSheet.create({
resizeMode: 'contain'
},
touchableText: {
fontWeight: '500',
color: 'blue'
color: 'blue',
fontWeight: '500'
}
});
examples.forEach(example => {
storiesOf('component: Image', module)
.addDecorator(renderStory => <View style={{ width: '100%' }}>{renderStory()}</View>)
.add(example.title, () => example.render());
});
storiesOf('Components', module).add('Image', () =>
<UIExplorer
description="An accessibile image component with support for image resizing, default image, and child content."
examples={examples}
title="Image"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/Image.md"
/>
);

View File

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

View File

@@ -1,8 +1,4 @@
import createReactClass from 'create-react-class';
import { ProgressBar, StyleSheet, View } from 'react-native';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import TimerMixin from 'react-timer-mixin';
/* eslint-disable react/prefer-es6-class */
/**
* Copyright (c) 2013-present, Facebook, Inc.
@@ -27,6 +23,13 @@ import TimerMixin from 'react-timer-mixin';
* @flow
*/
import createReactClass from 'create-react-class';
import { ProgressBar, StyleSheet, View } from 'react-native';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import TimerMixin from 'react-timer-mixin';
import UIExplorer from '../../UIExplorer';
const ProgressBarExample = createReactClass({
mixins: [TimerMixin],
@@ -54,10 +57,7 @@ const ProgressBarExample = createReactClass({
render() {
return (
<View style={styles.container}>
<ProgressBar color="purple" progress={this.getProgress(0.2)} style={styles.progressView} />
<ProgressBar color="red" progress={this.getProgress(0.4)} style={styles.progressView} />
<ProgressBar color="orange" progress={this.getProgress(0.6)} style={styles.progressView} />
<ProgressBar color="yellow" progress={this.getProgress(0.8)} style={styles.progressView} />
<ProgressBar color="#794BC4" progress={this.getProgress(0.2)} style={styles.progressView} />
</View>
);
}
@@ -65,16 +65,76 @@ const ProgressBarExample = createReactClass({
const examples = [
{
title: 'progress',
title: 'Default',
render() {
return <ProgressBar progress={0.5} />;
}
},
{
title: 'Controlled',
render() {
return <ProgressBarExample />;
}
},
{
title: 'indeterminate',
title: 'Indeterminate',
render() {
return <ProgressBar indeterminate style={styles.progressView} trackColor="#D1E3F6" />;
}
},
{
title: 'Custom colors',
render() {
return (
<View>
<ProgressBar color="#1DA1F2" progress={1} />
<View style={styles.verticalDivider} />
<ProgressBar color="#17BF63" progress={0.8} />
<View style={styles.verticalDivider} />
<ProgressBar color="#F45D22" progress={0.6} />
<View style={styles.verticalDivider} />
<ProgressBar color="#794BC4" progress={0.4} />
<View style={styles.verticalDivider} />
<ProgressBar color="#E0245E" progress={0.2} />
</View>
);
}
},
{
title: 'Custom track colors',
render() {
return (
<View>
<ProgressBar color="#1DA1F2" progress={0.1} trackColor="#D1E3F6" />
<View style={styles.verticalDivider} />
<ProgressBar color="#1DA1F2" progress={0.2} trackColor="rgba(121, 74, 196, 0.34)" />
<View style={styles.verticalDivider} />
<ProgressBar color="#1DA1F2" progress={0.3} trackColor="rgba(224, 36, 94, 0.35)" />
</View>
);
}
},
{
title: 'Custom sizes',
render() {
return (
<View>
<ProgressBar
color="#1DA1F2"
progress={0.33}
style={{ height: 10 }}
trackColor="#D1E3F6"
/>
<View style={styles.verticalDivider} />
<ProgressBar
color="#1DA1F2"
progress={0.33}
style={{ height: 15 }}
trackColor="#D1E3F6"
/>
</View>
);
}
}
];
@@ -87,9 +147,17 @@ const styles = StyleSheet.create({
progressView: {
marginTop: 20,
minWidth: 200
},
verticalDivider: {
height: '1.3125rem'
}
});
examples.forEach(example => {
storiesOf('component: ProgressBar', module).add(example.title, () => example.render());
});
storiesOf('Components', module).add('ProgressBar', () =>
<UIExplorer
description="Display an activity progress bar"
examples={examples}
title="ProgressBar"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/ProgressBar.md"
/>
);

View File

@@ -1,45 +1,51 @@
/* eslint-disable react/jsx-no-bind */
import React from 'react';
import UIExplorer from '../../UIExplorer';
import { action, storiesOf } from '@kadira/storybook';
import { ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native';
const onScroll = action('ScrollView.onScroll');
storiesOf('component: ScrollView', module)
.add('vertical', () => (
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
onScroll={onScroll}
scrollEventThrottle={1000} // 1 event per second
style={styles.scrollViewStyle}
>
{Array.from({ length: 50 }).map((item, i) => (
<View key={i} style={styles.box}>
<TouchableHighlight onPress={() => {}}><Text>{i}</Text></TouchableHighlight>
</View>
))}
</ScrollView>
</View>
))
.add('horizontal', () => (
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
horizontal
onScroll={onScroll}
scrollEventThrottle={16} // ~60 events per second
style={styles.scrollViewStyle}
>
{Array.from({ length: 50 }).map((item, i) => (
<View key={i} style={[styles.box, styles.horizontalBox]}>
<Text>{i}</Text>
</View>
))}
</ScrollView>
</View>
));
const examples = [
{
title: 'vertical',
render: () =>
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
onScroll={onScroll}
scrollEventThrottle={1000} // 1 event per second
style={styles.scrollViewStyle}
>
{Array.from({ length: 50 }).map((item, i) =>
<View key={i} style={styles.box}>
<TouchableHighlight onPress={() => {}}><Text>{i}</Text></TouchableHighlight>
</View>
)}
</ScrollView>
</View>
},
{
title: 'horizontal',
render: () =>
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
horizontal
onScroll={onScroll}
scrollEventThrottle={16} // ~60 events per second
style={styles.scrollViewStyle}
>
{Array.from({ length: 50 }).map((item, i) =>
<View key={i} style={[styles.box, styles.horizontalBox]}>
<Text>{i}</Text>
</View>
)}
</ScrollView>
</View>
}
];
const styles = StyleSheet.create({
box: {
@@ -48,7 +54,7 @@ const styles = StyleSheet.create({
borderWidth: 1
},
scrollViewContainer: {
height: '200px',
height: 200,
width: 300
},
scrollViewStyle: {
@@ -59,3 +65,11 @@ const styles = StyleSheet.create({
padding: 10
}
});
storiesOf('Components', module).add('ScrollView', () =>
<UIExplorer
examples={examples}
title="ScrollView"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/ScrollView.md"
/>
);

View File

@@ -1,4 +1,4 @@
/* eslint-disable react/jsx-no-bind */
/* eslint-disable react/jsx-no-bind, react/prefer-es6-class */
/**
* Copyright (c) 2013-present, Facebook, Inc.
@@ -27,6 +27,7 @@ import createReactClass from 'create-react-class';
import { Switch, Text, View } from 'react-native';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer from '../../UIExplorer';
const BasicSwitchExample = createReactClass({
getInitialState() {
@@ -198,6 +199,10 @@ const examples = [
}
];
examples.forEach(example => {
storiesOf('component: Switch', module).add(example.title, () => example.render());
});
storiesOf('Components', module).add('Switch', () =>
<UIExplorer
examples={examples}
title="Switch"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/Switch.md"
/>
);

View File

@@ -1,4 +1,4 @@
/* eslint-disable react/jsx-no-bind */
/* eslint-disable react/jsx-no-bind, react/prefer-es6-class, react/prop-types */
/**
* Copyright (c) 2013-present, Facebook, Inc.
@@ -26,6 +26,7 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer from '../../UIExplorer';
import { Image, Text, View } from 'react-native';
const Entity = createReactClass({
@@ -268,9 +269,10 @@ const examples = [
},
{
title: 'Nested',
description: 'Nested text components will inherit the styles of their ' +
'parents (only backgroundColor is inherited from non-Text parents). ' +
'<Text> only supports other <Text> and raw text (strings) as children.',
description:
'Nested text components will inherit the styles of their ' +
'parents (only backgroundColor is inherited from non-Text parents). ' +
'<Text> only supports other <Text> and raw text (strings) as children.',
render: function() {
return (
<View>
@@ -362,7 +364,7 @@ const examples = [
render: function() {
return (
<Text>
A {'generated'} {' '} {'string'} and some &nbsp;&nbsp;&nbsp; spaces
A {'generated'} {' '} {'string'} and some &nbsp;&nbsp;&nbsp; spaces
</Text>
);
}
@@ -453,7 +455,8 @@ const examples = [
style={{ backgroundColor: 'white', textDecorationLine: 'underline', color: 'blue' }}
suppressHighlighting={false}
>
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud
</Text>
{' '}
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
@@ -469,7 +472,8 @@ const examples = [
<View>
<Text>
By default, text will respect Text Size accessibility setting on iOS.
It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
It means that all font sizes will be increased or descreased depending on the value of
Text Size setting in
{' '}
<Text style={{ fontWeight: 'bold' }}>
Settings.app - Display & Brightness - Text Size
@@ -558,8 +562,10 @@ const examples = [
}
];
examples.forEach(example => {
storiesOf('component: Text', module)
.addDecorator(renderStory => <View style={{ width: 320 }}>{renderStory()}</View>)
.add(example.title, () => example.render());
});
storiesOf('Components', module).add('Text', () =>
<UIExplorer
examples={examples}
title="Text"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/Text.md"
/>
);

View File

@@ -25,8 +25,9 @@
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import { any, bool, object, string } from 'prop-types';
import { StyleSheet, Text, TextInput, View } from 'react-native';
import UIExplorer from '../../UIExplorer';
import { any, bool, string } from 'prop-types';
import { StyleSheet, Text, TextInput, TextPropTypes, View } from 'react-native';
class WithLabel extends React.Component {
static propTypes = {
@@ -107,7 +108,8 @@ class AutoExpandingTextInput extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about — learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native.',
text:
'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about — learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native.',
height: 0
};
}
@@ -318,7 +320,7 @@ class SelectionExample extends React.Component {
static propTypes = {
multiline: bool,
style: object,
style: TextPropTypes.style,
value: string
};
@@ -463,7 +465,7 @@ const styles = StyleSheet.create({
const examples = [
{
title: 'Auto-focus',
render: function() {
render() {
return (
<View>
<TextInput
@@ -477,19 +479,19 @@ const examples = [
},
{
title: "Live Re-Write (<sp> -> '_') + maxLength",
render: function() {
render() {
return <RewriteExample />;
}
},
{
title: 'Live Re-Write (no spaces allowed)',
render: function() {
render() {
return <RewriteExampleInvalidCharacters />;
}
},
{
title: 'Auto-capitalize',
render: function() {
render() {
return (
<View>
<WithLabel label="none">
@@ -510,7 +512,7 @@ const examples = [
},
{
title: 'Auto-correct',
render: function() {
render() {
return (
<View>
<WithLabel label="true">
@@ -525,7 +527,7 @@ const examples = [
},
{
title: 'Keyboard types',
render: function() {
render() {
const keyboardTypes = [
'default',
//'ascii-capable',
@@ -552,7 +554,7 @@ const examples = [
},
{
title: 'Keyboard appearance',
render: function() {
render() {
const keyboardAppearance = ['default', 'light', 'dark'];
const examples = keyboardAppearance.map(type => {
return (
@@ -566,7 +568,7 @@ const examples = [
},
{
title: 'Return key types',
render: function() {
render() {
const returnKeyTypes = [
'default',
'go',
@@ -592,7 +594,7 @@ const examples = [
},
{
title: 'Enable return key automatically',
render: function() {
render() {
return (
<View>
<WithLabel label="true">
@@ -604,7 +606,7 @@ const examples = [
},
{
title: 'Secure text entry',
render: function() {
render() {
return (
<View>
<WithLabel label="true">
@@ -616,13 +618,13 @@ const examples = [
},
{
title: 'Event handling',
render: function(): React.Element<any> {
render(): React.Element<any> {
return <TextEventsExample />;
}
},
{
title: 'Colored input text',
render: function() {
render() {
return (
<View>
<TextInput defaultValue="Blue" style={[styles.default, { color: 'blue' }]} />
@@ -633,7 +635,7 @@ const examples = [
},
{
title: 'Colored highlight/cursor for text input',
render: function() {
render() {
return (
<View>
<TextInput defaultValue="Highlight me" selectionColor={'green'} style={styles.default} />
@@ -648,7 +650,7 @@ const examples = [
},
{
title: 'Clear button mode',
render: function() {
render() {
return (
<View>
<WithLabel label="never">
@@ -669,7 +671,7 @@ const examples = [
},
{
title: 'Clear and select',
render: function() {
render() {
return (
<View>
<WithLabel label="clearTextOnFocus">
@@ -694,13 +696,13 @@ const examples = [
},
{
title: 'Blur on submit',
render: function(): React.Element<any> {
render(): React.Element<any> {
return <BlurOnSubmitExample />;
}
},
{
title: 'Multiline blur on submit',
render: function() {
render() {
return (
<View>
<TextInput
@@ -719,7 +721,7 @@ const examples = [
},
{
title: 'Multiline',
render: function() {
render() {
return (
<View>
<TextInput multiline={true} placeholder="multiline text input" style={styles.multiline} />
@@ -758,7 +760,7 @@ const examples = [
},
{
title: 'Number of lines',
render: function() {
render() {
return (
<View>
<TextInput
@@ -772,7 +774,7 @@ const examples = [
},
{
title: 'Auto-expanding',
render: function() {
render() {
return (
<View>
<AutoExpandingTextInput
@@ -786,13 +788,13 @@ const examples = [
},
{
title: 'Attributed text',
render: function() {
render() {
return <TokenizedTextExample />;
}
},
{
title: 'Text selection & cursor placement',
render: function() {
render() {
return (
<View>
<SelectionExample style={styles.default} value="text selection can be changed" />
@@ -807,7 +809,7 @@ const examples = [
},
{
title: 'TextInput maxLength',
render: function() {
render() {
return (
<View>
<WithLabel label="maxLength: 5">
@@ -828,6 +830,10 @@ const examples = [
}
];
examples.forEach(example => {
storiesOf('component: TextInput', module).add(example.title, () => example.render());
});
storiesOf('Components', module).add('TextInput', () =>
<UIExplorer
examples={examples}
title="TextInput"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/TextInput.md"
/>
);

View File

@@ -24,7 +24,8 @@
*/
import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import UIExplorer from '../../UIExplorer';
import { action, storiesOf } from '@kadira/storybook';
import {
Image,
StyleSheet,
@@ -36,90 +37,6 @@ import {
View
} from 'react-native';
const examples = [
{
title: '<TouchableHighlight>',
description: 'TouchableHighlight works by adding an extra view with a ' +
'black background under the single child view. This works best when the ' +
'child view is fully opaque, although it can be made to work as a simple ' +
'background color change as well with the activeOpacity and ' +
'underlayColor props.',
render: function() {
return (
<View>
<View style={styles.row}>
<TouchableHighlight
onPress={() => console.log('stock THW image - highlight')}
style={styles.wrapper}
>
<Image source={heartImage} style={styles.image} />
</TouchableHighlight>
<TouchableHighlight
activeOpacity={1}
onPress={() => console.log('custom THW text - highlight')}
style={styles.wrapper}
underlayColor="rgb(210, 230, 255)"
>
<View style={styles.wrapperCustom}>
<Text style={styles.text}>
Tap Here For Custom Highlight!
</Text>
</View>
</TouchableHighlight>
</View>
</View>
);
}
},
{
title: '<Text onPress={fn}> with highlight',
render: function() {
return <TextOnPressBox />;
}
},
{
title: 'Touchable feedback events',
description: '<Touchable*> components accept onPress, onPressIn, ' +
'onPressOut, and onLongPress as props.',
render: function() {
return <TouchableFeedbackEvents />;
}
},
{
title: 'Touchable delay for events',
description: '<Touchable*> components also accept delayPressIn, ' +
'delayPressOut, and delayLongPress as props. These props impact the ' +
'timing of feedback events.',
render: function() {
return <TouchableDelayEvents />;
}
},
{
title: '3D Touch / Force Touch',
description: 'iPhone 6s and 6s plus support 3D touch, which adds a force property to touches',
render: function() {
return <ForceTouchExample />;
},
platform: 'ios'
},
{
title: 'Touchable Hit Slop',
description: '<Touchable*> components accept hitSlop prop which extends the touch area ' +
'without changing the view bounds.',
render: function() {
return <TouchableHitSlop />;
}
},
{
title: 'Disabled Touchable*',
description: '<Touchable*> components accept disabled prop which prevents ' +
'any interaction with component',
render: function() {
return <TouchableDisabled />;
}
}
];
class TextOnPressBox extends React.Component {
constructor(props) {
super(props);
@@ -393,6 +310,66 @@ class TouchableDisabled extends React.Component {
}
}
class TouchableStyling extends React.Component {
constructor(props, context) {
super(props, context);
this.buttons = ['a', 'b', 'c'];
this.state = {};
this.select = this.select.bind(this);
}
select(selectedButton) {
const newState = {};
this.buttons.forEach(button => {
newState[button] = selectedButton === button;
});
this.setState(newState);
}
render() {
return (
<View style={touchableStylingStyles.container}>
{this.buttons.map(button => {
const tstyle = [
touchableStylingStyles.textInput,
this.state[button] && touchableStylingStyles.blue
];
return (
<TouchableHighlight
key={button}
onPress={this.select.bind(this, button)}
style={tstyle}
>
<Text style={[this.state[button] && touchableStylingStyles.white]}>
Button {button} {this.state[button] ? 'On' : 'Off'}
</Text>
</TouchableHighlight>
);
})}
</View>
);
}
}
const touchableStylingStyles = StyleSheet.create({
blue: {
backgroundColor: 'rgb(100, 100, 200)',
borderColor: 'white'
},
white: {
color: 'white'
},
container: {
backgroundColor: '#f6f6f6'
},
textInput: {
borderWidth: 3,
borderColor: 'black',
padding: 20,
width: 200
}
});
const heartImage = { uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small' };
const styles = StyleSheet.create({
@@ -468,6 +445,105 @@ const styles = StyleSheet.create({
}
});
examples.forEach(example => {
storiesOf('component: Touchable*', module).add(example.title, () => example.render());
});
const examples = [
{
title: '<TouchableHighlight>',
description:
'TouchableHighlight works by adding an extra view with a ' +
'black background under the single child view. This works best when the ' +
'child view is fully opaque, although it can be made to work as a simple ' +
'background color change as well with the activeOpacity and ' +
'underlayColor props.',
render() {
return (
<View>
<View style={styles.row}>
<TouchableHighlight
onPress={() => console.log('stock THW image - highlight')}
style={styles.wrapper}
>
<Image source={heartImage} style={styles.image} />
</TouchableHighlight>
<TouchableHighlight
activeOpacity={1}
onPress={() => console.log('custom THW text - highlight')}
style={styles.wrapper}
underlayColor="rgb(210, 230, 255)"
>
<View style={styles.wrapperCustom}>
<Text style={styles.text}>
Tap Here For Custom Highlight!
</Text>
</View>
</TouchableHighlight>
</View>
</View>
);
}
},
{
title: '<Text onPress={fn}> with highlight',
render() {
return <TextOnPressBox />;
}
},
{
title: 'Touchable feedback events',
description:
'<Touchable*> components accept onPress, onPressIn, ' +
'onPressOut, and onLongPress as props.',
render() {
return <TouchableFeedbackEvents />;
}
},
{
title: 'Touchable delay for events',
description:
'<Touchable*> components also accept delayPressIn, ' +
'delayPressOut, and delayLongPress as props. These props impact the ' +
'timing of feedback events.',
render() {
return <TouchableDelayEvents />;
}
},
{
title: 'Touchable styling',
render() {
return <TouchableStyling />;
}
},
{
title: '3D Touch / Force Touch',
description: 'iPhone 6s and 6s plus support 3D touch, which adds a force property to touches',
render() {
return <ForceTouchExample />;
},
platform: 'ios'
},
{
title: 'Touchable Hit Slop',
description:
'<Touchable*> components accept hitSlop prop which extends the touch area ' +
'without changing the view bounds.',
render() {
return <TouchableHitSlop />;
}
},
{
title: 'Disabled Touchable*',
description:
'<Touchable*> components accept disabled prop which prevents ' +
'any interaction with component',
render() {
return <TouchableDisabled />;
}
}
];
storiesOf('Components', module).add('Touchables', () =>
<UIExplorer
examples={examples}
title="Touchables"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/TouchableWithoutFeedback.md"
/>
);

View File

@@ -1,8 +1,3 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native';
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
@@ -26,6 +21,16 @@ import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native';
* @flow
*/
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer from '../../UIExplorer';
import ViewTransformsExample from './ViewTransformsExample';
import { StyleSheet, Text, TouchableHighlight, TouchableWithoutFeedback, View } from 'react-native';
const logger = e => {
console.log(e.nativeEvent);
};
const styles = StyleSheet.create({
box: {
backgroundColor: '#527FE4',
@@ -51,12 +56,10 @@ const styles = StyleSheet.create({
}
});
const ViewBorderStyleExample = createReactClass({
getInitialState() {
return {
showBorder: true
};
},
class ViewBorderStyleExample extends React.Component {
state = {
showBorder: true
};
render() {
return (
@@ -89,19 +92,17 @@ const ViewBorderStyleExample = createReactClass({
</View>
</TouchableWithoutFeedback>
);
},
}
_handlePress() {
this.setState({ showBorder: !this.state.showBorder });
}
});
}
const ZIndexExample = createReactClass({
getInitialState() {
return {
flipped: false
};
},
class ZIndexExample extends React.Component {
state = {
flipped: false
};
render() {
const indices = this.state.flipped ? [-1, 0, 1, 2] : [2, 1, 0, -1];
@@ -144,17 +145,17 @@ const ZIndexExample = createReactClass({
</View>
</TouchableWithoutFeedback>
);
},
}
_handlePress() {
this.setState({ flipped: !this.state.flipped });
}
});
}
const examples = [
{
title: 'Background Color',
render: function() {
render() {
return (
<View style={{ backgroundColor: '#527FE4', padding: 5 }}>
<Text style={{ fontSize: 11 }}>
@@ -166,7 +167,7 @@ const examples = [
},
{
title: 'Border',
render: function() {
render() {
return (
<View style={{ borderColor: '#527FE4', borderWidth: 5, padding: 10 }}>
<Text style={{ fontSize: 11 }}>5px blue border</Text>
@@ -176,7 +177,7 @@ const examples = [
},
{
title: 'Padding/Margin',
render: function() {
render() {
return (
<View style={{ borderColor: '#bb0000', borderWidth: 0.5 }}>
<View style={[styles.box, { padding: 5 }]}>
@@ -199,7 +200,7 @@ const examples = [
},
{
title: 'Border Radius',
render: function() {
render() {
return (
<View style={{ borderWidth: 0.5, borderRadius: 5, padding: 5 }}>
<Text style={{ fontSize: 11 }}>
@@ -213,19 +214,19 @@ const examples = [
},
{
title: 'Border Style',
render: function() {
render() {
return <ViewBorderStyleExample />;
}
},
{
title: 'Circle with Border Radius',
render: function() {
render() {
return <View style={{ borderRadius: 10, borderWidth: 1, width: 20, height: 20 }} />;
}
},
{
title: 'Overflow',
render: function() {
render() {
return (
<View style={{ flexDirection: 'row' }}>
<View
@@ -253,7 +254,7 @@ const examples = [
},
{
title: 'Opacity',
render: function() {
render() {
return (
<View>
<View style={{ opacity: 0 }}><Text>Opacity 0</Text></View>
@@ -269,10 +270,31 @@ const examples = [
},
{
title: 'ZIndex',
render: function() {
render() {
return <ZIndexExample />;
}
},
{
title: 'Pointer Events',
render() {
return (
<View pointerEvents="box-none">
<View pointerEvents="box-none">
<View pointerEvents="none"><Text onPress={logger}>none</Text></View>
<TouchableHighlight onPress={logger} pointerEvents="auto">
<Text>auto</Text>
</TouchableHighlight>
<TouchableHighlight onPress={logger} pointerEvents="box-only">
<Text>box-only</Text>
</TouchableHighlight>
<TouchableHighlight onPress={logger} pointerEvents="box-none">
<Text>box-none</Text>
</TouchableHighlight>
</View>
</View>
);
}
},
{
title: 'Basic shadow',
render() {
@@ -286,8 +308,12 @@ const examples = [
return <View style={[styles.shadowBox, styles.shadow, { borderRadius: 50 }]} />;
}
}
];
].concat(ViewTransformsExample);
examples.forEach(example => {
storiesOf('component: View', module).add(example.title, () => example.render());
});
storiesOf('Components', module).add('View', () =>
<UIExplorer
examples={examples}
title="View"
url="https://github.com/necolas/react-native-web/blob/master/docs/components/View.md"
/>
);

View File

@@ -1,7 +1,4 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import { Animated, StyleSheet, Text, View } from 'react-native';
/* eslint-disable react/prefer-es6-class */
/**
* Copyright (c) 2013-present, Facebook, Inc.
@@ -25,24 +22,28 @@ import { Animated, StyleSheet, Text, View } from 'react-native';
* @flow
*/
const Flip = createReactClass({
getInitialState() {
return {
import React from 'react';
import { Animated, StyleSheet, Text, View } from 'react-native';
class Flip extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
theta: new Animated.Value(45)
};
},
}
componentDidMount() {
this._animate();
},
}
_animate() {
_animate = () => {
this.state.theta.setValue(0);
Animated.timing(this.state.theta, {
toValue: 360,
duration: 5000
}).start(this._animate);
},
};
render() {
return (
@@ -93,7 +94,7 @@ const Flip = createReactClass({
</View>
);
}
});
}
const styles = StyleSheet.create({
box1: {
@@ -254,6 +255,4 @@ const examples = [
}
];
examples.forEach(example => {
storiesOf('component: View (transforms)', module).add(example.title, () => example.render());
});
export default examples;

View File

@@ -0,0 +1,441 @@
/* eslint-disable react/jsx-no-bind, react/prop-types */
// React Native version of https://codepen.io/mjijackson/pen/xOzyGX
import React from 'react';
import { StyleSheet, Text, TouchableHighlight, View } from 'react-native';
/**
* KeyboardInput
*/
class KeyboardInput extends React.Component {
componentDidMount() {
document.addEventListener('keydown', this.handleKeyDown);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeyDown);
}
render() {
return null;
}
handleKeyDown = event => {
if (this.props.onKeyDown) {
this.props.onKeyDown(event);
}
};
}
/**
* CalculatorDisplay
*/
class CalculatorDisplay extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
scale: 1
};
}
handleTextLayout = e => {
const { width, x } = e.nativeEvent.layout;
const maxWidth = width + x;
const newScale = maxWidth / width;
if (x < 0) {
this.setState({ scale: newScale });
} else if (x > width) {
this.setState({ scale: 1 });
}
};
render() {
const { value } = this.props;
const { scale } = this.state;
const language = navigator.language || 'en-US';
let formattedValue = parseFloat(value).toLocaleString(language, {
useGrouping: true,
maximumFractionDigits: 6
});
const trailingZeros = value.match(/\.0*$/);
if (trailingZeros) {
formattedValue += trailingZeros;
}
return (
<View style={calculatorDisplayStyles.root}>
<Text
children={formattedValue}
onLayout={this.handleTextLayout}
style={[calculatorDisplayStyles.text, { transform: [{ scale }] }]}
/>
</View>
);
}
}
const calculatorDisplayStyles = StyleSheet.create({
root: {
backgroundColor: '#1c191c',
flex: 1,
justifyContent: 'center'
},
text: {
alignSelf: 'flex-end',
color: 'white',
//lineHeight: 130,
fontSize: '5.25em',
fontWeight: '100',
fontFamily: 'Roboto, sans-serif',
paddingHorizontal: 30,
//position: 'absolute',
right: 0,
transformOrigin: 'right'
}
});
/**
* CalculatorKey
*/
class CalculatorKey extends React.Component {
render() {
const { children, onPress, style, textStyle } = this.props;
return (
<TouchableHighlight
accessibilityRole="button"
onPress={onPress}
style={[calculatorKeyStyles.root, style]}
underlayColor="rgba(0,0,0,0.25)"
>
<Text children={children} style={[calculatorKeyStyles.text, textStyle]} />
</TouchableHighlight>
);
}
}
const calculatorKeyStyles = StyleSheet.create({
root: {
width: 80,
height: 80,
borderTopWidth: 1,
borderTopColor: '#777',
borderTopStyle: 'solid',
borderRightWidth: 1,
borderRightColor: '#666',
borderRightStyle: 'solid',
outline: 'none'
},
text: {
fontWeight: '100',
fontFamily: 'Roboto, sans-serif',
lineHeight: 80,
textAlign: 'center'
}
});
const CalculatorOperations = {
'/': (prevValue, nextValue) => prevValue / nextValue,
'*': (prevValue, nextValue) => prevValue * nextValue,
'+': (prevValue, nextValue) => prevValue + nextValue,
'-': (prevValue, nextValue) => prevValue - nextValue,
'=': (prevValue, nextValue) => nextValue
};
/**
* Calculator
*/
class Calculator extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
value: null,
displayValue: '0',
operator: null,
waitingForOperand: false
};
}
clearAll() {
this.setState({
value: null,
displayValue: '0',
operator: null,
waitingForOperand: false
});
}
clearDisplay() {
this.setState({
displayValue: '0'
});
}
clearLastChar() {
const { displayValue } = this.state;
this.setState({
displayValue: displayValue.substring(0, displayValue.length - 1) || '0'
});
}
toggleSign() {
const { displayValue } = this.state;
const newValue = parseFloat(displayValue) * -1;
this.setState({
displayValue: String(newValue)
});
}
inputPercent() {
const { displayValue } = this.state;
const currentValue = parseFloat(displayValue);
if (currentValue === 0) return;
const fixedDigits = displayValue.replace(/^-?\d*\.?/, '');
const newValue = parseFloat(displayValue) / 100;
this.setState({
displayValue: String(newValue.toFixed(fixedDigits.length + 2))
});
}
inputDot() {
const { displayValue } = this.state;
if (!/\./.test(displayValue)) {
this.setState({
displayValue: displayValue + '.',
waitingForOperand: false
});
}
}
inputDigit(digit) {
const { displayValue, waitingForOperand } = this.state;
if (waitingForOperand) {
this.setState({
displayValue: String(digit),
waitingForOperand: false
});
} else {
this.setState({
displayValue: displayValue === '0' ? String(digit) : displayValue + digit
});
}
}
performOperation(nextOperator) {
const { value, displayValue, operator } = this.state;
const inputValue = parseFloat(displayValue);
if (value == null) {
this.setState({
value: inputValue
});
} else if (operator) {
const currentValue = value || 0;
const newValue = CalculatorOperations[operator](currentValue, inputValue);
this.setState({
value: newValue,
displayValue: String(newValue)
});
}
this.setState({
waitingForOperand: true,
operator: nextOperator
});
}
handleKeyDown(event) {
let { key } = event;
if (key === 'Enter') key = '=';
if (/\d/.test(key)) {
event.preventDefault();
this.inputDigit(parseInt(key, 10));
} else if (key in CalculatorOperations) {
event.preventDefault();
this.performOperation(key);
} else if (key === '.') {
event.preventDefault();
this.inputDot();
} else if (key === '%') {
event.preventDefault();
this.inputPercent();
} else if (key === 'Backspace') {
event.preventDefault();
this.clearLastChar();
} else if (key === 'Clear') {
event.preventDefault();
if (this.state.displayValue !== '0') {
this.clearDisplay();
} else {
this.clearAll();
}
}
}
render() {
const { displayValue } = this.state;
const clearDisplay = displayValue !== '0';
const clearText = clearDisplay ? 'C' : 'AC';
return (
<View style={calculatorStyles.root}>
<KeyboardInput onKeyDown={event => this.handleKeyDown(event)} />
<CalculatorDisplay value={displayValue} />
<View style={calculatorStyles.keypad}>
<View style={calculatorStyles.inputKeys}>
<View style={calculatorStyles.functionKeys}>
<FunctionKey onPress={() => (clearDisplay ? this.clearDisplay() : this.clearAll())}>
{clearText}
</FunctionKey>
<FunctionKey onPress={() => this.toggleSign()} style={calculatorStyles.keySign}>
±
</FunctionKey>
<FunctionKey onPress={() => this.inputPercent()} style={calculatorStyles.keyPercent}>
%
</FunctionKey>
</View>
<View style={calculatorStyles.digitKeys}>
<DigitKey
onPress={() => this.inputDigit(0)}
style={calculatorStyles.key0}
textStyle={{ textAlign: 'left' }}
>
0
</DigitKey>
<DigitKey
onPress={() => this.inputDot()}
style={calculatorStyles.keyDot}
textStyle={calculatorStyles.keyDotText}
>
.
</DigitKey>
<DigitKey onPress={() => this.inputDigit(1)}>1</DigitKey>
<DigitKey onPress={() => this.inputDigit(2)}>2</DigitKey>
<DigitKey onPress={() => this.inputDigit(3)}>3</DigitKey>
<DigitKey onPress={() => this.inputDigit(4)}>4</DigitKey>
<DigitKey onPress={() => this.inputDigit(5)}>5</DigitKey>
<DigitKey onPress={() => this.inputDigit(6)}>6</DigitKey>
<DigitKey onPress={() => this.inputDigit(7)}>7</DigitKey>
<DigitKey onPress={() => this.inputDigit(8)}>8</DigitKey>
<DigitKey onPress={() => this.inputDigit(9)}>9</DigitKey>
</View>
</View>
<View style={calculatorStyles.operatorKeys}>
<OperatorKey onPress={() => this.performOperation('/')}>÷</OperatorKey>
<OperatorKey onPress={() => this.performOperation('*')}>×</OperatorKey>
<OperatorKey onPress={() => this.performOperation('-')}></OperatorKey>
<OperatorKey onPress={() => this.performOperation('+')}>+</OperatorKey>
<OperatorKey onPress={() => this.performOperation('=')}>=</OperatorKey>
</View>
</View>
</View>
);
}
}
const DigitKey = props =>
<CalculatorKey
{...props}
style={[calculatorStyles.digitKey, props.style]}
textStyle={[calculatorStyles.digitKeyText, props.textStyle]}
/>;
const FunctionKey = props =>
<CalculatorKey
{...props}
style={[calculatorStyles.functionKey, props.style]}
textStyle={[calculatorStyles.functionKeyText, props.textStyle]}
/>;
const OperatorKey = props =>
<CalculatorKey
{...props}
style={[calculatorStyles.operatorKey, props.style]}
textStyle={[calculatorStyles.operatorKeyText, props.textStyle]}
/>;
const calculatorStyles = StyleSheet.create({
root: {
width: 320,
height: 520,
backgroundColor: 'black',
boxShadow: '0px 0px 20px 0px #aaa'
},
keypad: {
height: 400,
flexDirection: 'row'
},
inputKeys: {
width: 240
},
calculatorKeyText: {
fontFamily: 'Roboto, sans-serif',
fontSize: '1.75em',
fontWeight: '100'
},
functionKeys: {
backgroundImage: 'linear-gradient(to bottom, rgba(202,202,204,1) 0%, rgba(196,194,204,1) 100%)',
flexDirection: 'row'
},
functionKeyText: {
fontFamily: 'Roboto, sans-serif',
fontSize: '1.75em',
fontWeight: '100'
},
digitKeys: {
backgroundColor: '#e0e0e7',
flexDirection: 'row',
flexWrap: 'wrap-reverse'
},
digitKeyText: {
fontFamily: 'Roboto, sans-serif',
fontSize: '2em',
fontWeight: '100'
},
operatorKeys: {
backgroundImage: 'linear-gradient(to bottom, rgba(252,156,23,1) 0%, rgba(247,126,27,1) 100%)'
},
operatorKey: {
borderRightWidth: 0
},
operatorKeyText: {
color: 'white',
fontFamily: 'Roboto, sans-serif',
fontSize: '2.65em',
fontWeight: '100'
},
keyMultiplyText: {
lineHeight: 50
},
key0: {
paddingLeft: 32,
width: 160
},
keyDot: {
overflow: 'hidden'
},
keyDotText: {
fontFamily: 'Roboto, sans-serif',
fontSize: '4.375em',
fontWeight: '100',
marginTop: -10
}
});
export default Calculator;

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import Calculator from './Calculator';
import { StyleSheet, View } from 'react-native';
const styles = StyleSheet.create({
container: { alignItems: 'center', justifyContent: 'center', flex: 1 }
});
storiesOf('Example apps', module).add('Calculator', () =>
<View style={styles.container}>
<Calculator />
</View>
);

View File

@@ -323,4 +323,4 @@ const styles = StyleSheet.create({
AppRegistry.registerComponent('Game2048', () => Game2048);
module.exports = Game2048;
export default Game2048;

View File

@@ -2,4 +2,4 @@ import React from 'react';
import { storiesOf } from '@kadira/storybook';
import Game2048 from './Game2048';
storiesOf('demo: Game2048', module).add('the game', () => <Game2048 />);
storiesOf('Example apps', module).add('Game2048', () => <Game2048 />);

View File

@@ -207,4 +207,4 @@ Board.prototype.hasLost = function() {
return !canMove;
};
module.exports = Board;
export default Board;

View File

@@ -1,4 +1,4 @@
/* eslint-disable react/jsx-no-bind */
/* eslint-disable react/jsx-no-bind, react/prefer-es6-class, react/prop-types */
/**
* The examples provided by Facebook are for non-commercial testing and
@@ -211,17 +211,17 @@ const TicTacToeApp = createReactClass({
},
render() {
const rows = this.state.board.grid.map((cells, row) => (
const rows = this.state.board.grid.map((cells, row) =>
<View key={'row' + row} style={styles.row}>
{cells.map((player, col) => (
{cells.map((player, col) =>
<Cell
key={'cell' + col}
onPress={this.handleCellPress.bind(this, row, col)}
player={player}
/>
))}
)}
</View>
));
);
return (
<View style={styles.container}>
@@ -325,4 +325,4 @@ const styles = StyleSheet.create({
AppRegistry.registerComponent('TicTacToeApp', () => TicTacToeApp);
module.exports = TicTacToeApp;
export default TicTacToeApp;

View File

@@ -2,4 +2,4 @@ import React from 'react';
import { storiesOf } from '@kadira/storybook';
import TicTacToe from './TicTacToe';
storiesOf('demo: TicTacToe', module).add('the game', () => <TicTacToe />);
storiesOf('Example apps', module).add('TicTacToe', () => <TicTacToe />);

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.0.98",
"version": "0.0.107",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
@@ -14,16 +14,16 @@
"compile": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
"docs:build": "build-storybook -o ./docs/dist -c ./docs/storybook/.storybook",
"docs:start": "start-storybook -p 9001 -c ./docs/storybook/.storybook --dont-track",
"docs:publish": "git checkout gh-pages && rm -rf ./storybook && mv docs/dist storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
"docs:publish": "yarn docs:build && git checkout gh-pages && rm -rf ./storybook && mv docs/dist storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
"flow": "flow",
"fmt": "find benchmarks docs src -name '*.js' | grep -v -E '(node_modules|dist)' | xargs npm run fmt:cmd",
"fmt": "find benchmarks docs src -name '*.js' | grep -v -E '(node_modules|dist)' | xargs yarn fmt:cmd",
"fmt:cmd": "prettier --print-width=100 --single-quote --write",
"jest": "jest",
"jest:watch": "npm run test -- --watch",
"lint": "npm run lint:cmd -- benchmarks docs src",
"jest:watch": "yarn test -- --watch",
"lint": "yarn lint:cmd -- benchmarks docs src",
"lint:cmd": "eslint --fix --ignore-path .gitignore",
"precommit": "lint-staged",
"release": "npm run lint && npm run test && npm run compile && npm run build && npm publish",
"release": "yarn lint && yarn test && yarn compile && yarn build && npm publish",
"test": "flow && jest"
},
"babel": {
@@ -57,48 +57,46 @@
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"babel-runtime": "^6.23.0",
"create-react-class": "^15.5.2",
"create-react-class": "^15.6.0",
"debounce": "1.0.2",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.12",
"hyphenate-style-name": "^1.0.2",
"inline-style-prefixer": "^3.0.3",
"inline-style-prefixer": "^3.0.6",
"normalize-css-color": "^1.0.2",
"prop-types": "^15.5.8",
"prop-types": "^15.5.10",
"react-timer-mixin": "^0.13.3"
},
"devDependencies": {
"@kadira/storybook": "^2.5.1",
"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.2",
"babel-preset-react-native": "^1.9.1",
"del-cli": "^0.2.1",
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.6",
"babel-preset-react-native": "^2.0.0",
"del-cli": "^1.0.0",
"enzyme": "^2.8.2",
"enzyme-to-json": "^1.5.1",
"eslint": "^3.19.0",
"eslint-config-prettier": "^1.7.0",
"eslint": "^4.0.0",
"eslint-config-prettier": "^2.2.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^6.10.3",
"file-loader": "^0.11.1",
"flow-bin": "^0.47.0",
"jest": "^19.0.2",
"lint-staged": "^3.4.2",
"node-libs-browser": "^0.5.3",
"prettier": "^1.3.1",
"react": "^15.5.4",
"react-addons-test-utils": "^15.5.1",
"react-dom": "^15.5.4",
"react-test-renderer": "^15.5.4",
"url-loader": "^0.5.8",
"webpack": "^2.5.0",
"webpack-bundle-analyzer": "^2.4.0"
"eslint-plugin-react": "^7.1.0",
"file-loader": "^0.11.2",
"flow-bin": "^0.48.0",
"jest": "^20.0.4",
"lint-staged": "^4.0.0",
"prettier": "^1.4.4",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-test-renderer": "^15.6.1",
"url-loader": "^0.5.9",
"webpack": "^3.0.0",
"webpack-bundle-analyzer": "^2.8.2"
},
"peerDependencies": {
"react": "15.4.x || 15.5.x",
"react-dom": "15.4.x || 15.5.x"
"react": "15.4.x || 15.5.x || 15.6.x",
"react-dom": "15.4.x || 15.5.x || 15.6.x"
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause",

View File

@@ -1,3 +1,14 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Animated
* @noflow
*/
import Animated from 'animated';
import Image from '../../components/Image';
import ScrollView from '../../components/ScrollView';
@@ -7,10 +18,12 @@ import View from '../../components/View';
Animated.inject.FlattenStyle(StyleSheet.flatten);
module.exports = {
const AnimatedImplementation = {
...Animated,
Image: Animated.createAnimatedComponent(Image),
ScrollView: Animated.createAnimatedComponent(ScrollView),
Text: Animated.createAnimatedComponent(Text),
View: Animated.createAnimatedComponent(View)
};
export default AnimatedImplementation;

View File

@@ -1,4 +1,11 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
@@ -20,7 +27,7 @@ type State = {
mainKey: number
};
class AppContainer extends Component {
export default class AppContainer extends Component {
props: Props;
state: State = { mainKey: 1 };
@@ -41,7 +48,7 @@ class AppContainer extends Component {
render() {
return (
<View pointerEvents="box-none" style={styles.appContainer}>
<View pointerEvents="box-none" style={[styles.appContainer, StyleSheet.absoluteFill]}>
<View
children={this.props.children}
key={this.state.mainKey}
@@ -61,5 +68,3 @@ const styles = StyleSheet.create({
flex: 1
}
});
module.exports = AppContainer;

View File

@@ -9,22 +9,22 @@ exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
`;
exports[`apis/AppRegistry/renderApplication getApplication 2`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
"<style id=\\"react-native-stylesheet-static\\">html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto;}
.rn-pointerEvents-12vffkv{pointer-events:none;}
.rn-pointerEvents-12vffkv *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
.rn-bottom-1p0dtai{bottom:0px}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}</style>"
`;
exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
"<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}
@@ -48,9 +48,11 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.rn-boxSizing-deolkf{box-sizing:border-box}
.rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
.rn-display-xoduu5{display:-webkit-inline-box;display:-moz-inline-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}
.rn-flexShrink-1qe8dj5{-webkit-flex-shrink:0;flex-shrink:0}
.rn-flexShrink-1wbh5a2{-webkit-flex-shrink:1;flex-shrink:1}
.rn-flexBasis-1mlwlqe{-webkit-flex-basis:auto;flex-basis:auto}
.rn-flexBasis-1ro0kt6{-webkit-flex-basis:0%;flex-basis:0%}
.rn-flexDirection-eqz5dr{-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;flex-direction:column}
.rn-marginTop-1mnahxq{margin-top:0px}
.rn-marginRight-61z16t{margin-right:0px}
@@ -64,6 +66,5 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
.rn-flexGrow-16y2uox{-webkit-flex-grow:1;flex-grow:1}
</style>"
.rn-flexGrow-16y2uox{-webkit-flex-grow:1;flex-grow:1}</style>"
`;

View File

@@ -2,14 +2,18 @@
import { getApplication } from '../renderApplication';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
const RootComponent = () => <div />;
describe('apis/AppRegistry/renderApplication', () => {
test('getApplication', () => {
const { element, stylesheet } = getApplication(RootComponent, {});
const { element, stylesheets } = getApplication(RootComponent, {});
expect(element).toMatchSnapshot();
expect(stylesheet).toMatchSnapshot();
stylesheets.forEach(sheet => {
const result = ReactDOMServer.renderToStaticMarkup(sheet);
expect(result).toMatchSnapshot();
});
});
});

View File

@@ -3,6 +3,10 @@
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule AppRegistry
* @flow
*/
@@ -24,7 +28,7 @@ export type AppConfig = {
/**
* `AppRegistry` is the JS entry point to running all React Native apps.
*/
class AppRegistry {
export default class AppRegistry {
static getAppKeys(): Array<string> {
return Object.keys(runnables);
}
@@ -90,5 +94,3 @@ class AppRegistry {
unmountComponentAtNode(rootTag);
}
}
module.exports = AppRegistry;

View File

@@ -3,6 +3,9 @@
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
@@ -33,6 +36,9 @@ export function getApplication(RootComponent: ReactClass<Object>, initialProps:
<RootComponent {...initialProps} />
</AppContainer>
);
const stylesheet = StyleSheet.renderToString();
return { element, stylesheet };
const stylesheets = StyleSheet.getStyleSheets().map(sheet =>
// ensure that CSS text is not escaped
<style dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} />
);
return { element, stylesheets };
}

View File

@@ -1,4 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule AppState
* @flow
*/
@@ -16,7 +24,7 @@ const AppStates = {
const listeners = [];
class AppState {
export default class AppState {
static isAvailable = ExecutionEnvironment.canUseDOM && document.visibilityState;
static get currentState() {
@@ -65,5 +73,3 @@ class AppState {
}
}
}
module.exports = AppState;

View File

@@ -3,8 +3,13 @@
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule AsyncStorage
* @flow
*/
import merge from 'deep-assign';
const mergeLocalStorageItem = (key, value) => {
@@ -46,7 +51,7 @@ const createPromiseAll = (promises, callback, processResult) => {
);
};
class AsyncStorage {
export default class AsyncStorage {
/**
* Erases *all* AsyncStorage for the domain.
*/
@@ -147,5 +152,3 @@ class AsyncStorage {
return createPromiseAll(promises, callback);
}
}
module.exports = AsyncStorage;

View File

@@ -1,13 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* web stub for BackAndroid.android.js
* LICENSE file in the root directory of this source tree.
*
* @providesModule BackHandler
* @flow
*/
@@ -23,4 +22,4 @@ const BackAndroid = {
removeEventListener: emptyFunction
};
module.exports = BackAndroid;
export default BackAndroid;

View File

@@ -1,10 +1,17 @@
/* global window */
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Clipboard
* @flow
*/
class Clipboard {
static isSupported() {
export default class Clipboard {
static isAvailable() {
return (
typeof document.queryCommandSupported === 'function' && document.queryCommandSupported('copy')
);
@@ -47,5 +54,3 @@ class Clipboard {
return success;
}
}
module.exports = Clipboard;

View File

@@ -3,6 +3,10 @@
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Dimensions
* @flow
*/
@@ -24,7 +28,7 @@ const win = canUseDOM
const dimensions = {};
class Dimensions {
export default class Dimensions {
static get(dimension: string): Object {
invariant(dimensions[dimension], `No dimension set for key ${dimension}`);
return dimensions[dimension];
@@ -52,5 +56,3 @@ Dimensions.set();
if (canUseDOM) {
window.addEventListener('resize', debounce(Dimensions.set, 16), false);
}
module.exports = Dimensions;

View File

@@ -1,4 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule I18nManager
* @flow
*/
@@ -48,4 +56,4 @@ const I18nManager: I18nManagerStatus = {
}
};
module.exports = I18nManager;
export default I18nManager;

View File

@@ -1,7 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule InteractionManager
* @flow
*/
@@ -38,4 +43,4 @@ const InteractionManager = {
addListener: () => {}
};
module.exports = InteractionManager;
export default InteractionManager;

View File

@@ -1,7 +1,19 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Linking
* @flow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
const initialURL = canUseDOM ? window.location.href : '';
const Linking = {
addEventListener() {},
removeEventListener() {},
@@ -9,7 +21,7 @@ const Linking = {
return Promise.resolve(true);
},
getInitialURL() {
return Promise.resolve('');
return Promise.resolve(initialURL);
},
openURL(url: string) {
try {
@@ -54,4 +66,4 @@ const iframeOpen = url => {
}
};
module.exports = Linking;
export default Linking;

View File

@@ -3,6 +3,10 @@
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule NetInfo
* @flow
*/
@@ -111,4 +115,4 @@ const NetInfo = {
}
};
module.exports = NetInfo;
export default NetInfo;

View File

@@ -1,21 +1,27 @@
/* eslint-disable */
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule PanResponder
* @noflow
*/
'use strict';
import TouchHistoryMath from '../../vendor/TouchHistoryMath';
var TouchHistoryMath = require('../../vendor/TouchHistoryMath');
var currentCentroidXOfTouchesChangedAfter = TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
var currentCentroidYOfTouchesChangedAfter = TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
var previousCentroidXOfTouchesChangedAfter =
const currentCentroidXOfTouchesChangedAfter =
TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
const currentCentroidYOfTouchesChangedAfter =
TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
const previousCentroidXOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
var previousCentroidYOfTouchesChangedAfter =
const previousCentroidYOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
var currentCentroidX = TouchHistoryMath.currentCentroidX;
var currentCentroidY = TouchHistoryMath.currentCentroidY;
const currentCentroidX = TouchHistoryMath.currentCentroidX;
const currentCentroidY = TouchHistoryMath.currentCentroidY;
/**
* `PanResponder` reconciles several touches into a single gesture. It makes
@@ -112,7 +118,7 @@ var currentCentroidY = TouchHistoryMath.currentCentroidY;
* [PanResponder example in UIExplorer](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/PanResponderExample.js)
*/
var PanResponder = {
const PanResponder = {
/**
*
* A graphical explanation of the touch data flow:
@@ -224,16 +230,16 @@ var PanResponder = {
touchHistory,
gestureState._accountsForMovesUpTo
);
var movedAfter = gestureState._accountsForMovesUpTo;
var prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
var x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
var prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
var y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
var nextDX = gestureState.dx + (x - prevX);
var nextDY = gestureState.dy + (y - prevY);
const movedAfter = gestureState._accountsForMovesUpTo;
const prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
const x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
const prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
const y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
const nextDX = gestureState.dx + (x - prevX);
const nextDY = gestureState.dy + (y - prevY);
// TODO: This must be filtered intelligently.
var dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
const dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
gestureState.vx = (nextDX - gestureState.dx) / dt;
gestureState.vy = (nextDY - gestureState.dy) / dt;
@@ -275,12 +281,12 @@ var PanResponder = {
* are the responder.
*/
create: function(config) {
var gestureState = {
const gestureState = {
// Useful for debugging
stateID: Math.random()
};
PanResponder._initializeGestureState(gestureState);
var panHandlers = {
const panHandlers = {
onStartShouldSetResponder: function(e) {
return config.onStartShouldSetPanResponder === undefined
? false
@@ -311,7 +317,7 @@ var PanResponder = {
},
onMoveShouldSetResponderCapture: function(e) {
var touchHistory = e.touchHistory;
const touchHistory = e.touchHistory;
// Responder system incorrectly dispatches should* to current responder
// Filter out any touch moves past the first one - we would have
// already processed multi-touch geometry during the first event.
@@ -346,13 +352,13 @@ var PanResponder = {
},
onResponderStart: function(e) {
var touchHistory = e.touchHistory;
const touchHistory = e.touchHistory;
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
config.onPanResponderStart && config.onPanResponderStart(e, gestureState);
},
onResponderMove: function(e) {
var touchHistory = e.touchHistory;
const touchHistory = e.touchHistory;
// Guard against the dispatch of two touch moves when there are two
// simultaneously changed touches.
if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
@@ -365,7 +371,7 @@ var PanResponder = {
},
onResponderEnd: function(e) {
var touchHistory = e.touchHistory;
const touchHistory = e.touchHistory;
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
config.onPanResponderEnd && config.onPanResponderEnd(e, gestureState);
},
@@ -385,4 +391,4 @@ var PanResponder = {
}
};
module.exports = PanResponder;
export default PanResponder;

View File

@@ -3,6 +3,10 @@
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule PixelRatio
* @flow
*/
@@ -11,7 +15,7 @@ import Dimensions from '../Dimensions';
/**
* PixelRatio gives access to the device pixel density.
*/
class PixelRatio {
export default class PixelRatio {
/**
* Returns the device pixel density.
*/
@@ -45,5 +49,3 @@ class PixelRatio {
return Math.round(layoutSize * ratio) / ratio;
}
}
module.exports = PixelRatio;

View File

@@ -1,4 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Platform
* @flow
*/
@@ -7,4 +15,4 @@ const Platform = {
select: (obj: Object) => ('web' in obj ? obj.web : obj.default)
};
module.exports = Platform;
export default Platform;

View File

@@ -1,7 +1,16 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import generateCss from './generateCss';
import hash from './hash';
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
import hash from '../../vendor/hash';
import staticCss from './staticCss';
const emptyObject = {};
@@ -25,15 +34,19 @@ const pointerEvents = {
none: createClassName('pointerEvents', 'none')
};
// See #513
const pointerEventsCss =
`.${pointerEvents.auto}{pointer-events:auto;}\n` +
`.${pointerEvents.boxNone}{pointer-events:none;}\n` +
`.${pointerEvents.boxNone} *{pointer-events:auto;}\n` +
`.${pointerEvents.boxOnly}{pointer-events:auto;}\n` +
`.${pointerEvents.boxOnly} *{pointer-events:none;}\n` +
`.${pointerEvents.none}{pointer-events:none;}`;
`.${pointerEvents.auto}{pointer-events:auto !important;}\n` +
`.${pointerEvents.boxOnly}{pointer-events:auto !important;}\n` +
`.${pointerEvents.none}{pointer-events:none !important;}\n` +
`.${pointerEvents.boxNone}{pointer-events:none !important;}\n` +
`.${pointerEvents.boxNone} > *{pointer-events:auto;}\n` +
`.${pointerEvents.boxOnly} > *{pointer-events:none;}`;
export default class StyleManager {
cache = null;
mainSheet = null;
class StyleManager {
constructor() {
// custom pointer event values are implemented using descendent selectors,
// so we manually create the CSS and pre-register the declarations
@@ -84,6 +97,15 @@ class StyleManager {
}
getStyleSheetHtml() {
const styleSheets = this.getStyleSheets();
return styleSheets
.map(sheet => {
return `<style id="${sheet.id}">\n${sheet.textContent}\n</style>`;
})
.join('\n');
}
getStyleSheets() {
const cache = this.cache.byProp;
const mainSheetTextContext = Object.keys(cache)
@@ -98,9 +120,16 @@ class StyleManager {
}, [])
.join('\n');
const staticSheet = `<style id="react-native-stylesheet-static">\n${staticCss}\n${pointerEventsCss}\n</style>`;
const mainSheet = `<style id="${STYLE_ELEMENT_ID}">\n${mainSheetTextContext}\n</style>`;
return `${staticSheet}\n${mainSheet}`;
return [
{
id: 'react-native-stylesheet-static',
textContent: `${staticCss}\n${pointerEventsCss}`
},
{
id: STYLE_ELEMENT_ID,
textContent: `${mainSheetTextContext}`
}
];
}
setDeclaration(prop, value) {
@@ -109,14 +138,12 @@ class StyleManager {
className = createClassName(prop, value);
this._addToCache(className, prop, value);
if (canUseDOM) {
requestAnimationFrame(() => {
const sheet = this.mainSheet.sheet;
// avoid injecting if the rule already exists (e.g., server rendered, hot reload)
if (this.mainSheet.textContent.indexOf(className) === -1) {
const rule = createCssRule(className, prop, value);
sheet.insertRule(rule, sheet.cssRules.length);
}
});
const sheet = this.mainSheet.sheet;
// avoid injecting if the rule already exists (e.g., server rendered, hot reload)
if (this.mainSheet.textContent.indexOf(className) === -1) {
const rule = createCssRule(className, prop, value);
sheet.insertRule(rule, sheet.cssRules.length);
}
}
}
return className;
@@ -131,5 +158,3 @@ class StyleManager {
cache.byClassName[className] = { prop, value };
}
}
module.exports = StyleManager;

View File

@@ -1,7 +1,16 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* WARNING: changes to this file in particular can cause significant changes to
* the results of render performance benchmarks.
*
* @noflow
*/
import createReactDOMStyle from './createReactDOMStyle';
import flattenArray from '../../modules/flattenArray';
import flattenStyle from './flattenStyle';
@@ -20,14 +29,12 @@ const createCacheKey = id => {
const classListToString = list => list.join(' ').trim();
class StyleRegistry {
constructor() {
this.cache = { ltr: {}, rtl: {} };
this.styleManager = new StyleManager();
}
export default class StyleRegistry {
cache = { ltr: {}, rtl: {} };
styleManager = new StyleManager();
getStyleSheetHtml() {
return this.styleManager.getStyleSheetHtml();
getStyleSheets() {
return this.styleManager.getStyleSheets();
}
/**
@@ -189,5 +196,3 @@ class StyleRegistry {
return this._resolveStyle(style, options);
}
}
module.exports = StyleRegistry;

View File

@@ -1,10 +1,10 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* LICENSE file in the root directory of this source tree.
*
* @providesModule StyleSheetValidation
* @flow
@@ -23,7 +23,7 @@ import { oneOf, string } from 'prop-types';
// plz don't fire me.
const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
class StyleSheetValidation {
export default class StyleSheetValidation {
static validateStyleProp(prop, style, caller) {
if (process.env.NODE_ENV !== 'production') {
if (allStylePropTypes[prop] === undefined) {
@@ -89,5 +89,3 @@ StyleSheetValidation.addValidStylePropTypes({
listStyle: string,
pointerEvents: string
});
module.exports = StyleSheetValidation;

View File

@@ -10,12 +10,12 @@ button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto;}
.rn-pointerEvents-12vffkv{pointer-events:none;}
.rn-pointerEvents-12vffkv *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
@@ -30,12 +30,12 @@ button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto;}
.rn-pointerEvents-12vffkv{pointer-events:none;}
.rn-pointerEvents-12vffkv *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
.rn-width-b8lwoo{width:100px}

View File

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

View File

@@ -22,36 +22,63 @@ describe('apis/StyleSheet/createReactDOMStyle', () => {
expect(firstStyle).toEqual(secondStyle);
});
test('flex', () => {
expect(createReactDOMStyle({ display: 'flex' })).toEqual({
display: 'flex',
flexShrink: 0
describe('flexbox styles', () => {
test('flex defaults', () => {
expect(createReactDOMStyle({ display: 'flex' })).toEqual({
display: 'flex',
flexShrink: 0,
flexBasis: 'auto'
});
});
expect(createReactDOMStyle({ display: 'flex', flex: 1 })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 1,
flexBasis: 'auto'
test('flex property expansion', () => {
expect(createReactDOMStyle({ display: 'flex', flex: 1 })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 1,
flexBasis: '0%'
});
expect(createReactDOMStyle({ display: 'flex', flex: 10 })).toEqual({
display: 'flex',
flexGrow: 10,
flexShrink: 1,
flexBasis: '0%'
});
});
expect(createReactDOMStyle({ display: 'flex', flex: 10 })).toEqual({
display: 'flex',
flexGrow: 10,
flexShrink: 1,
flexBasis: 'auto'
test('flexBasis', () => {
// is flex-basis applied?
expect(createReactDOMStyle({ display: 'flex', flexBasis: '25%' })).toEqual({
display: 'flex',
flexShrink: 0,
flexBasis: '25%'
});
// can flex-basis override the 'flex' expansion?
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexBasis: '25%' })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 1,
flexBasis: '25%'
});
});
expect(createReactDOMStyle({ display: 'flex', flexShrink: 1 })).toEqual({
display: 'flex',
flexShrink: 1
});
test('flexShrink overrides', () => {
// is flex-shrink applied?
expect(createReactDOMStyle({ display: 'flex', flexShrink: 1 })).toEqual({
display: 'flex',
flexShrink: 1,
flexBasis: 'auto'
});
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexShrink: 2 })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 2,
flexBasis: 'auto'
// can flex-shrink override the 'flex' expansion?
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexShrink: 2 })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 2,
flexBasis: '0%'
});
});
});

View File

@@ -37,7 +37,7 @@ describe('apis/StyleSheet', () => {
expect(Number.isInteger(StyleSheet.hairlineWidth) === true).toBeTruthy();
});
test('renderToString', () => {
expect(StyleSheet.renderToString()).toMatchSnapshot();
test('getStyleSheets', () => {
expect(StyleSheet.getStyleSheets()).toMatchSnapshot();
});
});

View File

@@ -1,12 +1,20 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* The browser implements the CSS cascade, where the order of properties is a
* factor in determining which styles to paint. React Native is different in
* giving precedence to the more specific styles. For example, the value of
* `paddingTop` takes precedence over that of `padding`.
* factor in determining which styles to paint. React Native is different. It
* gives giving precedence to the more specific style property. For example,
* the value of `paddingTop` takes precedence over that of `padding`.
*
* This module creates mutally exclusive style declarations by expanding all of
* React Native's supported shortform properties (e.g. `padding`) to their
* longfrom equivalents.
*
* @noflow
*/
import normalizeValue from './normalizeValue';
@@ -138,9 +146,14 @@ const createReducer = (style, styleProps) => {
switch (prop) {
case 'display': {
resolvedStyle.display = value;
// default of 'flexShrink:0' has lowest precedence
if (style.display === 'flex' && style.flex == null && style.flexShrink == null) {
resolvedStyle.flexShrink = 0;
// defaults of 'flexBasis:auto' and 'flexShrink:0' have lowest precedence
if (style.display === 'flex' && style.flex == null) {
if (style.flexShrink == null) {
resolvedStyle.flexShrink = 0;
}
if (style.flexBasis == null) {
resolvedStyle.flexBasis = 'auto';
}
}
break;
}
@@ -155,7 +168,7 @@ const createReducer = (style, styleProps) => {
case 'flex': {
resolvedStyle.flexGrow = value;
resolvedStyle.flexShrink = 1;
resolvedStyle.flexBasis = 'auto';
resolvedStyle.flexBasis = '0%';
break;
}
case 'shadowColor':
@@ -223,4 +236,4 @@ const createReactDOMStyle = style => {
return resolvedStyle;
};
module.exports = createReactDOMStyle;
export default createReactDOMStyle;

View File

@@ -1,14 +1,15 @@
/**
* Copyright (c) 2015-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* LICENSE file in the root directory of this source tree.
*
* @providesModule flattenStyle
* @flow
*/
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
import invariant from 'fbjs/lib/invariant';
@@ -48,4 +49,4 @@ function flattenStyle(style: ?StyleObj): ?Object {
return result;
}
module.exports = flattenStyle;
export default flattenStyle;

View File

@@ -1,3 +1,13 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import hyphenateStyleName from 'hyphenate-style-name';
import mapKeyValue from '../../modules/mapKeyValue';
import normalizeValue from './normalizeValue';
@@ -21,4 +31,4 @@ const createDeclarationString = (prop, val) => {
const generateCss = style =>
mapKeyValue(prefixStyles(style), createDeclarationString).sort().join(';');
module.exports = generateCss;
export default generateCss;

View File

@@ -1,3 +1,13 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import I18nManager from '../I18nManager';
import multiplyStyleLengthValue from '../../modules/multiplyStyleLengthValue';
@@ -78,4 +88,4 @@ const i18nStyle = originalStyle => {
return nextStyle;
};
module.exports = i18nStyle;
export default i18nStyle;

View File

@@ -1,6 +1,25 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule StyleSheet
* @noflow
*/
import flattenStyle from './flattenStyle';
import StyleRegistry from './registry';
// allow component styles to be editable in React Dev Tools
if (process.env.NODE_ENV !== 'production') {
const { canUseDOM } = require('fbjs/lib/ExecutionEnvironment');
if (canUseDOM && window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.resolveRNStyle = flattenStyle;
}
}
const absoluteFillObject = {
position: 'absolute',
left: 0,
@@ -10,22 +29,25 @@ const absoluteFillObject = {
};
const absoluteFill = StyleRegistry.register(absoluteFillObject);
module.exports = {
const StyleSheet = {
absoluteFill,
absoluteFillObject,
create(styles) {
const result = {};
Object.keys(styles).forEach(key => {
if (process.env.NODE_ENV !== 'production') {
require('./StyleSheetValidation').validateStyle(key, styles);
const StyleSheetValidation = require('./StyleSheetValidation').default;
StyleSheetValidation.validateStyle(key, styles);
}
result[key] = StyleRegistry.register(styles[key]);
});
return result;
},
hairlineWidth: 1,
flatten: flattenStyle,
renderToString() {
return StyleRegistry.getStyleSheetHtml();
}
getStyleSheets() {
return StyleRegistry.getStyleSheets();
},
hairlineWidth: 1
};
export default StyleSheet;

View File

@@ -1,10 +1,20 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import unitlessNumbers from '../../modules/unitlessNumbers';
const normalizeValue = (property, value) => {
const normalizeValue = (property: string, value) => {
if (!unitlessNumbers[property] && typeof value === 'number') {
value = `${value}px`;
}
return value;
};
module.exports = normalizeValue;
export default normalizeValue;

View File

@@ -1,3 +1,13 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import StyleRegistry from './StyleRegistry';
const registry = new StyleRegistry();
module.exports = registry;
export default registry;

View File

@@ -1,5 +1,14 @@
module.exports =
'html{' + // css reset
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
export default 'html{' + // css reset
'font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;' +
'-webkit-tap-highlight-color:rgba(0,0,0,0);' +
'}\n' +

View File

@@ -1,3 +1,14 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule UIManager
* @noflow
*/
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
import setImmediate from 'fbjs/lib/setImmediate';
import setValueForStyles from '../../vendor/setValueForStyles';
@@ -108,4 +119,4 @@ const UIManager = {
}
};
module.exports = UIManager;
export default UIManager;

View File

@@ -1,4 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Vibration
* @flow
*/
@@ -23,4 +31,4 @@ const Vibration = {
}
};
module.exports = Vibration;
export default Vibration;

View File

@@ -1,4 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ActivityIndicator
* @flow
*/
@@ -110,4 +118,4 @@ const indicatorSizes = StyleSheet.create({
}
});
module.exports = applyNativeMethods(ActivityIndicator);
export default applyNativeMethods(ActivityIndicator);

View File

@@ -1,4 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Button
* @flow
*/
@@ -62,4 +70,4 @@ const styles = StyleSheet.create({
}
});
module.exports = Button;
export default Button;

View File

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

View File

@@ -11,4 +11,4 @@ const ImageResizeMode = {
stretch: 'stretch'
};
module.exports = ImageResizeMode;
export default ImageResizeMode;

View File

@@ -10,7 +10,7 @@ import ShadowPropTypes from '../../propTypes/ShadowPropTypes';
import TransformPropTypes from '../../propTypes/TransformPropTypes';
import { number, oneOf, string } from 'prop-types';
module.exports = {
const ImageStylePropTypes = {
...BorderPropTypes,
...LayoutPropTypes,
...ShadowPropTypes,
@@ -28,3 +28,5 @@ module.exports = {
*/
boxShadow: string
};
export default ImageStylePropTypes;

View File

@@ -2,13 +2,15 @@
* @flow
*/
class ImageUriCache {
const dataUriPattern = /^data:/;
export default class ImageUriCache {
static _maximumEntries: number = 256;
static _entries = {};
static has(uri: string) {
const entries = ImageUriCache._entries;
const isDataUri = /^data:/.test(uri);
const isDataUri = dataUriPattern.test(uri);
return isDataUri || Boolean(entries[uri]);
}
@@ -61,5 +63,3 @@ class ImageUriCache {
}
}
}
module.exports = ImageUriCache;

View File

@@ -1,10 +1,12 @@
/* eslint-env jasmine, jest */
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import Image from '../';
import ImageUriCache from '../ImageUriCache';
import React from 'react';
import { mount, render } from 'enzyme';
const originalCanUseDOM = ExecutionEnvironment.canUseDOM;
const originalImage = window.Image;
describe('components/Image', () => {
@@ -113,6 +115,15 @@ describe('components/Image', () => {
ImageUriCache.remove(uriTwo);
expect(component.render().find('img').attr('src')).toBe(uriTwo);
});
test('is set immediately when rendered on the server', () => {
ExecutionEnvironment.canUseDOM = false;
const uri = 'https://google.com/favicon.ico';
const component = render(<Image source={{ uri }} />);
expect(component.find('img').attr('src')).toBe(uri);
expect(ImageUriCache.has(uri)).toBe(true);
ExecutionEnvironment.canUseDOM = originalCanUseDOM;
});
});
describe('prop "style"', () => {

View File

@@ -1,9 +1,17 @@
/* global window */
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Image
* @flow
*/
import applyNativeMethods from '../../modules/applyNativeMethods';
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import createDOMElement from '../../modules/createDOMElement';
import ImageLoader from '../../modules/ImageLoader';
import ImageResizeMode from './ImageResizeMode';
@@ -14,7 +22,7 @@ import StyleSheet from '../../apis/StyleSheet';
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
import View from '../View';
import ViewPropTypes from '../View/ViewPropTypes';
import { any, func, number, oneOf, oneOfType, shape, string } from 'prop-types';
import { any, bool, func, number, oneOf, oneOfType, shape, string } from 'prop-types';
import React, { Component } from 'react';
const emptyObject = {};
@@ -34,8 +42,8 @@ const ImageSourcePropType = oneOfType([
string
]);
const getImageState = (uri, isPreviouslyLoaded) => {
return isPreviouslyLoaded ? STATUS_LOADED : uri ? STATUS_PENDING : STATUS_IDLE;
const getImageState = (uri, shouldDisplaySource) => {
return shouldDisplaySource ? STATUS_LOADED : uri ? STATUS_PENDING : STATUS_IDLE;
};
const resolveAssetDimensions = source => {
@@ -45,8 +53,17 @@ const resolveAssetDimensions = source => {
}
};
const svgDataUriPattern = /^data:image\/svg\+xml;/;
const resolveAssetSource = source => {
return (typeof source === 'object' ? source.uri : source) || '';
const uri = typeof source === 'object' ? source.uri : source || '';
// SVG data may contain characters (e.g., #, ") that need to be escaped
if (svgDataUriPattern.test(uri)) {
const parts = uri.split('<svg');
const [prefix, ...svgFragment] = parts;
const svg = encodeURIComponent(`<svg${svgFragment}`);
return `${prefix}${svg}`;
}
return uri;
};
class Image extends Component {
@@ -54,6 +71,10 @@ class Image extends Component {
static displayName = 'Image';
static contextTypes = {
isInAParentText: bool
};
static propTypes = {
...ViewPropTypes,
children: any,
@@ -91,10 +112,10 @@ class Image extends Component {
super(props, context);
// If an image has been loaded before, render it immediately
const uri = resolveAssetSource(props.source);
const isPreviouslyLoaded = ImageUriCache.has(uri);
this.state = { shouldDisplaySource: isPreviouslyLoaded };
this._imageState = getImageState(uri, isPreviouslyLoaded);
isPreviouslyLoaded && ImageUriCache.add(uri);
const shouldDisplaySource = ImageUriCache.has(uri) || !canUseDOM;
this.state = { shouldDisplaySource };
this._imageState = getImageState(uri, shouldDisplaySource);
shouldDisplaySource && ImageUriCache.add(uri);
}
componentDidMount() {
@@ -158,6 +179,7 @@ class Image extends Component {
imageSizeStyle,
originalStyle,
resizeModeStyles[finalResizeMode],
this.context.isInAParentText && styles.inline,
backgroundImage && { backgroundImage }
]);
// View doesn't support 'resizeMode' as a style
@@ -267,6 +289,9 @@ const styles = StyleSheet.create({
backgroundSize: 'cover',
zIndex: 0
},
inline: {
display: 'inline-flex'
},
img: {
height: '100%',
opacity: 0,
@@ -298,4 +323,4 @@ const resizeModeStyles = StyleSheet.create({
}
});
module.exports = applyNativeMethods(Image);
export default applyNativeMethods(Image);

View File

@@ -27,11 +27,10 @@
* @typechecks
* @flow
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
var isEmpty = require('fbjs/lib/isEmpty');
var warning = require('fbjs/lib/warning');
import invariant from 'fbjs/lib/invariant';
import isEmpty from 'fbjs/lib/isEmpty';
import warning from 'fbjs/lib/warning';
function defaultGetRowData(dataBlob: any, sectionID: number | string, rowID: number | string): any {
return dataBlob[sectionID][rowID];
@@ -392,4 +391,4 @@ function keyedDictionaryFromArray(arr) {
return result;
}
module.exports = ListViewDataSource;
export default ListViewDataSource;

View File

@@ -441,4 +441,4 @@ class ListView extends Component {
};
}
module.exports = applyNativeMethods(ListView);
export default applyNativeMethods(ListView);

View File

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

View File

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

View File

@@ -1,4 +1,11 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ProgressBar
* @flow
*/
@@ -11,7 +18,7 @@ import React, { Component } from 'react';
import { bool, number } from 'prop-types';
class ProgressBar extends Component {
_progressElement = null;
_progressElement: View;
static displayName = 'ProgressBar';
@@ -80,10 +87,12 @@ const styles = StyleSheet.create({
track: {
height: 5,
overflow: 'hidden',
userSelect: 'none'
userSelect: 'none',
zIndex: 0
},
progress: {
height: '100%'
height: '100%',
zIndex: -1
},
animation: {
animationDuration: '1s',
@@ -93,4 +102,4 @@ const styles = StyleSheet.create({
}
});
module.exports = applyNativeMethods(ProgressBar);
export default applyNativeMethods(ProgressBar);

View File

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

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