Compare commits

..

1 Commits

Author SHA1 Message Date
Nicolas Gallagher
a6f8c111af Preact benchmarks 2017-04-22 11:45:56 -07:00
463 changed files with 12499 additions and 23812 deletions

8
.babelrc Normal file
View File

@@ -0,0 +1,8 @@
{
"presets": [
"react-native"
],
"plugins": [
[ "transform-react-remove-prop-types", { "mode": "wrap" } ]
]
}

9
.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
# EditorConfig: http://editorconfig.org
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2

View File

@@ -24,16 +24,7 @@
"globals": {
"document": false,
"navigator": false,
"window": false,
// Flow global types
"HTMLInputElement": false,
"ReactClass": false,
"ReactComponent": false,
"ReactElement": false,
"ReactPropsChainableTypeChecker": false,
"ReactPropsCheckType": false,
"ReactPropTypes": false,
"SyntheticEvent": false
"window": false
},
"rules": {
"camelcase": 0,
@@ -56,6 +47,7 @@
"no-dupe-class-members": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-duplicate-imports": 2,
"no-empty-character-class": 2,
"no-empty-pattern": 2,
"no-eval": 2,
@@ -128,6 +120,9 @@
// react
"react/display-name": 0,
"react/jsx-handler-names": [2, {
"eventHandlerPrefix": "_handle"
}],
"react/jsx-no-bind": 2,
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-undef": 2,

View File

@@ -1,14 +0,0 @@
[ignore]
.*/__tests__/.*
.*/benchmarks/.*
.*/docs/.*
.*/node_modules/animated/*
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
[include]
[libs]
types
[options]
unsafe.enable_getters_and_setters=true

View File

@@ -1,125 +0,0 @@
# Contributing
## Reporting Issues and Asking Questions
Before opening an issue, please search the [issue
tracker](https://github.com/necolas/react-native-web/issues) to make sure your
issue hasn't already been reported.
## Getting started
Visit the [Issue tracker](https://github.com/necolas/react-native-web/issues)
to find a list of open issues that need attention.
Fork, then clone the repo:
```
git clone https://github.com/your-username/react-native-web.git
```
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install):
```
yarn
```
## Automated tests
To run flow:
```
yarn flow
```
To run the unit tests:
```
yarn jest
```
…in watch mode:
```
yarn jest:watch
```
To run all automated tests:
```
yarn test
```
## Visual tests
To run the interactive storybook:
```
yarn docs:start
```
To generate a static build of the storybook:
```
yarn docs:build
```
To run the performance benchmarks in a browser (opening `./benchmarks/index.html`):
```
yarn benchmark
```
## Compile and build
To compile the source code to `dist`:
```
yarn compile
```
To create a UMD bundle of the library:
```
yarn build
```
### Pre-commit
To format and lint code before commit:
```
yarn precommit
```
To format and lint the entire project:
```
yarn fmt
yarn lint
```
### New Features
Please open an issue with a proposal for a new feature or refactoring before
starting on the work. We don't want you to waste your efforts on a pull request
that we won't want to accept.
## Pull requests
**Before submitting a pull request,** please make sure the following is done:
1. Fork the repository and create your branch from `master`.
2. If you've added code that should be tested, add tests!
3. If you've changed APIs, update the documentation.
4. Ensure the tests pass (`yarn test`).
5. Lint and format your code (`yarn fmt && yarn lint`).
You can now submit a pull request, referencing any issues it addresses.
Please try to keep your pull request focused in scope and avoid including
unrelated commits.
After you have submitted your pull request, we'll try to get back to you as
soon as possible. We may suggest some changes or improvements.
Thank you for contributing!

View File

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

View File

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

1
.gitignore vendored
View File

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

View File

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

88
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,88 @@
# Contributing
## Reporting Issues and Asking Questions
Before opening an issue, please search the [issue
tracker](https://github.com/necolas/react-native-web/issues) to make sure your
issue hasn't already been reported.
## Development
Visit the [Issue tracker](https://github.com/necolas/react-native-web/issues)
to find a list of open issues that need attention.
Fork, then clone the repo:
```
git clone https://github.com/your-username/react-native-web.git
```
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install):
```
yarn
```
Run the examples:
```
npm run examples
```
Run the benchmarks in a browser by opening `./performance/index.html` after running:
```
npm run build:performance
```
### Building
```
npm run build
```
To create a UMD build:
```
npm run build:umd
```
### Testing and Linting
To run the tests:
```
npm run test
```
To continuously watch and run tests, run the following:
```
npm run test:watch
```
To perform only linting, run the following:
```
npm run lint
```
### New Features
Please open an issue with a proposal for a new feature or refactoring before
starting on the work. We don't want you to waste your efforts on a pull request
that we won't want to accept.
## Submitting Changes
* Open a new issue in the [Issue tracker](https://github.com/necolas/react-native-web/issues).
* Fork the repo.
* Create a new feature branch based off the `master` branch.
* Make sure all tests pass and there are no linting errors.
* Submit a pull request, referencing any issues it addresses.
Please try to keep your pull request focused in scope and avoid including unrelated commits.
After you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or improvements.
Thank you for contributing!

131
README.md
View File

@@ -3,66 +3,101 @@
[![Build Status][travis-image]][travis-url]
[![npm version][npm-image]][npm-url]
"React Native for Web" brings the platform-agnostic Components and APIs of
[React Native][react-native-url] to the Web.
[React Native][react-native-url] components and APIs for the Web.
Browse the [interactive
documentation](https://necolas.github.io/react-native-web/storybook/) or [try
it out](https://glitch.com/edit/#!/react-native-web-playground) on Glitch.
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
## Features
[npm-image]: https://badge.fury.io/js/react-native-web.svg
[npm-url]: https://npmjs.org/package/react-native-web
[react-native-url]: https://facebook.github.io/react-native/
[travis-image]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[travis-url]: https://travis-ci.org/necolas/react-native-web
* Interoperability with ReactDOM components.
* Native-like touch handling.
* Built-in integration with web accessibility APIs.
* Built-in support for LTR and RTL layouts.
* Built-in expressive and reliable subset of CSS.
* Optimized, vendor-prefixed CSS with [good runtime performance](benchmarks/README.md).
* Server-side rendering of HTML and critical CSS.
* 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).
Browse the UI Explorer to see React Native [examples running on
Web](https://necolas.github.io/react-native-web/storybook/). Or try it out
online with [React Native for Web: Playground](https://www.webpackbin.com/bins/-KgucwxRbn7HRU-V-3Bc).
## Quick start
Install in your existing app using `yarn` or `npm`:
To install in your app:
```
yarn add react react-dom react-native-web
npm install --save react@15.4 react-dom@15.4 react-native-web
```
Add the `react-native-web/babel` plugin to your Babel configuration. This will
alias `react-native` to `react-native-web` and exclude any modules not required
by the app.
Read the [Getting Started](docs/guides/getting-started.md) guide.
```json
{
"plugins": [
"react-native-web/babel"
],
"presets": [
"react-native"
]
}
```
(For React/ReactDOM 15.4 15.6 support, install `react-native-web@<0.1.0`)
See the [Getting Started](docs/guides/getting-started.md) guide for more details.
Alternatively, you can quickly setup a local project
using [create-react-app](https://github.com/facebookincubator/create-react-app)
(which supports `react-native-web` out-of-the-box once installed) and
[react-native-web-starter](https://github.com/grabcode/react-native-web-starter).
## Documentation
The [interactive
documentation](https://necolas.github.io/react-native-web/storybook/) shows all
the supported APIs and Components.
Guides:
* [Getting started](docs/guides/getting-started.md)
* [Style](docs/guides/style.md)
* [Accessibility](docs/guides/accessibility.md)
* [Direct manipulation](docs/guides/direct-manipulation.md)
* [Internationalization](docs/guides/internationalization.md)
* [Advanced use](docs/guides/advanced.md)
* [Known issues](docs/guides/known-issues.md)
* [Style](docs/guides/style.md)
Exported modules:
* Components
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
* [`Button`](docs/components/Button.md)
* [`Image`](docs/components/Image.md)
* [`ListView`](docs/components/ListView.md)
* [`ProgressBar`](docs/components/ProgressBar.md)
* [`ScrollView`](docs/components/ScrollView.md)
* [`Switch`](docs/components/Switch.md)
* [`Text`](docs/components/Text.md)
* [`TextInput`](docs/components/TextInput.md)
* [`TouchableHighlight`](http://facebook.github.io/react-native/releases/0.22/docs/touchablehighlight.html) (mirrors React Native)
* [`TouchableOpacity`](http://facebook.github.io/react-native/releases/0.22/docs/touchableopacity.html) (mirrors React Native)
* [`TouchableWithoutFeedback`](docs/components/TouchableWithoutFeedback.md)
* [`View`](docs/components/View.md)
* APIs
* [`Animated`](http://facebook.github.io/react-native/releases/0.20/docs/animated.html) (mirrors React Native)
* [`AppRegistry`](docs/apis/AppRegistry.md)
* [`AppState`](docs/apis/AppState.md)
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
* [`Clipboard`](docs/apis/Clipboard.md)
* [`Dimensions`](docs/apis/Dimensions.md)
* [`I18nManager`](docs/apis/I18nManager.md)
* [`NativeMethods`](docs/apis/NativeMethods.md)
* [`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)
* [`Platform`](docs/apis/Platform.md)
* [`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: a common way to handle style,
animation, touch, viewport adaptation, accessibility, themes, RTL layout, and
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 facing Web teams.
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
@@ -103,25 +138,13 @@ AppRegistry.registerComponent('MyApp', () => App)
AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-root') })
```
## Starter kits
* [Glitch](https://glitch.com/edit/#!/react-native-web-playground)
* [create-react-app](https://github.com/facebookincubator/create-react-app)
* [re-start](https://github.com/react-everywhere/re-start)
## Related projects
* [react-primitives](https://github.com/lelandrichardson/react-primitives/)
* [react-sketchapp](https://github.com/airbnb/react-sketchapp)
* [reactxp](https://github.com/microsoft/reactxp)
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
* [reactxp](https://github.com/microsoft/reactxp)
* [react-web](https://github.com/taobaofed/react-web)
## License
React Native for Web is [BSD licensed](LICENSE).
[npm-image]: https://badge.fury.io/js/react-native-web.svg
[npm-url]: https://npmjs.org/package/react-native-web
[react-native-url]: https://facebook.github.io/react-native/
[travis-image]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[travis-url]: https://travis-ci.org/necolas/react-native-web

View File

@@ -1,161 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`1. Rewrite react-native paths for react-native-web 1`] = `
"
import { View } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import View from 'react-native-web/dist/components/View';
"
`;
exports[`2. Rewrite react-native paths for react-native-web 1`] = `
"
import { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import Switch from 'react-native-web/dist/components/Switch';
import Text from 'react-native-web/dist/components/Text';
import MyView from 'react-native-web/dist/components/View';
import ViewPropTypes from 'react-native-web/dist/components/View/ViewPropTypes';
"
`;
exports[`3. Rewrite react-native paths for react-native-web 1`] = `
"
import { createElement, Switch, StyleSheet } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import createElement from 'react-native-web/dist/modules/createElement';
import Switch from 'react-native-web/dist/components/Switch';
import StyleSheet from 'react-native-web/dist/apis/StyleSheet';
"
`;
exports[`4. Rewrite react-native paths for react-native-web 1`] = `
"
import { InvalidThing, TouchableOpacity } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import { InvalidThing } from 'react-native-web';
import TouchableOpacity from 'react-native-web/dist/components/Touchable/TouchableOpacity';
"
`;
exports[`5. Rewrite react-native paths for react-native-web 1`] = `
"
import * as RNW from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import * as RNW from 'react-native-web';
"
`;
exports[`6. Rewrite react-native paths for react-native-web 1`] = `
"
const { View } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const View = require('react-native-web/dist/components/View');
"
`;
exports[`7. Rewrite react-native paths for react-native-web 1`] = `
"
let { Switch, Text, View: MyView } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
let Switch = require('react-native-web/dist/components/Switch');
let Text = require('react-native-web/dist/components/Text');
let MyView = require('react-native-web/dist/components/View');
"
`;
exports[`8. Rewrite react-native paths for react-native-web 1`] = `
"
var { createElement, Switch, StyleSheet } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
var createElement = require('react-native-web/dist/modules/createElement');
var Switch = require('react-native-web/dist/components/Switch');
var StyleSheet = require('react-native-web/dist/apis/StyleSheet');
"
`;
exports[`9. Rewrite react-native paths for react-native-web 1`] = `
"
const { InvalidThing, TouchableOpacity } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const TouchableOpacity = require('react-native-web/dist/components/Touchable/TouchableOpacity');
"
`;
exports[`10. Rewrite react-native paths for react-native-web 1`] = `
"
export { View } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as View } from 'react-native-web/dist/components/View';
"
`;
exports[`11. Rewrite react-native paths for react-native-web 1`] = `
"
export { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as Switch } from 'react-native-web/dist/components/Switch';
export { default as Text } from 'react-native-web/dist/components/Text';
export { default as MyView } from 'react-native-web/dist/components/View';
export { default as ViewPropTypes } from 'react-native-web/dist/components/View/ViewPropTypes';
"
`;
exports[`12. Rewrite react-native paths for react-native-web 1`] = `
"
export { createElement, Switch, StyleSheet } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as createElement } from 'react-native-web/dist/modules/createElement';
export { default as Switch } from 'react-native-web/dist/components/Switch';
export { default as StyleSheet } from 'react-native-web/dist/apis/StyleSheet';
"
`;
exports[`13. Rewrite react-native paths for react-native-web 1`] = `
"
export { InvalidThing, TouchableOpacity } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { InvalidThing } from 'react-native-web';
export { default as TouchableOpacity } from 'react-native-web/dist/components/Touchable/TouchableOpacity';
"
`;
exports[`14. Rewrite react-native paths for react-native-web 1`] = `
"
export { default as RNW } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as RNW } from 'react-native-web';
"
`;

View File

@@ -1,46 +0,0 @@
const plugin = require('..');
const pluginTester = require('babel-plugin-tester');
pluginTester({
plugin,
snapshot: true,
tests: [
// import react-native
"import { View } from 'react-native';",
"import { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';",
"import { createElement, Switch, StyleSheet } from 'react-native';",
"import { InvalidThing, TouchableOpacity } from 'react-native';",
"import * as RNW from 'react-native';",
// import react-native-web
// "import { View } from 'react-native-web';",
// "import { Switch, Text, View as MyView } from 'react-native-web';",
// "import { createElement, Switch, StyleSheet } from 'react-native-web';",
// "import { InvalidThing, TouchableOpacity } from 'react-native-web';",
// "import * as RNW from 'react-native-web';",
// require react-native
"const { View } = require('react-native');",
"let { Switch, Text, View: MyView } = require('react-native');",
"var { createElement, Switch, StyleSheet } = require('react-native');",
"const { InvalidThing, TouchableOpacity } = require('react-native');",
// require react-native-web
// "const { View } = require('react-native-web');",
// "let { Switch, Text, View: MyView } = require('react-native-web');",
// "var { createElement, Switch, StyleSheet } = require('react-native-web');",
// "const { InvalidThing, TouchableOpacity } = require('react-native-web');",
// export react-native
"export { View } from 'react-native';",
"export { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';",
"export { createElement, Switch, StyleSheet } from 'react-native';",
"export { InvalidThing, TouchableOpacity } from 'react-native';",
"export { default as RNW } from 'react-native';",
{
code: "const RNW = require('react-native');",
output: "const RNW = require('react-native');",
snapshot: false
}
]
});

View File

@@ -1,181 +0,0 @@
const getDistLocation = importName => {
const root = 'react-native-web/dist';
switch (importName) {
// apis
case 'Animated':
case 'AppRegistry':
case 'AppState':
case 'AsyncStorage':
case 'BackHandler':
case 'Clipboard':
case 'Dimensions':
case 'Easing':
case 'I18nManager':
case 'InteractionManager':
case 'Keyboard':
case 'Linking':
case 'NetInfo':
case 'PanResponder':
case 'PixelRatio':
case 'Platform':
case 'StyleSheet':
case 'UIManager':
case 'Vibration': {
return `${root}/apis/${importName}`;
}
// components
case 'ActivityIndicator':
case 'Button':
case 'FlatList':
case 'Image':
case 'KeyboardAvoidingView':
case 'ListView':
case 'Modal':
case 'Picker':
case 'ProgressBar':
case 'RefreshControl':
case 'ScrollView':
case 'SectionList':
case 'Slider':
case 'StatusBar':
case 'Switch':
case 'Text':
case 'TextInput':
case 'View':
case 'VirtualizedList': {
return `${root}/components/${importName}`;
}
case 'Touchable':
case 'TouchableHighlight':
case 'TouchableNativeFeedback':
case 'TouchableOpacity':
case 'TouchableWithoutFeedback': {
return `${root}/components/Touchable/${importName}`;
}
// modules
case 'createElement':
case 'findNodeHandle':
case 'NativeModules':
case 'processColor':
case 'render':
case 'unmountComponentAtNode': {
return `${root}/modules/${importName}`;
}
// propTypes
case 'ColorPropType':
case 'EdgeInsetsPropType':
case 'PointPropType': {
return `${root}/propTypes/${importName}`;
}
case 'TextPropTypes': {
return `${root}/components/Text/${importName}`;
}
case 'ViewPropTypes': {
return `${root}/components/View/${importName}`;
}
default:
return;
}
};
const isReactNativeRequire = (t, node) => {
const { declarations } = node;
if (declarations.length > 1) {
return false;
}
const { id, init } = declarations[0];
return (
t.isObjectPattern(id) &&
t.isCallExpression(init) &&
t.isIdentifier(init.callee) &&
init.callee.name === 'require' &&
init.arguments.length === 1 &&
init.arguments[0].value === 'react-native'
);
};
module.exports = function({ types: t }) {
return {
name: 'Rewrite react-native paths for react-native-web',
visitor: {
ImportDeclaration(path) {
const { source, specifiers } = path.node;
if (source && source.value === 'react-native' && specifiers.length) {
const imports = specifiers
.map(specifier => {
if (t.isImportSpecifier(specifier)) {
const importName = specifier.imported.name;
const distLocation = getDistLocation(importName);
if (distLocation) {
return t.importDeclaration(
[t.importDefaultSpecifier(t.identifier(specifier.local.name))],
t.stringLiteral(distLocation)
);
}
}
return t.importDeclaration([specifier], t.stringLiteral('react-native-web'));
})
.filter(Boolean);
path.replaceWithMultiple(imports);
}
},
ExportNamedDeclaration(path) {
const { source, specifiers } = path.node;
if (source && source.value === 'react-native' && specifiers.length) {
const exports = specifiers
.map(specifier => {
if (t.isExportSpecifier(specifier)) {
const exportName = specifier.exported.name;
const localName = specifier.local.name;
const distLocation = getDistLocation(localName);
if (distLocation) {
return t.exportNamedDeclaration(
null,
[t.exportSpecifier(t.identifier('default'), t.identifier(exportName))],
t.stringLiteral(distLocation)
);
}
return t.exportNamedDeclaration(
null,
[specifier],
t.stringLiteral('react-native-web')
);
}
})
.filter(Boolean);
path.replaceWithMultiple(exports);
}
},
VariableDeclaration(path) {
if (isReactNativeRequire(t, path.node)) {
const { id } = path.node.declarations[0];
const imports = id.properties
.map(identifier => {
const distLocation = getDistLocation(identifier.key.name);
if (distLocation) {
return t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(identifier.value.name),
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)])
)
]);
}
})
.filter(Boolean);
path.replaceWithMultiple(imports);
}
}
}
};
};

View File

@@ -1,52 +0,0 @@
# Performance
To run these benchmarks:
```
npm run build:performance
open ./performance/index.html
```
Append `?fastest` to the URL to include the fastest "other libraries", and
`?all` to include all the "other libraries".
## Notes
The components used in the render benchmarks are simple enough to be
implemented by multiple UI or style libraries. The implementations are not
equivalent in functionality. For example, React Native for Web's stylesheet is
unique in that it also converts React Native styles to DOM styles, has
deterministic resolution, and supports RTL layout.
`react-native-web/stylesheet` is a comparative baseline that implements a
simple `View` without much of React Native's functionality.
## Benchmark results
Typical render timings*: mean ± two standard deviations.
| Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `88.83` `±18.63` | `198.79` `±22.98` | |
| `react-native-web/stylesheet@0.0.121` | `91.17` `±19.29` | `209.67` `±32.38` | |
| `react-native-web@0.0.121` | `124.21` `±16.84` | `264.55` `±38.75` | `16.90` `±7.30ms` |
Other libraries
| Implementation | Deep tree (ms) | Wide tree (ms) |
| :--- | ---: | ---: |
| `aphrodite@1.2.3` | `91.73` `±41.63` | `197.72` `±44.90` |
| `styletron@2.5.1` | `94.73` `±37.58` | `201.81` `±57.93` |
| `glamor@2.20.40` | `146.60` `±26.73` | `277.46` `±29.17` |
| `emotion@7.2.2` | `150.79` `±38.29` | `282.18` `±41.79` |
| `react-jss@7.1.0` | `201.83` `±34.65` | `428.61` `±47.8` |
| `reactxp@0.42.1` | `262.69` `±24.14` | `595.20` `±66.17` |
| `styled-components@2.1.2` | `280.59` `±31.77` | `599.00` `±62.99` |
| `styled-components/primitives@2.1.2` | `291.74` `±48.96` | `606.57` `±78.18` |
| `radium@0.19.4` | `563.94` `±69.91` | `1139.18` `±152.59` |
These results indicate that style render performance is not a significant
differentiating factor between `aphrodite`, `css-modules`, `react-native-web`,
and `styletron`.
*MacBook Pro (13-inch, Early 2015); 3.1 GHz Intel Core i7; 16 GB 1867 MHz DDR3. Google Chrome 58 (2x CPU slowdown).

View File

@@ -1,22 +0,0 @@
{
"name": "benchmarks",
"private": true,
"dependencies": {
"aphrodite": "^1.2.5",
"classnames": "^2.2.5",
"emotion": "^8.0.9",
"glamor": "^2.20.40",
"marky": "^1.2.0",
"radium": "^0.19.6",
"react-jss": "^7.2.0",
"react-primitives": "^0.4.3",
"reactxp": "^0.42.11",
"styled-components": "^2.2.3",
"styletron-client": "^2.5.7",
"styletron-utils": "^2.5.4"
},
"devDependencies": {
"css-loader": "^0.28.7",
"style-loader": "^0.19.0"
}
}

View File

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

View File

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

View File

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

View File

@@ -1,13 +0,0 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import styles from './styles.css';
class View extends React.Component {
render() {
const props = this.props;
return <div {...props} className={classnames(styles.initial, props.className)} />;
}
}
export default View;

View File

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

View File

@@ -1,31 +0,0 @@
/* eslint-disable react/prop-types */
import Radium from 'radium';
import React from 'react';
class View extends React.Component {
render() {
const { style, ...other } = this.props;
return <div {...other} style={[styles.root, style]} />;
}
}
const styles = {
root: {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
}
};
export default Radium(View);

View File

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

View File

@@ -1,9 +0,0 @@
import Box from './components/Box/radium';
import View from './components/View/radium';
const api = {
Box,
View
};
export default api;

View File

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

1
core.js Normal file
View File

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

59
docs/apis/AppRegistry.md Normal file
View File

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

60
docs/apis/AppState.md Normal file
View File

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

71
docs/apis/AsyncStorage.md Normal file
View File

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

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

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

13
docs/apis/Dimensions.md Normal file
View File

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

25
docs/apis/I18nManager.md Normal file
View File

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

View File

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

77
docs/apis/NetInfo.md Normal file
View File

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

51
docs/apis/PixelRatio.md Normal file
View File

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

45
docs/apis/Platform.md Normal file
View File

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

78
docs/apis/StyleSheet.md Normal file
View File

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

35
docs/apis/Vibration.md Normal file
View File

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

View File

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

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

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

167
docs/components/Image.md Normal file
View File

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

View File

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

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

View File

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

76
docs/components/Switch.md Normal file
View File

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

161
docs/components/Text.md Normal file
View File

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

View File

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

View File

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

316
docs/components/View.md Normal file
View File

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

View File

@@ -40,40 +40,35 @@ using `aria-label`.
In some cases, we also want to alert the end user of the type of selected
component (i.e., that it is a “button”). To provide more context to screen
readers, you should specify the `accessibilityRole` property. (Note that React
Native for Web also provides a compatibility mapping of equivalent
Native for Web provides a compatibility mapping of equivalent
`accessibilityTraits` and `accessibilityComponentType` values to
`accessibilityRole`).
The `accessibilityRole` prop is used to infer an [analogous HTML
element][html-aria-url] and ARIA `role`, where possible. In most cases, both
the element and ARIA `role` are rendered. While this may contradict some ARIA
recommendations, it also helps avoid certain HTML5 conformance errors and
accessibility anti-patterns (e.g., giving a `heading` role to a `button`
element) and browser bugs.
element][html-aria-url] to use in addition to the resulting ARIA `role`, where
possible. While this may contradict some ARIA recommendations, it also helps
avoid certain HTML5 conformance errors and accessibility anti-patterns (e.g.,
giving a `heading` role to a `button` element).
For example:
* `<View accessibilityRole='article' />` => `<article role='article' />`.
* `<View accessibilityRole='banner' />` => `<header role='banner' />`.
* `<View accessibilityRole='button' />` => `<div role='button' tabIndex='0' />`.
* `<Text accessibilityRole='label' />` => `<label />`.
* `<View accessibilityRole='button' />` => `<button type='button' role='button' />`.
* `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`.
* `<View accessibilityRole='main' />` => `<main role='main' />`.
In the example below, the `TouchableHighlight` is announced by screen
readers as a button.
In the example below, the `TouchableWithoutFeedback` is announced by screen
readers as a native Button.
```js
<TouchableHighlight accessibilityRole="button" onPress={this._handlePress}>
```
<TouchableWithoutFeedback accessibilityRole="button" onPress={this._onPress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Press me!</Text>
</View>
</TouchableHighlight>
</TouchableWithoutFeedback>
```
Note: The `button` role is not implemented using the native `button` element
due to browsers limiting the use of flexbox layout on its children.
Note: Avoid changing `accessibilityRole` values over time or after user
actions. Generally, accessibility APIs do not provide a means of notifying
assistive technologies of a `role` value change.

View File

@@ -1,113 +0,0 @@
# Advanced use
## Use with existing React DOM components
React Native for Web exports a web-specific module called `createElement`,
which can be used to wrap React DOM components. This allows you to use React
Native's accessibility and style optimizations.
In the example below, `Video` will now accept common React Native props such as
`accessibilityLabel`, `accessible`, `style`, and even the Responder event
props.
```js
import { createElement } from 'react-native-web';
const Video = (props) => createElement('video', props);
```
This also works with composite components defined in your existing component
gallery or dependencies ([live example](https://www.webpackbin.com/bins/-KiTSGFw3fB9Szg7quLI)).
```js
import RaisedButton from 'material-ui/RaisedButton';
import { createElement } from 'react-native-web';
import { StyleSheet } from 'react-native';
const CustomButton = (props) => createElement(RaisedButton, {
...props,
style: [ styles.button, props.style ]
});
const styles = StyleSheet.create({
button: {
padding: 20
}
});
```
And `createElement` can be used as drop-in replacement for `React.createElement`:
```js
/* @jsx createElement */
import { createElement } from 'react-native-web';
const Video = (props) => <video {...props} style={[ { marginVertical: 10 }, props.style ]} />
```
Remember that React Native styles are not the same as React DOM styles, and
care needs to be taken not to pass React DOM styles into your React Native
wrapped components.
## Use as a library framework
The React Native (for Web) building blocks can be used to create higher-level
components and abstractions. In the example below, a `styled` function provides
an API inspired by styled-components ([live
example](https://www.webpackbin.com/bins/-KjT9ziwv4O7FDZdvsnX)).
```js
import { createElement } from 'react-native-web';
import { StyleSheet } from 'react-native';
/**
* styled API
*/
const styled = (Component, styler) => {
const isDOMComponent = typeof Component === 'string';
class Styled extends React.Component {
static contextTypes = {
getTheme: React.PropTypes.func
};
render() {
const theme = this.context.getTheme && this.context.getTheme();
const localProps = { ...this.props, theme };
const nextProps = { ...this.props }
const style = typeof styler === 'function' ? styler(localProps) : styler;
nextProps.style = [ style, this.props.style ];
return (
isDOMComponent
? createElement(Component, nextProps)
: <Component {...nextProps} />
);
}
}
return Styled;
}
const styles = StyleSheet.create({
container: {
alignItems: 'center',
backgroundColor: '#2196F3',
flex: 1,
justifyContent: 'center'
}
});
const StyledView = styled(View, styles.container);
```
## Use with react-sketchapp
Use with [react-sketchapp](http://airbnb.io/react-sketchapp/) requires that you
alias `react-native` to `react-sketchapp`. This will allow you to render your
existing React Native components in Sketch. Sketch-specific components like
`Artboard` should be imported from `react-sketchapp`.
If you're using `skpm`, you can rely on an [undocumented
feature](https://github.com/sketch-pm/skpm/blob/master/lib/utils/webpackConfig.js)
which will merge your `webpack.config.js`, `.babelrc`, or `package.json` Babel
config into its internal webpack config. The simplest option may be to use the
[babel-plugin-module-alias](https://www.npmjs.com/package/babel-plugin-module-alias)
and configure it in your `package.json`.

View File

@@ -1,68 +1,27 @@
# Direct manipulation
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.
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.
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` 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
@@ -76,7 +35,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:
@@ -104,7 +63,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.
@@ -127,7 +86,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

@@ -1,22 +1,16 @@
# Getting started
This guide will help you to correctly configure build and test tools to work
with React Native for Web. (Alternatively, you can quickly setup a local
project using the starter kits listed in the README.)
It is recommended that your application provide a `Promise` and `Array.from`
polyfill.
## Web packager
## Webpack and Babel
[Webpack](https://webpack.js.org) is a popular build tool for web apps. Below is an
[Webpack](webpack.js.org) is a popular build tool for web apps. Below is an
example of how to configure a build that uses [Babel](https://babeljs.io/) to
compile your JavaScript for the web.
Create a `web/webpack.config.js` file:
```js
// web/webpack.config.js
// webpack.config.js
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
@@ -34,10 +28,7 @@ const babelLoaderConfiguration = {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// This aliases 'react-native' to 'react-native-web' and includes only
// the modules needed by the app
plugins: ['react-native-web/babel'],
// The 'react-native' preset is recommended (or use your own .babelrc)
// The 'react-native' preset is recommended
presets: ['react-native']
}
}
@@ -66,14 +57,17 @@ module.exports = {
plugins: [
// `process.env.NODE_ENV === 'production'` must be `true` for production
// builds to eliminate development checks and reduce build size. You may
// wish to include additional optimizations.
// builds to eliminate development checks and reduce build size.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
],
},
resolve: {
// Maps the 'react-native' import to 'react-native-web'.
alias: {
'react-native': 'react-native-web'
},
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
@@ -82,136 +76,14 @@ module.exports = {
}
```
To run in development:
Please refer to the Webpack documentation for more information.
## Jest
[Jest](https://facebook.github.io/jest/) also needs to map `react-native` to `react-native-web`.
```
./node_modules/.bin/webpack-dev-server -d --config web/webpack.config.js --inline --hot --colors
```
To build for production:
```
./node_modules/.bin/webpack -p --config web/webpack.config.js
```
Please refer to the Webpack documentation for more information on configuration.
## Web entry
Create a `index.web.js` file (or simply `index.js` for web-only apps).
### Client-side rendering
Rendering using `AppRegistry`:
```js
// index.web.js
import App from './src/App';
import React from 'react';
import ReactNative, { AppRegistry } from 'react-native';
// register the app
AppRegistry.registerComponent('App', () => App);
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: document.getElementById('react-app')
});
```
Rendering within existing web apps is also possible using `ReactNative`:
```js
import AppHeader from './src/AppHeader';
import React from 'react';
import ReactNative from 'react-native';
// use .hydrate if hydrating a SSR app
ReactNative.render(<AppHeader />, document.getElementById('react-app-header'))
```
And finally, `react-native-web` components will also be rendering within a tree
produced by calling `ReactDOM.render` (i.e., an existing web app), but
otherwise it is not recommended.
### Server-side rendering
Server-side rendering is supported using the `AppRegistry`:
```js
import App from './src/App';
import ReactDOMServer from 'react-dom/server'
import ReactNative, { AppRegistry } from 'react-native'
// register the app
AppRegistry.registerComponent('App', () => App)
// prerender the app
const { element, stylesheets } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
const initialStyles = stylesheets.map((sheet) => ReactDOMServer.renderToStaticMarkup(sheet)).join('\n');
// construct HTML document
const document = `
<!DOCTYPE html>
<html>
<head>
${initialStyles}
</head>
<body>
${initialHTML}
`
```
## Web-specific code
Minor platform differences can use the `Platform` module.
```js
import { Platform } from 'react-native';
const styles = StyleSheet.create({
height: (Platform.OS === 'web') ? 200 : 100,
});
```
More significant platform differences should use platform-specific files (see
the webpack configuration above for resolving `*.web.js` files):
For example, with the following files in your project:
```
MyComponent.android.js
MyComponent.ios.js
MyComponent.web.js
```
And the following import:
```js
import MyComponent from './MyComponent';
```
React Native will automatically import the correct variant for each specific
target platform.
## Testing with Jest
[Jest](https://facebook.github.io/jest/) can be configured to improve snapshots
of `react-native-web` components.
```
{
"snapshotSerializers": [ "enzyme-to-json/serializer", "react-native-web/jest/serializer" ]
}
```
Jest also needs to map `react-native` to `react-native-web` (unless you are
using Babel with the `react-native-web/babel` plugin).
```
{
"jest": {
"moduleNameMapper": {
"react-native": "<rootDir>/node_modules/react-native-web"
}
@@ -219,3 +91,73 @@ using Babel with the `react-native-web/babel` plugin).
```
Please refer to the Jest documentation for more information.
## Web-specific code
Minor platform differences can use the `Platform` module.
```js
import { AppRegistry, Platform } from 'react-native'
AppRegistry.registerComponent('MyApp', () => MyApp)
if (Platform.OS === 'web') {
AppRegistry.runApplication('MyApp', {
rootTag: document.getElementById('react-root')
});
}
```
## Client-side rendering
Rendering using `ReactNative`:
```js
import React from 'react'
import ReactNative from 'react-native'
// component that renders the app
const AppHeaderContainer = (props) => { /* ... */ }
ReactNative.render(<AppHeaderContainer />, document.getElementById('react-app-header'))
```
Rendering using `AppRegistry`:
```js
import React from 'react'
import ReactNative, { AppRegistry } from 'react-native'
// component that renders the app
const AppContainer = (props) => { /* ... */ }
// register the app
AppRegistry.registerComponent('App', () => AppContainer)
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: document.getElementById('react-app')
})
```
Rendering within `ReactDOM.render` also works when introduce `react-native-web`
to an existing web app, but it is not recommended oherwise.
## Server-side rendering
Rendering using the `AppRegistry`:
```js
import ReactDOMServer from 'react-dom/server'
import ReactNative, { AppRegistry } from 'react-native'
// component that renders the app
const AppContainer = (props) => { /* ... */ }
// register the app
AppRegistry.registerComponent('App', () => AppContainer)
// prerender the app
const { element, stylesheet } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
```

View File

@@ -1,90 +1,73 @@
# Style
React Native relies on JavaScript to define and resolve the styles of your
application. React Native for Web implements the React Native style API in a
way that avoids *all* the [problems with CSS at
scale](https://speakerdeck.com/vjeux/react-css-in-js):
React Native for Web relies on JavaScript to define styles for your
application. This allows you to avoid issues arising from the [7 deadly sins of
CSS](https://speakerdeck.com/vjeux/react-css-in-js):
1. No local variables
2. Implicit dependencies
1. Global namespace
2. Dependency hell
3. No dead code elimination
4. No code minification
5. No sharing of constants
6. Non-deterministic resolution
7. No isolation
At the same time, it has several benefits:
1. Simple API and expressive subset of CSS
2. Generates CSS; the minimum required
3. Good runtime performance
4. Support for static and dynamic styles
5. Support for RTL layouts
6. Easy pre-rendering of critical CSS
7. Lack of isolation
## Defining styles
Styles should be defined outside of the component. Using `StyleSheet.create` is
optional but provides the best performance (by relying on generated CSS
stylesheets). Avoid creating unregistered style objects.
Styles should be defined outside of the component:
```js
class Example extends React.Component {}
const styles = StyleSheet.create({
heading: {
color: 'gray',
fontSize: '2rem'
},
text: {
marginTop: '1rem',
margin: 10
color: 'gray',
fontSize: '1.25rem'
}
})
```
See the `style` documentation of individual components for supported properties.
Using `StyleSheet.create` is optional but provides the best performance
(`style` is resolved to CSS stylesheets). Avoid creating unregistered style
objects.
The attribute names and values are a subset of CSS. See the `style`
documentation of individual components.
## Using styles
All the React Native components accept a `style` property. The value can be a
registered object, a plain object, or an array.
All the React Native components accept a `style` attribute.
```js
// registered object
<Text style={styles.text} />
<View style={styles.view} />
// plain object
<View style={{ transform: [ { translateX } ] }} />
// array of registered or plain objects
<View style={[ styles.container, this.props.style ]} />
```
The array syntax will merge styles from left-to-right as normal JavaScript
objects, and can be used to conditionally apply styles:
A common pattern is to conditionally add style based on a condition:
```js
// either
<View style={[
styles.container,
styles.base,
this.state.active && styles.active
]} />
```
When styles are registered with `StyleSheet.create`, the return value is a
number and not a style object. This is important for performance optimizations,
but still allows you to merge styles in a deterministic manner at runtime. If
you need access to the underlying style objects you need to use
`StyleSheet.flatten` (but be aware that this is not the optimized path).
## Composing styles
To let other components customize the style of a component's children you can
expose a prop so styles can be explicitly passed into the component.
In order to let a call site customize the style of your component children, you
can pass styles around. Use `View.propTypes.style` and `Text.propTypes.style` in
order to make sure only valid styles are being passed.
```js
class List extends React.Component {
static propTypes = {
style: ViewPropTypes.style,
elementStyle: ViewPropTypes.style,
style: View.propTypes.style,
elementStyle: View.propTypes.style,
}
render() {
@@ -107,126 +90,56 @@ In another file:
You also have much greater control over how styles are composed when compared
to using class names. For example, you may choose to accept a limited subset
of style props in the component's API, and control when they are applied.
## How styles are resolved
React Native style resolution is deterministic and slightly different from CSS.
In the following HTML/CSS example, the `.margin` selector is defined last in
the CSS and takes precedence over the previous rules, resulting in a margin of
`0, 0, 0, 0`.
```html
<style>
.marginTop { margin-top: 10px; }
.marginBottom { margin-bottom: 20px; }
.margin { margin: 0; }
</style>
<div class="marginTop marginBottom margin"></div>
```
But in React Native the most *specific* style property takes precedence,
resulting in margins of `10, 0, 20, 0`.
of style props in the component's API, and control when they are applied:
```js
const style = [
{ marginTop: 10 },
{ marginBottom: 20 },
{ margin: 0 }
];
const Box = () => <View style={style} />
```
## Implementation details
React Native for Web transforms React Native styles into React DOM styles. Any
styles defined using `StyleSheet.create` will ultimately be rendered using CSS
class names.
React Native for Web introduced a novel strategy to achieve this. Each rule is
broken down into declarations, properties are expanded to their long-form, and
the resulting key-value pairs are mapped to unique "atomic CSS" class names.
Input:
```js
const Box = () => <View style={styles.box} />
const styles = StyleSheet.create({
box: {
margin: 0
class List extends React.Component {
static propTypes = {
children: React.PropTypes.any,
// limit which styles are accepted
style: React.PropTypes.shape({
borderColor: View.propTypes.borderColor,
borderWidth: View.propTypes.borderWidth
})
}
});
render() {
return (
<View
children={children}
style={[
this.props.style,
// override border-color when scrolling
isScrolling && { borderColor: 'transparent' }
]}
/>
)
}
}
```
Output:
```html
<style>
.rn-1mnahxq { margin-top: 0px; }
.rn-61z16t { margin-right: 0px; }
.rn-p1pxzi { margin-bottom: 0px; }
.rn-11wrixw { margin-left: 0px; }
</style>
<div class="rn-156q2ks rn-61z16t rn-p1pxzi rn-11wrixw"></div>
```
This ensures that CSS order doesn't impact rendering and CSS rules are
efficiently deduplicated. Rather than the total CSS growing in proportion to
the number of *rules*, it grows in proportion to the number of *unique
declarations*. As a result, the DOM style sheet is only written to when new
unique declarations are defined and it is usually small enough to be
pre-rendered and inlined.
Class names are deterministic, which means that the resulting CSS and HTML is
consistent across builds important for large apps using code-splitting and
deploying incremental updates.
At runtime registered styles are resolved to DOM style props and memoized.
Any dynamic styles that contain declarations previously registered as static
styles can also be converted to CSS class names. Otherwise, they render as
inline styles.
All this allows React Native for Web to support the rich functionality of React
Native styles (including RTL layouts and `setNativeProps`) while providing one
of the [fastest](https://github.com/necolas/react-native-web/blob/master/benchmarks/README.md),
safest, and most efficient styles-in-JavaScript solutions.
## FAQs
### What about Media Queries?
## Media Queries
`StyleSheet.create` is a way of defining the styles your application requires;
it does not concern itself with _where_ or _when_ those styles are applied to
elements.
Media Queries may not be most appropriate for component-based designs. React
Native provides the `Dimensions` API and `onLayout` props. If you do need Media
Queries, using the `matchMedia` DOM API has the benefit of allowing you to swap
out entire components, not just styles. There are also many React libraries
wrapping JavaScript Media Query API's, e.g.,
[react-media](https://github.com/reacttraining/react-media),
There are various React libraries wrapping JavaScript Media Query API's, e.g.,
[react-media-queries](https://github.com/bloodyowl/react-media-queries),
[media-query-fascade](https://github.com/tanem/media-query-facade), or
[react-responsive](https://github.com/contra/react-responsive).
[react-responsive](https://github.com/contra/react-responsive). This has the
benefit of co-locating breakpoint-specific DOM and style changes.
### What about pseudo-classes and pseudo-elements?
## Pseudo-classes and pseudo-elements
Pseudo-classes like `:hover` and `:focus` can be implemented with events (e.g.
`onFocus`). Pseudo-elements are not supported; elements should be used instead.
### Do I need a CSS reset?
### Reset
No. React Native for Web includes a very small CSS reset that removes unwanted
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.
You **do not** need to include a CSS reset or
[normalize.css](https://necolas.github.io/normalize.css/).
### What about using Dev Tools?
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.
React Native for Web includes a very small CSS reset taken from normalize.css.
It removes unwanted User Agent styles from (pseudo-)elements beyond the reach
of React (e.g., `html`, `body`) or inline styles (e.g., `::-moz-focus-inner`).

View File

@@ -1,11 +0,0 @@
{
"scripts": {
"build": "yarn && build-storybook -o ./dist -c ./storybook/.storybook",
"start": "start-storybook -p 9001 -c ./storybook/.storybook",
"release": "yarn build && git checkout gh-pages && rm -rf ../storybook && mv dist ../storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"@storybook/addon-options": "^3.1.6",
"@storybook/react": "^3.1.9"
}
}

BIN
docs/static/components.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/static/styling-strategy.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

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

View File

@@ -1,23 +0,0 @@
import { setOptions } from '@storybook/addon-options';
import centered from './decorator-centered';
import { configure, addDecorator } from '@storybook/react';
const context = require.context('../', true, /Screen\.js$/);
addDecorator(centered);
setOptions({
name: 'React Native Web',
url: 'https://necolas.github.io/react-native-web',
goFullScreen: false,
showLeftPanel: true,
showDownPanel: false,
showSearchBox: false,
downPanelInRight: false
});
function loadStories() {
context.keys().forEach(context);
}
configure(loadStories, module);

View File

@@ -1,14 +0,0 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
const styles = StyleSheet.create({
root: {
minHeight: '100vh',
maxWidth: 680,
marginHorizontal: 'auto'
}
});
export default function(renderStory) {
return <View style={styles.root}>{renderStory()}</View>;
}

View File

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

View File

@@ -1,59 +0,0 @@
/* eslint-disable react/jsx-sort-props */
/**
* @flow
*/
import PropAnimating from './examples/PropAnimating';
import PropColor from './examples/PropColor';
import PropHidesWhenStopped from './examples/PropHidesWhenStopped';
import PropSize from './examples/PropSize';
import React from 'react';
import UIExplorer, { Description, DocItem, Section, storiesOf } from '../../ui-explorer';
const ActivityIndicatorScreen = () => (
<UIExplorer title="ActivityIndicator" url="1-components/ActivityIndicator">
<Description>Displays a customizable activity indicator</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
name="animating"
typeInfo="?boolean = true"
description="Whether to show the indicator or hide it."
example={{
render: () => <PropAnimating />
}}
/>
<DocItem
name="color"
typeInfo="?color = #1976D2"
description="The foreground color of the spinner."
example={{
render: () => <PropColor />
}}
/>
<DocItem
name="hidesWhenStopped"
typeInfo="?boolean = true"
description="Whether the indicator should hide when not animating."
example={{
render: () => <PropHidesWhenStopped />
}}
/>
<DocItem
name="size"
typeInfo="?enum('small', 'large') | number = 'small'"
description="Size of the indicator. Small has a height of 20px, large has a height of 36px."
example={{
render: () => <PropSize />
}}
/>
</Section>
</UIExplorer>
);
storiesOf('Components', module).add('ActivityIndicator', ActivityIndicatorScreen);

View File

@@ -1,22 +0,0 @@
/**
* @flow
*/
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import React from 'react';
const ActivityIndicatorAnimatingExample = () => (
<View style={styles.horizontal}>
<ActivityIndicator />
<ActivityIndicator animating={false} />
</View>
);
const styles = StyleSheet.create({
horizontal: {
alignItems: 'center',
flexDirection: 'row'
}
});
export default ActivityIndicatorAnimatingExample;

View File

@@ -1,34 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
const ActivityIndicatorColorExample = () => (
<View style={styles.horizontal}>
<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>
);
const styles = StyleSheet.create({
horizontal: {
alignItems: 'center',
flexDirection: 'row'
},
rightPadding: {
paddingRight: 10
}
});
ActivityIndicatorColorExample.metadata = {
id: 'ActivityIndicator.props.color',
description: ''
};
export default ActivityIndicatorColorExample;

View File

@@ -1,68 +0,0 @@
/**
* @flow
*/
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { bool } from 'prop-types';
import React, { PureComponent } from 'react';
class ToggleAnimatingActivityIndicator extends PureComponent {
static propTypes = {
hidesWhenStopped: bool,
style: ActivityIndicator.propTypes.style
};
state = {
animating: true
};
setToggleTimeout = () => {
this._timer = setTimeout(() => {
this.setState({ animating: !this.state.animating });
this.setToggleTimeout();
}, 2000);
};
componentDidMount() {
this.setToggleTimeout();
}
componentWillUnmount() {
clearTimeout(this._timer);
}
render() {
return (
<ActivityIndicator
animating={this.state.animating}
hidesWhenStopped={this.props.hidesWhenStopped}
size="large"
style={[styles.centering, this.props.style]}
/>
);
}
}
const ActivityIndicatorHidesWhenStoppedExample = () => (
<View style={[styles.horizontal]}>
<ToggleAnimatingActivityIndicator hidesWhenStopped={false} style={styles.rightPadding} />
<ToggleAnimatingActivityIndicator />
</View>
);
const styles = StyleSheet.create({
horizontal: {
alignItems: 'center',
flexDirection: 'row'
},
rightPadding: {
paddingRight: 10
}
});
ActivityIndicatorHidesWhenStoppedExample.metadata = {
id: 'ActivityIndicator.props.hidesWhenStopped',
description: ''
};
export default ActivityIndicatorHidesWhenStoppedExample;

View File

@@ -1,33 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
const sizes = [20, 'small', 36, 'large', 60];
const ActivityIndicatorSizeExample = () => (
<View style={styles.horizontal}>
{sizes.map((size, i) => <ActivityIndicator key={i} size={size} style={styles.rightPadding} />)}
<ActivityIndicator size="large" style={styles.large} />
</View>
);
const styles = StyleSheet.create({
horizontal: {
alignItems: 'center',
flexDirection: 'row'
},
rightPadding: {
paddingRight: 10
},
large: { marginLeft: 20, transform: [{ scale: 1.75 }] }
});
ActivityIndicatorSizeExample.metadata = {
id: 'ActivityIndicator.props.size',
description: ''
};
export default ActivityIndicatorSizeExample;

View File

@@ -1,74 +0,0 @@
/* eslint-disable react/jsx-sort-props */
/**
* @flow
*/
import React from 'react';
import PropColor from './examples/PropColor';
import PropDisabled from './examples/PropDisabled';
import PropOnPress from './examples/PropOnPress';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
storiesOf
} from '../../ui-explorer';
const ButtonScreen = () => (
<UIExplorer title="Button" url="1-components/Button">
<Description>
<AppText>
A basic button component. Supports a minimal level of customization. You can build your own
custom button using <Code>TouchableOpacity</Code> or <Code>TouchableNativeFeedback</Code>.
</AppText>
</Description>
<Section title="Props">
<DocItem
name="accessibilityLabel"
typeInfo="?string"
description="Overrides the text that's read by a screen reader when the user interacts with the element."
/>
<DocItem
name="color"
typeInfo="?string"
description="Background color of the button."
example={{
render: () => <PropColor />
}}
/>
<DocItem
name="disabled"
typeInfo="?boolean"
description="If true, disable all interactions for this element."
example={{
render: () => <PropDisabled />
}}
/>
<DocItem
name="onPress"
typeInfo="function"
description="This function is called on press."
example={{
render: () => <PropOnPress />
}}
/>
<DocItem
name="testID"
typeInfo="?string"
description="Used to locate this view in end-to-end tests."
/>
<DocItem name="title" typeInfo="string" description="Text to display inside the button." />
</Section>
</UIExplorer>
);
storiesOf('Components', module).add('Button', ButtonScreen);

View File

@@ -1,23 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { DividerVertical } from '../helpers';
import { Button, View } from 'react-native';
const emptyFunction = () => {};
const ButtonColorExample = () => (
<View>
<Button color="#17BF63" onPress={emptyFunction} title="Press me" />
<DividerVertical />
<Button color="#F45D22" onPress={emptyFunction} title="Press me" />
<DividerVertical />
<Button color="#794BC4" onPress={emptyFunction} title="Press me" />
<DividerVertical />
<Button color="#E0245E" onPress={emptyFunction} title="Press me" />
</View>
);
export default ButtonColorExample;

View File

@@ -1,13 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { Button } from 'react-native';
const onPress = () => {
console.error('Disabled button should not trigger onPress!');
};
const ButtonDisabledExample = () => <Button disabled onPress={onPress} title="Disabled button" />;
export default ButtonDisabledExample;

View File

@@ -1,29 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { DividerHorizontal } from '../helpers';
import { Button, StyleSheet, View } from 'react-native';
const emptyFunction = () => {};
const ButtonOnPressExample = () => (
<View style={styles.horizontal}>
<Button
accessibilityLabel="This sounds great!"
onPress={emptyFunction}
title="This looks great!"
/>
<DividerHorizontal />
<Button color="#841584" onPress={emptyFunction} title="Ok!" />
</View>
);
const styles = StyleSheet.create({
horizontal: {
flexDirection: 'row'
}
});
export default ButtonOnPressExample;

View File

@@ -1,20 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { StyleSheet, View } from 'react-native';
const DividerHorizontal = () => <View style={styles.horizontalDivider} />;
const DividerVertical = () => <View style={styles.verticalDivider} />;
const styles = StyleSheet.create({
horizontalDivider: {
width: '0.6rem'
},
verticalDivider: {
height: '1.3125rem'
}
});
export { DividerHorizontal, DividerVertical };

View File

@@ -1,181 +0,0 @@
/* eslint-disable react/jsx-sort-props */
/**
* @flow
*/
import React from 'react';
import PropChildren from './examples/PropChildren';
import PropDefaultSource from './examples/PropDefaultSource';
import PropDraggable from './examples/PropDraggable';
import PropOnError from './examples/PropOnError';
import PropOnLoad from './examples/PropOnLoad';
import PropOnLoadEnd from './examples/PropOnLoadEnd';
import PropOnLoadStart from './examples/PropOnLoadStart';
import PropResizeMode from './examples/PropResizeMode';
import PropSource from './examples/PropSource';
import StaticGetSizeExample from './examples/StaticGetSize';
import StaticPrefetchExample from './examples/StaticPrefetch';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
storiesOf
} from '../../ui-explorer';
const ImageScreen = () => (
<UIExplorer title="Image" url="1-components/Image">
<Description>
An accessibile image component with support for image resizing, default image, and child
content.
</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
name="children"
typeInfo="?any"
description="Content to display over the image."
example={{
render: () => <PropChildren />
}}
/>
<DocItem
name="defaultSource"
typeInfo="?object"
description={
<AppText>
An image to display as a placeholder while downloading the final image off the network.{' '}
<Code>{'{ uri: string, width, height }'}</Code>
</AppText>
}
example={{
render: () => <PropDefaultSource />
}}
/>
<DocItem
label="web"
name="draggable"
typeInfo="?boolean = true"
description="When false, the image will not be draggable"
example={{
render: () => <PropDraggable />
}}
/>
<DocItem
name="onError"
typeInfo="?function"
description={
<AppText>
Invoked on load error with <Code>{'{nativeEvent: {error}}'}</Code>.
</AppText>
}
example={{
render: () => <PropOnError />
}}
/>
<DocItem
name="onLoad"
typeInfo="?function"
description="Invoked when load completes successfully."
example={{
render: () => <PropOnLoad />
}}
/>
<DocItem
name="onLoadEnd"
typeInfo="?function"
description="Invoked when load either succeeds or fails."
example={{
render: () => <PropOnLoadEnd />
}}
/>
<DocItem
name="onLoadStart"
typeInfo="?function"
description="Invoked on load start."
example={{
render: () => <PropOnLoadStart />
}}
/>
<DocItem
name="resizeMode"
typeInfo="?enum('center', 'contain', 'cover', 'none', 'repeat', 'stretch') = 'cover';"
description="Determines how to resize the image when the frame doesn't match the raw image dimensions."
example={{
render: () => <PropResizeMode />
}}
/>
<DocItem
name="source"
typeInfo="?object"
description={
<AppText>
<Code>uri</Code> is a string representing the resource identifier for the image, which
could be an http address or a base64 encoded image.{' '}
<Code>{'{ uri: string, width, height }'}</Code>
</AppText>
}
example={{
render: () => <PropSource />
}}
/>
<DocItem name="style" typeInfo="?style" />
</Section>
<Section title="Properties">
<DocItem
name="static resizeMode"
typeInfo="object"
example={{
code: '<Image resizeMode={Image.resizeMode.contain} />'
}}
/>
</Section>
<Section title="Methods">
<DocItem
name="static getSize"
typeInfo="(uri: string, success: (width, height) => {}, failure: function) => void"
description={[
<AppText key={1}>
Retrieve the width and height (in pixels) of an image prior to displaying it. This
method can fail if the image cannot be found, or fails to download.
</AppText>,
<AppText key={2}>
(In order to retrieve the image dimensions, the image may first need to be loaded or
downloaded, after which it will be cached. This means that in principle you could use
this method to preload images, however it is not optimized for that purpose, and may in
future be implemented in a way that does not fully load/download the image data.)
</AppText>
]}
example={{
render: () => <StaticGetSizeExample />
}}
/>
<DocItem
name="static prefetch"
typeInfo="(url: string) => Promise"
description="Prefetches a remote image for later use by downloading it."
example={{
render: () => <StaticPrefetchExample />
}}
/>
</Section>
</UIExplorer>
);
storiesOf('Components', module).add('Image', ImageScreen);

View File

@@ -1,72 +0,0 @@
/**
* @flow
*/
/*
import React, { PureComponent } from 'react';
import { Image, StyleSheet, Text, View } from 'react-native';
export default class MultipleSourcesExample extends PureComponent {
state = {
width: 30,
height: 30
};
render() {
return (
<View style={styles.container}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Text style={styles.touchableText} onPress={this.decreaseImageSize}>
Decrease image size
</Text>
<Text style={styles.touchableText} onPress={this.increaseImageSize}>
Increase image size
</Text>
</View>
<Text>Container image size: {this.state.width}x{this.state.height} </Text>
<View style={{ height: this.state.height, width: this.state.width }}>
<Image
style={{ flex: 1 }}
source={[
{ uri: 'http://facebook.github.io/react/img/logo_small.png', width: 38, height: 38 },
{
uri: 'http://facebook.github.io/react/img/logo_small_2x.png',
width: 76,
height: 76
},
{ uri: 'http://facebook.github.io/react/img/logo_og.png', width: 400, height: 400 }
]}
/>
</View>
</View>
);
}
increaseImageSize = () => {
if (this.state.width >= 100) {
return;
}
this.setState(state => ({
width: state.width + 10,
height: state.height + 10
}));
};
decreaseImageSize = () => {
if (this.state.width <= 10) {
return;
}
this.setState(state => ({
width: state.width - 10,
height: state.height - 10
}));
};
}
const styles = StyleSheet.create({
touchableText: {
color: 'blue',
fontWeight: '500'
}
});
*/

View File

@@ -1,84 +0,0 @@
/**
* @flow
*/
import * as helpers from '../helpers';
import { oneOf } from 'prop-types';
import sources from '../sources';
import React, { PureComponent } from 'react';
import { ActivityIndicator, Image, Text, View } from 'react-native';
class NetworkImageExample extends PureComponent {
state = {
error: false,
loading: false
};
static propTypes = {
logMethod: oneOf(['onError', 'onLoad', 'onLoadEnd', 'onLoadStart']),
source: Image.propTypes.source
};
static defaultProps = {
logList: []
};
render() {
const loader = this.state.loading ? (
<View>
<ActivityIndicator />
</View>
) : null;
return (
<View>
<Image
defaultSource={sources.placeholder}
onError={this._handleError}
onLoad={this._handleLoad}
onLoadEnd={this._handleLoadEnd}
onLoadStart={this._handleLoadStart}
source={this.props.source}
style={helpers.styles.base}
>
{loader}
</Image>
{this.state.message && <Text style={helpers.styles.marginTop}>{this.state.message}</Text>}
</View>
);
}
_handleError = e => {
const nextState = { loading: false };
if (this.props.logMethod === 'onError') {
nextState.message = `✘ onError ${JSON.stringify(e.nativeEvent)}`;
}
this.setState(() => nextState);
};
_handleLoad = () => {
const nextState = { loading: false };
if (this.props.logMethod === 'onLoad') {
nextState.message = '✔ onLoad';
}
this.setState(() => nextState);
};
_handleLoadEnd = () => {
const nextState = { loading: false };
if (this.props.logMethod === 'onLoadEnd') {
nextState.message = '✔ onLoadEnd';
}
this.setState(() => nextState);
};
_handleLoadStart = () => {
const nextState = { loading: true };
if (this.props.logMethod === 'onLoadStart') {
nextState.message = '✔ onLoadStart';
}
this.setState(() => nextState);
};
}
export default NetworkImageExample;

View File

@@ -1,33 +0,0 @@
/**
* @flow
*/
import sources from '../sources';
import React from 'react';
import { Image, StyleSheet, Text } from 'react-native';
const ImageChildrenExample = () => (
<Image source={sources.large} style={styles.image}>
<Text style={styles.text}>Child content</Text>
</Image>
);
const styles = StyleSheet.create({
image: {
width: 300,
height: 200,
backgroundColor: 'transparent',
justifyContent: 'center',
alignItems: 'center'
},
text: {
backgroundColor: 'transparent',
color: 'white',
fontWeight: 'bold',
fontSize: 18,
position: 'relative',
top: 50
}
});
export default ImageChildrenExample;

View File

@@ -1,14 +0,0 @@
/**
* @flow
*/
import * as helpers from '../helpers';
import sources from '../sources';
import React from 'react';
import { Image } from 'react-native';
const ImageDefaultSourceExample = () => (
<Image defaultSource={sources.placeholder} style={helpers.styles.base} />
);
export default ImageDefaultSourceExample;

View File

@@ -1,27 +0,0 @@
/**
* @flow
*/
import sources from '../sources';
import React from 'react';
import { Image, StyleSheet, View } from 'react-native';
const ImageDraggableExample = () => (
<View style={styles.container}>
<Image draggable={false} source={sources.large} style={styles.image} />
</View>
);
const styles = StyleSheet.create({
container: {
flexDirection: 'row'
},
image: {
width: 300,
height: 200,
backgroundColor: 'transparent',
marginRight: 10
}
});
export default ImageDraggableExample;

View File

@@ -1,11 +0,0 @@
/**
* @flow
*/
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';
const ImageOnErrorExample = () => <NetworkImage logMethod="onError" source={sources.broken} />;
export default ImageOnErrorExample;

View File

@@ -1,14 +0,0 @@
/**
* @flow
*/
import { createUncachedURI } from '../helpers';
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';
const ImageOnLoadExample = () => (
<NetworkImage logMethod="onLoad" source={createUncachedURI(sources.largeAlt)} />
);
export default ImageOnLoadExample;

View File

@@ -1,14 +0,0 @@
/**
* @flow
*/
import { createUncachedURI } from '../helpers';
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';
const ImageOnLoadEndExample = () => (
<NetworkImage logMethod="onLoadEnd" source={createUncachedURI(sources.largeAlt)} />
);
export default ImageOnLoadEndExample;

View File

@@ -1,14 +0,0 @@
/**
* @flow
*/
import { createUncachedURI } from '../helpers';
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';
const ImageOnLoadStartExample = () => (
<NetworkImage logMethod="onLoadStart" source={createUncachedURI(sources.largeAlt)} />
);
export default ImageOnLoadStartExample;

View File

@@ -1,74 +0,0 @@
/**
* @flow
*/
import React from 'react';
import sources from '../sources';
import { Image, StyleSheet, Text, View } from 'react-native';
const ImageResizeModeExample = () => (
<View>
{[sources.small].map((source, i) => {
return (
<View key={i}>
<View style={styles.horizontal}>
<View>
<Text style={[styles.resizeModeText]}>Contain</Text>
<Image
resizeMode={Image.resizeMode.contain}
source={source}
style={styles.resizeMode}
/>
</View>
<View>
<Text style={[styles.resizeModeText]}>Cover</Text>
<Image
resizeMode={Image.resizeMode.cover}
source={source}
style={styles.resizeMode}
/>
</View>
<View>
<Text style={[styles.resizeModeText]}>Stretch</Text>
<Image
resizeMode={Image.resizeMode.stretch}
source={source}
style={styles.resizeMode}
/>
</View>
<View>
<Text style={[styles.resizeModeText]}>Repeat</Text>
<Image resizeMode={'repeat'} source={source} style={styles.resizeMode} />
</View>
<View>
<Text style={[styles.resizeModeText]}>Center</Text>
<Image resizeMode={'center'} source={source} style={styles.resizeMode} />
</View>
</View>
</View>
);
})}
</View>
);
const styles = StyleSheet.create({
horizontal: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between'
},
resizeMode: {
borderColor: 'black',
borderWidth: 0.5,
height: 120,
width: 120
},
resizeModeText: {
marginBottom: '0.5rem'
},
leftMargin: {
marginLeft: 10
}
});
export default ImageResizeModeExample;

View File

@@ -1,52 +0,0 @@
/**
* @flow
*/
import sources from '../sources';
import React from 'react';
import { Image, StyleSheet, Text, View } from 'react-native';
const ImageSourceExample = () => (
<View style={styles.row}>
<View style={styles.column}>
<Text style={styles.text}>Static image</Text>
<Image source={sources.static} style={styles.image} />
</View>
<View style={styles.column}>
<Text style={styles.text}>Animated GIF</Text>
<Image source={sources.animatedGif} style={styles.image} />
</View>
<View style={styles.column}>
<Text style={styles.text}>Data PNG</Text>
<Image source={sources.dataPng} style={styles.image} />
</View>
<View style={styles.column}>
<Text style={styles.text}>Data SVG</Text>
<Image source={sources.dataSvg} style={styles.image} />
</View>
</View>
);
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between'
},
column: {
alignItems: 'flex-start',
marginBottom: '1rem'
},
text: {
marginBottom: '0.5rem'
},
image: {
borderColor: 'black',
borderWidth: 0.5,
height: 120,
width: 120,
resizeMode: 'cover'
}
});
export default ImageSourceExample;

View File

@@ -1,66 +0,0 @@
/**
* @flow
*/
import { createUncachedURI } from '../helpers';
import sources from '../sources';
import React, { PureComponent } from 'react';
import { Button, Image, StyleSheet, Text, View } from 'react-native';
class ImageGetSizeExample extends PureComponent {
static propTypes = {
source: Image.propTypes.source
};
state = {
showButton: true,
startLoad: false,
width: 0,
height: 0
};
render() {
const { showButton, startLoad } = this.state;
return (
<View>
{showButton ? (
<View style={styles.button}>
<Button onPress={this._handlePress} title="(4.7MB) Get image dimensions" />
</View>
) : null}
{startLoad ? (
<View>
<Text>
Source dimensions:{' '}
{JSON.stringify({ width: this.state.width, height: this.state.height })}
</Text>
<Image source={createUncachedURI(this.props.source.uri)} style={styles.image} />
</View>
) : null}
</View>
);
}
_handlePress = () => {
Image.getSize(this.props.source.uri, (width, height) => {
this.setState({ startLoad: true, showButton: false, width, height });
});
};
}
const styles = StyleSheet.create({
button: {
maxWidth: 300
},
image: {
backgroundColor: '#eee',
height: 227,
marginTop: 10,
width: 323
}
});
const StaticGetSizeExample = () => <ImageGetSizeExample source={sources.huge} />;
export default StaticGetSizeExample;

View File

@@ -1,96 +0,0 @@
/**
* @flow
*/
import { createUncachedURI } from '../helpers';
import sources from '../sources';
import React, { PureComponent } from 'react';
import { Button, Image, StyleSheet, Text, View } from 'react-native';
class ImagePrefetchExample extends PureComponent {
static propTypes = {
source: Image.propTypes.source
};
state = {
events: [],
showButton: true,
startLoad: false
};
render() {
const { showButton, startLoad } = this.state;
return (
<View>
{showButton ? (
<View style={styles.button}>
<Button onPress={this._handlePress} title="Prefetch image" />
</View>
) : null}
{startLoad ? (
<View>
<Text>{this.state.events.join('\n')}</Text>
<Image
onLoad={this._handleLoad}
onLoadEnd={this._handleLoadEnd}
onLoadStart={this._handleLoadStart}
source={this.props.source}
style={styles.image}
/>
</View>
) : null}
</View>
);
}
_handleLoad = () => {
const startTime = this._startTime;
this._loadEventFired(`✔ (prefetched) onLoad (+${Date.now() - startTime}ms)`);
};
_handleLoadEnd = () => {
const startTime = this._startTime;
this._loadEventFired(`✔ (prefetched) onLoadEnd (+${Date.now() - startTime}ms)`);
};
_handleLoadStart = () => {
const startTime = this._startTime;
this._loadEventFired(`✔ (prefetched) onLoadStart (+${Date.now() - startTime}ms)`);
};
_handlePress = () => {
this._startTime = this._startTime || Date.now();
Image.prefetch(createUncachedURI(this.props.source.uri)).then(
() => {
this._loadEventFired('✔ Prefetch OK');
this.setState(() => ({ startLoad: true }));
},
error => {
this._loadEventFired(`✘ Prefetch failed (+${Date.now() - this._startTime}ms)`);
console.log(error);
}
);
};
_loadEventFired = event => {
this.setState(state => ({ events: [...state.events, event], showButton: false }));
};
}
const styles = StyleSheet.create({
button: {
maxWidth: 300
},
image: {
backgroundColor: '#eee',
height: 150,
marginTop: 10,
width: 150
}
});
const StaticPrefetchExample = () => <ImagePrefetchExample source={sources.prefetchable} />;
export default StaticPrefetchExample;

View File

@@ -1,30 +0,0 @@
/**
* @flow
*/
// import React from 'react';
import { StyleSheet } from 'react-native';
const createUncachedURI = source => {
const helper = str => `${str}?t=${Date.now()}`;
const uri = typeof source === 'string' ? source : source.uri;
return typeof source === 'string' ? helper(uri) : { ...source, uri: helper(uri) };
};
const styles = StyleSheet.create({
base: {
height: 200,
width: 300
},
row: {
flexDirection: 'row'
},
centerRow: {
alignItems: 'center'
},
marginTop: {
marginTop: '1rem'
}
});
export { createUncachedURI, styles };

View File

@@ -1,44 +0,0 @@
import placeholder from './placeholder.jpg';
import staticImage from './ladybug.jpg';
const dataPng =
'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>';
const sources = {
animatedGif: {
uri:
'http://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif'
},
broken: { uri: 'http://TYPO_ERROR.github.io/image.png' },
small: {
uri:
'https://images.unsplash.com/photo-1488584585634-35fc98ccb808?dpr=1&auto=format&fit=crop&w=100&h=66&q=60&cs=tinysrgb'
},
smallAlt: {
uri:
'https://images.unsplash.com/photo-1481595357459-84468f6eeaac?dpr=1&auto=format&fit=crop&w=100&h=66&q=60&cs=tinysrgb'
},
large: {
uri:
'https://images.unsplash.com/photo-1481595357459-84468f6eeaac?dpr=1&auto=format&fit=crop&w=376&h=251&q=60&cs=tinysrgb'
},
largeAlt: {
uri:
'https://images.unsplash.com/photo-1471145653077-54c6f0aae511?dpr=1&auto=format&fit=crop&w=376&h=251&q=60&cs=tinysrgb'
},
placeholder,
prefetchable: {
uri:
'https://images.unsplash.com/photo-1471145653077-54c6f0aae511?dpr=1&auto=format&fit=crop&w=376&h=251&q=60&cs=tinysrgb'
},
static: staticImage,
huge: {
uri: 'https://upload.wikimedia.org/wikipedia/commons/d/d7/Chestnut-mandibled_Toucan.jpg'
},
dataPng,
dataSvg
};
export default sources;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,68 +0,0 @@
/**
* @flow
*/
import PropColor from './examples/PropColor';
import CustomSize from './examples/CustomSize';
import PropIndeterminate from './examples/PropIndeterminate';
import PropProgress from './examples/PropProgress';
import PropTrackColor from './examples/PropTrackColor';
import React from 'react';
import UIExplorer, { Description, DocItem, Section, storiesOf } from '../../ui-explorer';
const ProgressBarScreen = () => (
<UIExplorer title="ProgressBar" url="1-components/ProgressBar">
<Description>Display an activity progress bar</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
description="Color of the progress bar."
example={{
render: () => <PropColor />
}}
name="color"
typeInfo="?string = #1976D2"
/>
<DocItem
description="Whether the progress bar will show indeterminate progress."
example={{
render: () => <PropIndeterminate />
}}
name="indeterminate"
typeInfo="?boolean = true"
/>
<DocItem
description="The progress value (between 0 and 1)."
example={{
render: () => <PropProgress />
}}
name="progress"
typeInfo="?number"
/>
<DocItem
description="Color of the track bar."
example={{
render: () => <PropTrackColor />
}}
name="trackColor"
typeInfo="?string = 'transparent'"
/>
</Section>
<Section title="More examples">
<DocItem
description="Custom sizes can be created using styles"
example={{
code: '<ProgressBar style={{ borderRadius: 10, height: 10 }} trackColor="#D1E3F6" />',
render: () => <CustomSize />
}}
/>
</Section>
</UIExplorer>
);
storiesOf('Components', module).add('ProgressBar', ProgressBarScreen);

View File

@@ -1,28 +0,0 @@
/**
* @flow
*/
import { DividerVertical } from '../helpers';
import React from 'react';
import { ProgressBar, StyleSheet, View } from 'react-native';
const ProgressBarCustomSizeExample = () => (
<View>
<ProgressBar color="#1DA1F2" progress={0.33} style={styles.one} trackColor="#D1E3F6" />
<DividerVertical />
<ProgressBar color="#1DA1F2" progress={0.33} style={styles.two} trackColor="#D1E3F6" />
</View>
);
const styles = StyleSheet.create({
one: {
borderRadius: 10,
height: 10
},
two: {
borderRadius: 10,
height: 20
}
});
export default ProgressBarCustomSizeExample;

View File

@@ -1,23 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { DividerVertical } from '../helpers';
import { ProgressBar, View } from 'react-native';
const ProgressBarColorExample = () => (
<View>
<ProgressBar color="#1DA1F2" progress={0.2} />
<DividerVertical />
<ProgressBar color="#17BF63" progress={0.4} />
<DividerVertical />
<ProgressBar color="#F45D22" progress={0.6} />
<DividerVertical />
<ProgressBar color="#794BC4" progress={0.8} />
<DividerVertical />
<ProgressBar color="#E0245E" progress={1.0} />
</View>
);
export default ProgressBarColorExample;

View File

@@ -1,10 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { ProgressBar } from 'react-native';
const ProgressBarIndeterminateExample = () => <ProgressBar indeterminate trackColor="#D1E3F6" />;
export default ProgressBarIndeterminateExample;

View File

@@ -1,48 +0,0 @@
/**
* @flow
*/
import { DividerVertical } from '../helpers';
import React, { PureComponent } from 'react';
import { ProgressBar, StyleSheet, View } from 'react-native';
class ProgressBarProgressExample extends PureComponent {
state = { progress: 0 };
componentDidMount() {
this._updateProgress();
}
componentWillUnmount() {
window.cancelAnimationFrame(this._frame);
}
render() {
return (
<View>
<ProgressBar color="#794BC4" progress={0.2} style={styles.progress} />
<DividerVertical />
<ProgressBar color="#794BC4" progress={this._getProgress(0.2)} style={styles.progress} />
</View>
);
}
_getProgress(offset) {
const progress = this.state.progress + offset;
return Math.sin(progress % Math.PI) % 1;
}
_updateProgress() {
const progress = this.state.progress + 0.01;
this.setState(() => ({ progress }));
this._frame = window.requestAnimationFrame(() => this._updateProgress());
}
}
const styles = StyleSheet.create({
progress: {
minWidth: 200
}
});
export default ProgressBarProgressExample;

View File

@@ -1,19 +0,0 @@
/**
* @flow
*/
import { DividerVertical } from '../helpers';
import React from 'react';
import { ProgressBar, View } from 'react-native';
const ProgressBarTrackColorExample = () => (
<View>
<ProgressBar color="#1DA1F2" progress={0.1} trackColor="#17BF63" />
<DividerVertical />
<ProgressBar color="#1DA1F2" progress={0.2} trackColor="#F45D22" />
<DividerVertical />
<ProgressBar color="#1DA1F2" progress={0.3} trackColor="#794BC4" />
</View>
);
export default ProgressBarTrackColorExample;

View File

@@ -1,20 +0,0 @@
/**
* @flow
*/
import React from 'react';
import { StyleSheet, View } from 'react-native';
const DividerHorizontal = () => <View style={styles.horizontalDivider} />;
const DividerVertical = () => <View style={styles.verticalDivider} />;
const styles = StyleSheet.create({
horizontalDivider: {
width: '0.6rem'
},
verticalDivider: {
height: '1.3125rem'
}
});
export { DividerHorizontal, DividerVertical };

View File

@@ -1,172 +0,0 @@
/* eslint-disable react/jsx-no-bind, react/jsx-sort-props */
/**
* @flow
*/
import { HorizontalExample } from './examples/Horizontal';
import ScrollToExample from './examples/ScrollTo';
import ScrollToEndExample from './examples/ScrollToEnd';
import React from 'react';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
storiesOf,
TextList
} from '../../ui-explorer';
const ScrollViewScreen = () => (
<UIExplorer title="ScrollView" url="1-components/ScrollView">
<Description>
<AppText>
A scrollable <Code>View</Code> that provides itegration with the touch-locking responder
system. <Code>ScrollView</Code>'s must have a bounded height: either set the height of the
view directly (discouraged) or make sure all parent views have bounded height (e.g.,
transfer <Code>{'{ flex: 1}'}</Code> down the view stack).
</AppText>
</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
name="contentContainerStyle"
typeInfo="?style"
description="These styles will be applied to the scroll view content container which wraps all of the child views."
/>
<DocItem
name="horizontal"
typeInfo="?boolean = false"
description="When true, the scroll view's children are arranged horizontally in a row instead of vertically in a column."
example={{
render: () => <HorizontalExample />
}}
/>
<DocItem
name="keyboardDismissMode"
typeInfo="?enum('none', 'on-drag') = 'none'"
description={[
<AppText>
Determines whether the keyboard gets dismissed in response to a scroll drag.
</AppText>,
<TextList
items={[
<AppText>
<Code>none</Code> (the default): drags do not dismiss the keyboard.
</AppText>,
<AppText>
<Code>on-drag</Code>: the keyboard is dismissed when a drag begins.
</AppText>,
<AppText>
<Code>interactive</Code> (not supported on web; same as <Code>none</Code>)
</AppText>
]}
/>
]}
/>
<DocItem
name="onContentSizeChange"
typeInfo="?function"
description={
<AppText>
Called when scrollable content view of the <Code>ScrollView</Code> changes. It's
implemented using the <Code>onLayout</Code> handler attached to the content container
which this <Code>ScrollView</Code> renders.
</AppText>
}
/>
<DocItem
name="onScroll"
typeInfo="?function"
description={[
<AppText>
Fires at most once per frame during scrolling. The frequency of the events can be
contolled using the <Code>scrollEventThrottle</Code> prop.
</AppText>,
<AppText>Invoked on scroll with the following event:</AppText>,
<Code>{`{
nativeEvent: {
contentOffset: { x, y },
contentSize: { height, width },
layoutMeasurement: { height, width }
}
}`}</Code>
]}
/>
<DocItem
name="scrollEnabled"
typeInfo="?boolean = true"
description="When false, the content does not scroll."
/>
<DocItem
name="scrollEventThrottle"
typeInfo="?number = 0"
description={
<AppText>
This controls how often the scroll event will be fired while scrolling (as a time
interval in ms). A lower number yields better accuracy for code that is tracking the
scroll position, but can lead to scroll performance problems. The default value is{' '}
<Code>0</Code>, which means the scroll event will be sent only once each time the view
is scrolled.
</AppText>
}
/>
</Section>
<Section title="Instance methods">
<DocItem
name="getInnerViewNode"
typeInfo="() => node"
description="Returns a reference to the underlying content container DOM node within the ScrollView."
/>
<DocItem
name="getScrollableNode"
typeInfo="() => node"
description="Returns a reference to the underlying scrollable DOM node."
/>
<DocItem
name="getScrollResponder"
typeInfo="() => ScrollResponder"
description={
<AppText>
Returns a reference to the underlying scroll responder, which supports operations like{' '}
<Code>scrollTo</Code>. All <Code>ScrollView</Code>-like components should implement this
method so that they can be composed while providing access to the underlying scroll
responder's methods.
</AppText>
}
/>
<DocItem
name="scrollTo"
typeInfo="(options: { x: number = 0; y: number = 0; animated: boolean = true }) => void"
description="Scrolls to a given `x`, `y` offset (animation is not currently supported)."
example={{
render: () => <ScrollToExample />
}}
/>
<DocItem
name="scrollToEnd"
typeInfo="(options: { animated: boolean = true }) => void"
description="Scrolls to the end of the scroll view."
example={{
render: () => <ScrollToEndExample />
}}
/>
</Section>
</UIExplorer>
);
storiesOf('Components', module).add('ScrollView', ScrollViewScreen);

View File

@@ -1,68 +0,0 @@
/* eslint-disable react/jsx-no-bind */
/**
* @flow
*/
import React from 'react';
import { ScrollView, StyleSheet, Text, View } from 'react-native';
const onScroll = () => {
console.log('ScrollView.onScroll');
};
const VerticalExample = () => (
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
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 HorizontalExample = () => (
<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>
);
export { HorizontalExample, VerticalExample };
const styles = StyleSheet.create({
box: {
flexGrow: 1,
justifyContent: 'center',
borderWidth: 1
},
scrollViewContainer: {
height: 200,
width: 300
},
scrollViewStyle: {
borderWidth: 1
},
scrollViewContentContainerStyle: {
backgroundColor: '#eee',
padding: 10
}
});

View File

@@ -1,61 +0,0 @@
/* eslint-disable react/jsx-no-bind */
/**
* @flow
*/
import React, { PureComponent } from 'react';
import { Button, ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native';
export default class ScrollToExample extends PureComponent {
render() {
return (
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
ref={scrollview => {
this.scrollview = scrollview;
}}
scrollEventThrottle={16} // ~60 events per second
style={styles.scrollViewStyle}
>
{Array.from({ length: 50 }).map((item, i) => (
<TouchableHighlight
key={i}
onPress={() => {}}
style={[styles.box, styles.horizontalBox]}
>
<Text>{i}</Text>
</TouchableHighlight>
))}
</ScrollView>
<Button
onPress={() => {
this.scrollview.scrollTo({ y: 100 });
}}
title="Scroll to 100px"
/>
</View>
);
}
}
const styles = StyleSheet.create({
box: {
flexGrow: 1,
justifyContent: 'center',
borderWidth: 1
},
scrollViewContainer: {
height: 150,
width: 300
},
scrollViewStyle: {
borderWidth: 1,
marginBottom: '1.3125rem'
},
scrollViewContentContainerStyle: {
backgroundColor: '#eee',
padding: 10
}
});

View File

@@ -1,57 +0,0 @@
/* eslint-disable react/jsx-no-bind */
/**
* @flow
*/
import React, { PureComponent } from 'react';
import { Button, ScrollView, StyleSheet, Text, View } from 'react-native';
export default class ScrollToEndExample extends PureComponent {
render() {
return (
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
ref={scrollview => {
this.scrollview = scrollview;
}}
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>
<Button
onPress={() => {
this.scrollview.scrollToEnd();
}}
title="Scroll to end"
/>
</View>
);
}
}
const styles = StyleSheet.create({
box: {
flexGrow: 1,
justifyContent: 'center',
borderWidth: 1
},
scrollViewContainer: {
height: 150,
width: 300
},
scrollViewStyle: {
borderWidth: 1,
marginBottom: '1.3125rem'
},
scrollViewContentContainerStyle: {
backgroundColor: '#eee',
padding: 10
}
});

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