Compare commits

...

16 Commits

Author SHA1 Message Date
Nicolas Gallagher
a35949fa71 0.0.115 2017-07-26 19:25:31 -07:00
Nicolas Gallagher
f5ac856c2d Revert unrelated changes to normalizeNativeEvent 2017-07-26 19:25:18 -07:00
Nicolas Gallagher
4b557b1e0b Fix canuse-api installation 2017-07-26 19:20:34 -07:00
Nicolas Gallagher
d4e9d9d256 0.0.114 2017-07-26 16:17:28 -07:00
Nicolas Gallagher
0ff3e91592 [fix] AppState support for Android browser 4.4 2017-07-26 15:52:11 -07:00
Nicolas Gallagher
092d5d12f7 [fix] unitless values for vendor prefixed properties
Problem:

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

Solution:

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

Close #563
2017-07-13 17:10:38 -07:00
Nicolas Gallagher
3677f0dd57 Use more components to build docs 2017-07-11 20:18:52 -07:00
68 changed files with 5593 additions and 3015 deletions

View File

@@ -66,7 +66,7 @@ yarn docs:build
To run the performance benchmarks in a browser (opening `./benchmarks/index.html`):
```
yarn benchmarks
yarn benchmark
```
## Compile and build

View File

@@ -19,8 +19,9 @@ Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
touch handling to the Web.
Browse the [UI Explorer](https://necolas.github.io/react-native-web/storybook/)
to see React Native examples running on Web. Or try it out online with [React
Native for Web: Playground](https://www.webpackbin.com/bins/-KgucwxRbn7HRU-V-3Bc).
to see React Native examples running on Web. Or remix the [React Native for
Web: Playground](https://glitch.com/edit/#!/react-native-web-playground) on
Glitch.
## Quick start
@@ -91,12 +92,15 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
## 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-player](https://github.com/dabbott/react-native-web-player)
## Start kits
* [create-react-app](https://github.com/facebookincubator/create-react-app) ([on Glitch](https://glitch.com/edit/#!/react-native-web-playground))
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
* [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack)
* [react-sketchapp](https://github.com/airbnb/react-sketchapp)
* [react-web](https://github.com/taobaofed/react-web)
* [reactxp](https://github.com/microsoft/reactxp)
## License

View File

@@ -7,6 +7,9 @@ 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
@@ -24,24 +27,25 @@ Typical render timings*: mean ± two standard deviations.
| Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `94.96` 31.01` | `200.43` 38.90` | |
| `react-native-web/stylesheet@0.0.107` | `98.58` `±10.83` | `218.59` 36.52` | |
| `react-native-web@0.0.107` | `117.45` `±18.76` | `288.27` `±33.50` | `15.10` 5.45ms` |
| `css-modules` | `84.19` 14.69` | `183.37` 22.98` | |
| `react-native-web/stylesheet@0.0.113` | `88.83` `±14.31` | `185.54` 24.62` | |
| `react-native-web@0.0.113` | `110.45` `±19.63` | `251.53` `±32.52` | `15.52` 7.93ms` |
Other libraries
| Implementation | Deep tree (ms) | Wide tree (ms) |
| :--- | ---: | ---: |
| `styletron@2.5.1` | `90.38` `±15.15` | `197.40` 29.02` |
| `aphrodite@1.2.0` | `88.65` `±19.62` | `187.35` 24.60` |
| `glamor@3.0.0-1` | `145.64` 21.93` | `283.60` 23.26` |
| `react-jss@5.4.1` | `143.17` 19.14` | `361.80` 33.39` |
| `reactxp@0.34.3` | `227.18` 28.75` | `496.08` 59.96` |
| `styled-components@2.1.0` | `262.85` 46.12` | `578.43` 35.86` |
| `styled-components/primitives@2.1.0` | `261.43` 44.14` | `569.65` 22.19` |
| `aphrodite@1.2.3` | `84.68` `±18.80` | `180.62` 41.98` |
| `styletron@2.5.1` | `83.93` `±13.10` | `185.96` `±45.65` |
| `react-jss@7.0.1` | `174.75` 30.87` | `411.77` 83.83` |
| `glamor@3.0.0-3` | `255.21` 45.68` | `545.74` 107.79` |
| `reactxp@0.34.3` | `237.46` 36.72` | `514.48` 84.87` |
| `styled-components@2.1.1` | `266.91` 50.04` | `598.29` 95.13` |
| `styled-components/primitives@2.1.1` | `266.62` 50.39` | `567.13` 68.12` |
| `radium@0.19.1` | `518.48` `±69.74` | `1058.85` `±120.85` |
These results indicate that render performance is not a significant
differentiating factor between `aphrodite`, `styletron`, and
`react-native-web/stylesheet`.
These results indicate that styled 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

@@ -2,6 +2,7 @@ import aphrodite from './src/aphrodite';
import cssModules from './src/css-modules';
import glamor from './src/glamor';
import jss from './src/jss';
import radium from './src/radium';
import reactNative from './src/react-native';
import reactNativeStyleSheet from './src/react-native-stylesheet';
import styledComponents from './src/styled-components';
@@ -14,6 +15,7 @@ import renderTweet from './tests/renderTweet';
import renderWideTree from './tests/renderWideTree';
const testAll = window.location.search === '?all';
const testFastest = window.location.search === '?fastest';
const coreTests = [
() => renderTweet('react-native-web', reactNative),
@@ -26,18 +28,23 @@ const coreTests = [
() => renderWideTree('react-native-web', reactNative)
];
/**
* Optionally run tests using other libraries
*/
const extraTests = [
const fastestTests = [
() => renderDeepTree('styletron', styletron),
() => renderWideTree('styletron', styletron),
() => renderDeepTree('aphrodite', aphrodite),
() => renderWideTree('aphrodite', aphrodite),
() => renderWideTree('aphrodite', aphrodite)
];
/**
* Optionally run tests using other libraries
*/
const restTests = [
() => renderDeepTree('glamor', glamor),
() => renderWideTree('glamor', glamor),
() => renderDeepTree('react-jss', jss),
() => renderWideTree('react-jss', jss),
() => renderDeepTree('radium', radium),
() => renderWideTree('radium', radium),
() => renderDeepTree('reactxp', xp),
() => renderWideTree('reactxp', xp),
() => renderDeepTree('styled-components', styledComponents),
@@ -46,7 +53,14 @@ const extraTests = [
() => renderWideTree('styled-components/primitives', styledComponentsPrimitives)
];
const tests = testAll ? coreTests.concat(extraTests) : coreTests;
const tests = [...coreTests];
if (testFastest) {
tests.push(...fastestTests);
}
if (testAll) {
tests.push(...fastestTests);
tests.push(...restTests);
}
// run benchmarks
tests.reduce((promise, test) => promise.then(test()), Promise.resolve());

View File

@@ -1,12 +1,13 @@
{
"name": "performance",
"name": "benchmarks",
"private": true,
"dependencies": {
"aphrodite": "^1.2.1",
"aphrodite": "^1.2.3",
"classnames": "^2.2.5",
"glamor": "^3.0.0-2",
"glamor": "^3.0.0-3",
"marky": "^1.2.0",
"react-jss": "^7.0.0-pre.1",
"radium": "^0.19.1",
"react-jss": "^7.0.1",
"react-primitives": "^0.4.3",
"reactxp": "0.34.3",
"styled-components": "^2.1.1",

View File

@@ -0,0 +1,48 @@
/* 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

@@ -0,0 +1,31 @@
/* 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);

9
benchmarks/src/radium.js Normal file
View File

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

View File

@@ -37,9 +37,9 @@ ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
aphrodite@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/aphrodite/-/aphrodite-1.2.1.tgz#a7b5066b198730be7b7a88f78dbefd77d4df5683"
aphrodite@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/aphrodite/-/aphrodite-1.2.3.tgz#4b161e9eef319b1f90a889501f985d7b5e70b285"
dependencies:
asap "^2.0.3"
inline-style-prefixer "^3.0.1"
@@ -55,6 +55,10 @@ array-find-index@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
array-find@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8"
asap@^2.0.3, asap@^2.0.5, asap@~2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"
@@ -84,7 +88,7 @@ babel-code-frame@^6.11.0:
esutils "^2.0.2"
js-tokens "^3.0.0"
babel-runtime@^6.18.0, babel-runtime@^6.23.0:
babel-runtime@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
dependencies:
@@ -95,6 +99,10 @@ balanced-match@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
base64-js@^1.0.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
@@ -107,6 +115,13 @@ bowser@^1.0.0, bowser@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.6.0.tgz#37fc387b616cb6aef370dab4d6bd402b74c5c54d"
brace-expansion@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
brcast@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brcast/-/brcast-2.0.1.tgz#4311508f0634a6f5a2465b6cf2db27f06902aaca"
@@ -208,11 +223,9 @@ colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
common-tags@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0"
dependencies:
babel-runtime "^6.18.0"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
core-js@^1.0.0:
version "1.2.7"
@@ -389,6 +402,10 @@ esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
exenv@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
fast-deep-equal@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-0.1.0.tgz#5c6f4599aba6b333ee3342e2ed978672f1001f8d"
@@ -417,18 +434,33 @@ flexibility@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/flexibility/-/flexibility-2.0.1.tgz#ad323aafc40f469ce624286518fc4d7cd72b7c77"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
function-bind@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
glamor@^3.0.0-2:
version "3.0.0-2"
resolved "https://registry.yarnpkg.com/glamor/-/glamor-3.0.0-2.tgz#cb28eb450a437c63c9911421a4bb74711c473dad"
glamor@^3.0.0-3:
version "3.0.0-3"
resolved "https://registry.yarnpkg.com/glamor/-/glamor-3.0.0-3.tgz#62e1cf2ce70a4db0b247a5f95d4b7d6a89ac83c9"
dependencies:
fbjs "^0.8.12"
inline-style-prefixer "^3.0.3"
react-css-property-operations "^15.4.1"
glob@^7.0.5:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@@ -487,7 +519,14 @@ indexes-of@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
inherits@2.0.1:
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
@@ -498,14 +537,7 @@ inline-style-prefixer@^2.0.1, inline-style-prefixer@^2.0.5:
bowser "^1.0.0"
hyphenate-style-name "^1.0.1"
inline-style-prefixer@^3.0.1:
version "3.0.2"
resolved "https://artifactory.twitter.biz:443/api/npm/js-virtual/inline-style-prefixer/-/inline-style-prefixer-3.0.2.tgz#989865e0c5de7a946acbea71e16e02741efe0dd7"
dependencies:
bowser "^1.6.0"
css-in-js-utils "^1.0.3"
inline-style-prefixer@^3.0.3, inline-style-prefixer@^3.0.6:
inline-style-prefixer@^3.0.1, inline-style-prefixer@^3.0.3, inline-style-prefixer@^3.0.6:
version "3.0.6"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-3.0.6.tgz#b27fe309b4168a31eaf38c8e8c60ab9e7c11731f"
dependencies:
@@ -660,9 +692,9 @@ jss-vendor-prefixer@^6.0.0:
dependencies:
css-vendor "^0.3.8"
jss@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/jss/-/jss-8.0.0.tgz#7b6e3153a5045d396245adc3fad5817d00c59457"
jss@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/jss/-/jss-8.1.0.tgz#b32f15efcce22446dfda4c2be09a04f38431da0a"
dependencies:
is-in-browser "^1.0.2"
warning "^3.0.0"
@@ -709,6 +741,12 @@ math-expression-evaluator@^1.2.14:
version "1.2.16"
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
brace-expansion "^1.1.7"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -751,6 +789,16 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
wrappy "1"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
postcss-calc@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
@@ -1025,6 +1073,16 @@ query-string@^4.1.0:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
radium@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/radium/-/radium-0.19.1.tgz#0fbcc6d0ea5526bdb1e5e5371cad386775a3eec8"
dependencies:
array-find "^1.0.0"
exenv "^1.2.1"
inline-style-prefixer "^2.0.5"
prop-types "^15.5.8"
rimraf "^2.6.1"
react-addons-perf@^15.4.2:
version "15.4.2"
resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.4.2.tgz#110bdcf5c459c4f77cb85ed634bcd3397536383b"
@@ -1036,16 +1094,15 @@ react-css-property-operations@^15.4.1:
version "15.4.1"
resolved "https://registry.yarnpkg.com/react-css-property-operations/-/react-css-property-operations-15.4.1.tgz#4c0e305df4cc35f0f5fd2d65a79214c8b012db35"
react-jss@^7.0.0-pre.1:
version "7.0.0-pre.1"
resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-7.0.0-pre.1.tgz#948127be53cd8c9fbd2362e87c1f4a93382aeb26"
react-jss@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-7.0.1.tgz#36c505c3798993edd46ea01734f171f895348e25"
dependencies:
common-tags "^1.4.0"
hoist-non-react-statics "^1.2.0"
jss "^8.0.0"
jss "^8.1.0"
jss-preset-default "^3.0.0"
prop-types "^15.5.8"
theming "^1.0.1"
theming "^1.1.0"
react-native-web@0.0.x:
version "0.0.106"
@@ -1148,6 +1205,12 @@ regjsparser@^0.1.4:
dependencies:
jsesc "~0.5.0"
rimraf@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
dependencies:
glob "^7.0.5"
sax@~1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
@@ -1267,9 +1330,9 @@ synctasks@^0.2.9:
version "0.2.17"
resolved "https://registry.yarnpkg.com/synctasks/-/synctasks-0.2.17.tgz#38852f008878de2e941b6e458ddf552245268da1"
theming@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/theming/-/theming-1.0.1.tgz#a3838c9de635e2f29fc9cd3dea4bf68d3c5a650a"
theming@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/theming/-/theming-1.1.0.tgz#0562760b55a1b919c2d5eeb94130351f8958e13a"
dependencies:
brcast "^2.0.0"
is-function "^1.0.1"
@@ -1318,3 +1381,7 @@ whatwg-fetch@>=0.10.0:
whet.extend@~0.9.9:
version "0.9.9"
resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"

13
docs/package.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "docs",
"private": true,
"scripts": {
"build": "yarn && build-storybook -o ./dist -c ./storybook/.storybook",
"start": "start-storybook -p 9001 -c ./storybook/.storybook --dont-track",
"publish": "yarn build && git checkout gh-pages && rm -rf ./storybook && mv docs/dist storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"@kadira/storybook": "^2.35.3",
"@kadira/storybook-addon-options": "^1.0.2"
}
}

View File

@@ -2,7 +2,7 @@ import { setOptions } from '@kadira/storybook-addon-options';
import centered from './decorator-centered';
import { configure, addDecorator } from '@kadira/storybook';
const context = require.context('../', true, /Docs\.js$/);
const context = require.context('../', true, /Screen\.js$/);
addDecorator(centered);

View File

@@ -10,13 +10,13 @@ import PropHidesWhenStopped from './examples/PropHidesWhenStopped';
import PropSize from './examples/PropSize';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { DocItem } from '../../ui-explorer';
import UIExplorer, { Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...View props" />,
const ActivityIndicatorScreen = () =>
<UIExplorer title="ActivityIndicator" url="components/ActivityIndicator">
<Description>Displays a customizable activity indicator</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
name="animating"
@@ -25,7 +25,7 @@ const sections = [
example={{
render: () => <PropAnimating />
}}
/>,
/>
<DocItem
name="color"
@@ -34,7 +34,7 @@ const sections = [
example={{
render: () => <PropColor />
}}
/>,
/>
<DocItem
name="hidesWhenStopped"
@@ -43,7 +43,7 @@ const sections = [
example={{
render: () => <PropHidesWhenStopped />
}}
/>,
/>
<DocItem
name="size"
@@ -53,15 +53,7 @@ const sections = [
render: () => <PropSize />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('ActivityIndicator', () =>
<UIExplorer
description="Displays a customizable activity indicator"
sections={sections}
title="ActivityIndicator"
url="components/ActivityIndicator"
/>
);
storiesOf('Components', module).add('ActivityIndicator', ActivityIndicatorScreen);

View File

@@ -8,18 +8,24 @@ import React from 'react';
import PropColor from './examples/PropColor';
import PropDisabled from './examples/PropDisabled';
import PropOnPress from './examples/PropOnPress';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
import { storiesOf } from '@kadira/storybook';
const sections = [
{
title: 'Props',
entries: [
const ButtonScreen = () =>
<UIExplorer title="Button" url="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"
@@ -28,16 +34,16 @@ const sections = [
example={{
render: () => <PropColor />
}}
/>,
/>
<DocItem
name="disabled"
typeInfo="?boolean"
description="If `true`, disable all interactions for this element."
description="If true, disable all interactions for this element."
example={{
render: () => <PropDisabled />
}}
/>,
/>
<DocItem
name="onPress"
@@ -46,30 +52,16 @@ const sections = [
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', () =>
<UIExplorer
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>
]}
sections={sections}
title="Button"
url="components/Button"
/>
);
storiesOf('Components', module).add('Button', ButtonScreen);

View File

@@ -17,13 +17,17 @@ import PropSource from './examples/PropSource';
import StaticGetSizeExample from './examples/StaticGetSize';
import StaticPrefetchExample from './examples/StaticPrefetch';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...View props" />,
const ImageScreen = () =>
<UIExplorer title="Image" url="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"
@@ -32,34 +36,44 @@ const sections = [
example={{
render: () => <PropChildren />
}}
/>,
/>
<DocItem
name="defaultSource"
typeInfo="?object"
description="An image to display as a placeholder while downloading the final image off the network. `{ uri: string, width, height }`"
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="Invoked on load error with `{nativeEvent: {error}}`."
description={
<AppText>
Invoked on load error with <Code>{'{nativeEvent: {error}}'}</Code>.
</AppText>
}
example={{
render: () => <PropOnError />
}}
/>,
/>
<DocItem
name="onLoad"
@@ -68,7 +82,7 @@ const sections = [
example={{
render: () => <PropOnLoad />
}}
/>,
/>
<DocItem
name="onLoadEnd"
@@ -77,7 +91,7 @@ const sections = [
example={{
render: () => <PropOnLoadEnd />
}}
/>,
/>
<DocItem
name="onLoadStart"
@@ -86,7 +100,7 @@ const sections = [
example={{
render: () => <PropOnLoadStart />
}}
/>,
/>
<DocItem
name="resizeMode"
@@ -95,25 +109,27 @@ const sections = [
example={{
render: () => <PropResizeMode />
}}
/>,
/>
<DocItem
name="source"
typeInfo="?object"
description="`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 }`"
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={{
code: '',
render: () => <PropSource />
}}
/>,
/>
<DocItem name="style" typeInfo="?style" />
]
},
</Section>
{
title: 'Properties',
entries: [
<Section title="Properties">
<DocItem
name="static resizeMode"
typeInfo="object"
@@ -121,20 +137,28 @@ const sections = [
code: '<Image resizeMode={Image.resizeMode.contain} />'
}}
/>
]
},
</Section>
{
title: 'Methods',
entries: [
<Section title="Methods">
<DocItem
name="static getSize"
typeInfo="(uri: string, success: (width, height) => {}, failure: function) => void"
description="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.\n\n(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.)"
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"
@@ -144,15 +168,7 @@ const sections = [
render: () => <StaticPrefetchExample />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('Image', () =>
<UIExplorer
description="An accessibile image component with support for image resizing, default image, and child content."
sections={sections}
title="Image"
url="components/Image"
/>
);
storiesOf('Components', module).add('Image', ImageScreen);

View File

@@ -9,13 +9,13 @@ import PropProgress from './examples/PropProgress';
import PropTrackColor from './examples/PropTrackColor';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { DocItem } from '../../ui-explorer';
import UIExplorer, { Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...View props" />,
const ProgressBarScreen = () =>
<UIExplorer title="ProgressBar" url="components/ProgressBar">
<Description>Display an activity progress bar</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
description="Color of the progress bar."
@@ -24,7 +24,7 @@ const sections = [
}}
name="color"
typeInfo="?string = #1976D2"
/>,
/>
<DocItem
description="Whether the progress bar will show indeterminate progress."
@@ -33,7 +33,7 @@ const sections = [
}}
name="indeterminate"
typeInfo="?boolean = true"
/>,
/>
<DocItem
description="The progress value (between 0 and 1)."
@@ -42,7 +42,7 @@ const sections = [
}}
name="progress"
typeInfo="?number"
/>,
/>
<DocItem
description="Color of the track bar."
@@ -52,11 +52,9 @@ const sections = [
name="trackColor"
typeInfo="?string = 'transparent'"
/>
]
},
{
title: 'More examples',
entries: [
</Section>
<Section title="More examples">
<DocItem
description="Custom sizes can be created using styles"
example={{
@@ -64,15 +62,7 @@ const sections = [
render: () => <CustomSize />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('ProgressBar', () =>
<UIExplorer
description="Display an activity progress bar"
sections={sections}
title="ProgressBar"
url="components/ProgressBar"
/>
);
storiesOf('Components', module).add('ProgressBar', ProgressBarScreen);

View File

@@ -9,19 +9,34 @@ import ScrollToExample from './examples/ScrollTo';
import ScrollToEndExample from './examples/ScrollToEnd';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem, TextList } from '../../ui-explorer';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
TextList
} from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...View props" />,
const ScrollViewScreen = () =>
<UIExplorer title="ScrollView" url="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"
@@ -30,7 +45,7 @@ const sections = [
example={{
render: () => <HorizontalExample />
}}
/>,
/>
<DocItem
name="keyboardDismissMode"
@@ -53,7 +68,7 @@ const sections = [
]}
/>
]}
/>,
/>
<DocItem
name="onContentSizeChange"
@@ -65,7 +80,7 @@ const sections = [
which this <Code>ScrollView</Code> renders.
</AppText>
}
/>,
/>
<DocItem
name="onScroll"
@@ -84,13 +99,13 @@ const sections = [
}
}`}</Code>
]}
/>,
/>
<DocItem
name="scrollEnabled"
typeInfo="?boolean = true"
description="When false, the content does not scroll."
/>,
/>
<DocItem
name="scrollEventThrottle"
@@ -105,22 +120,20 @@ const sections = [
</AppText>
}
/>
]
},
{
title: 'Instance methods',
entries: [
</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"
@@ -133,7 +146,7 @@ const sections = [
responder's methods.
</AppText>
}
/>,
/>
<DocItem
name="scrollTo"
@@ -142,7 +155,7 @@ const sections = [
example={{
render: () => <ScrollToExample />
}}
/>,
/>
<DocItem
name="scrollToEnd"
@@ -152,22 +165,7 @@ const sections = [
render: () => <ScrollToEndExample />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('ScrollView', () =>
<UIExplorer
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>
}
sections={sections}
title="ScrollView"
url="components/ScrollView"
/>
);
storiesOf('Components', module).add('ScrollView', ScrollViewScreen);

View File

@@ -5,7 +5,7 @@
*/
import React, { PureComponent } from 'react';
import { Button, ScrollView, StyleSheet, Text, View } from 'react-native';
import { Button, ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native';
export default class ScrollToExample extends PureComponent {
render() {
@@ -20,11 +20,15 @@ export default class ScrollToExample extends PureComponent {
style={styles.scrollViewStyle}
>
{Array.from({ length: 50 }).map((item, i) =>
<View key={i} style={[styles.box, styles.horizontalBox]}>
<TouchableHighlight
key={i}
onPress={() => {}}
style={[styles.box, styles.horizontalBox]}
>
<Text>
{i}
</Text>
</View>
</TouchableHighlight>
)}
</ScrollView>
<Button

View File

@@ -14,13 +14,21 @@ import PropTrackColor from './examples/PropTrackColor';
import PropValue from './examples/PropValue';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...View props" />,
const SwitchScreen = () =>
<UIExplorer title="Switch" url="components/Switch">
<Description>
<AppText>
This is a controlled component that requires an <Code>onValueChange</Code> callback that
updates the value prop in order for the component to reflect user actions. If the{' '}
<Code>value</Code> prop is not updated, the component will continue to render the supplied{' '}
<Code>value</Code> prop instead of the expected result of any user actions.
</AppText>
</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
description="The color of the thumb grip when the switch is turned on."
@@ -29,7 +37,7 @@ const sections = [
}}
name="activeThumbColor"
typeInfo="?color = #009688"
/>,
/>
<DocItem
description="The color of the track when the switch is turned on."
@@ -38,16 +46,16 @@ const sections = [
}}
name="activeTrackColor"
typeInfo="?color = #A3D3CF"
/>,
/>
<DocItem
description="If `true` the user won't be able to interact with the switch."
description="If true, the user won't be able to interact with the switch."
example={{
render: () => <PropDisabled />
}}
name="disabled"
typeInfo="?boolean = false"
/>,
/>
<DocItem
description="Invoked with the new value when the value changes."
@@ -56,7 +64,7 @@ const sections = [
}}
name="onValueChange"
typeInfo="?function"
/>,
/>
<DocItem
description="The color of the thumb grip when the switch is turned off."
@@ -65,7 +73,7 @@ const sections = [
}}
name="thumbColor"
typeInfo="?color = #FAFAFA"
/>,
/>
<DocItem
description="The color of the track when the switch is turned off."
@@ -74,7 +82,7 @@ const sections = [
}}
name="trackColor"
typeInfo="?color = #939393"
/>,
/>
<DocItem
description="The value of the switch. If `true` the switch will be turned on."
@@ -83,21 +91,21 @@ const sections = [
}}
name="value"
typeInfo="?boolean = false"
/>,
/>
<DocItem
description="(For compatibility with React Native. Equivalent to &quot;activeTrackColor&quot;)"
label="compat"
name="onTintColor"
typeInfo="?color"
/>,
/>
<DocItem
description="(For compatibility with React Native. Equivalent to &quot;trackColor&quot;)"
label="compat"
name="tintColor"
typeInfo="?color"
/>,
/>
<DocItem
description="(For compatibility with React Native. Equivalent to &quot;thumbColor&quot;)"
@@ -105,12 +113,9 @@ const sections = [
name="thumbTintColor"
typeInfo="?color"
/>
]
},
</Section>
{
title: 'More examples',
entries: [
<Section title="More examples">
<DocItem
description="Custom sizes can be created using styles"
example={{
@@ -118,22 +123,7 @@ const sections = [
render: () => <CustomSize />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('Switch', () =>
<UIExplorer
description={
<AppText>
This is a controlled component that requires an <Code>onValueChange</Code> callback that
updates the value prop in order for the component to reflect user actions. If the{' '}
<Code>value</Code> prop is not updated, the component will continue to render the supplied{' '}
<Code>value</Code> prop instead of the expected result of any user actions.
</AppText>
}
sections={sections}
title="Switch"
url="components/Switch"
/>
);
storiesOf('Components', module).add('Switch', SwitchScreen);

View File

@@ -9,7 +9,154 @@ import PropNumberOfLines from './examples/PropNumberOfLines';
import PropOnPress from './examples/PropOnPress';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem, StyleList } from '../../ui-explorer';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
StyleList
} from '../../ui-explorer';
const TextScreen = () =>
<UIExplorer title="Text" url="components/Text">
<Description>
<AppText>
Text is component for displaying text. It supports style, basic touch handling, and inherits
typographic styles from ancestor elements.
</AppText>
<AppText>
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.
</AppText>
<AppText>NOTE: Text will transfer all other props to the rendered HTML element.</AppText>
</Description>
<Section title="Props">
<DocItem
name="accessibilityLabel"
typeInfo="?string"
description="Overrides the text that is read by a screen reader when the user interacts with the element. (This is implemented using 'aria-label'.)"
/>
<DocItem
name="accessibilityLiveRegion"
typeInfo="?enum('assertive', 'none', 'polite')"
description={
<AppText>
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 <Code>polite</Code> (recommended), updates take low priority. When regions
are specified as <Code>assertive</Code>, assistive technologies will interrupt and
immediately notify the user. (This is implemented using 'aria-live'.)
</AppText>
}
/>
<DocItem
label="web"
name="accessibilityRole"
typeInfo="?enum(roles)"
description={
<AppText>
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 <Code>accessibilityRole</Code> of{' '}
<Code>button</Code>. For compatibility with React Native{' '}
<Code>accessibilityTraits</Code> and <Code>accessibilityComponentType</Code> are mapped
to <Code>accessibilityRole</Code>. (This is implemented using ARIA roles.)
</AppText>
}
/>
<DocItem
name="accessible"
typeInfo="?boolean"
description={
<AppText>
When <Code>true</Code>, 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 <Code>accessibilityRole</Code> of <Code>button</Code> and{' '}
<Code>link</Code> are accessible. (This is implemented using 'tabindex'.)
</AppText>
}
/>
<DocItem
name="children"
typeInfo="?any"
description={`Child content. Nested text components will inherit the styles of their parents
(only backgroundColor is inherited from non-Text parents). <Text>
only supports other <Text> and raw text (strings) as children.`}
example={{
render: () => <PropChildren />
}}
/>
<DocItem
name="importantForAccessibility"
typeInfo="?enum('auto', 'no', 'no-hide-descendants', 'yes')"
description={[
<AppText>
A value of <Code>no</Code> will remove the element from the tab flow.
</AppText>,
<AppText>
A value of <Code>no-hide-descendants</Code> will hide the element and its children from
assistive technologies. (This is implemented using 'aria-hidden'.)
</AppText>
]}
/>
<DocItem
name="numberOfLines"
typeInfo="?number"
description="Truncates the text with an ellipsis after this many lines. Currently only supports `1`."
example={{
render: () => <PropNumberOfLines />
}}
/>
<DocItem
name="onLayout"
typeInfo="?function"
description={
<AppText>
Invoked on mount and layout changes with{' '}
<Code>{'{ nativeEvent: { layout: { x, y, width, height } } }'}</Code>, where{' '}
<Code>x</Code> and <Code>y</Code> are the offsets from the parent node.
</AppText>
}
/>
<DocItem
name="onPress"
typeInfo="?function"
description="Called when the Text is pressed"
example={{
render: () => <PropOnPress />
}}
/>
<DocItem
name="selectable"
typeInfo="?boolean"
description="When false, the text is not selectable."
/>
<DocItem
name="style"
typeInfo="?style"
description={<StyleList stylePropTypes={stylePropTypes} />}
/>
<DocItem
name="testID"
typeInfo="?string"
description="Used to locate this view in end-to-end tests. The test ID is rendered to a 'data-testid' DOM attribute"
/>
</Section>
</UIExplorer>;
const stylePropTypes = [
{
@@ -112,142 +259,4 @@ const stylePropTypes = [
}
];
const sections = [
{
title: 'Props',
entries: [
<DocItem
name="accessibilityLabel"
typeInfo="?string"
description="Overrides the text that is read by a screen reader when the user interacts with the element. (This is implemented using 'aria-label'.)"
/>,
<DocItem
name="accessibilityLiveRegion"
typeInfo="?enum('assertive', 'none', 'polite')"
description={
<AppText>
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 <Code>polite</Code> (recommended), updates take low priority. When regions
are specified as <Code>assertive</Code>, assistive technologies will interrupt and
immediately notify the user. (This is implemented using 'aria-live'.)
</AppText>
}
/>,
<DocItem
label="web"
name="accessibilityRole"
typeInfo="?enum(roles)"
description={
<AppText>
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 <Code>accessibilityRole</Code> of{' '}
<Code>button</Code>. For compatibility with React Native{' '}
<Code>accessibilityTraits</Code> and <Code>accessibilityComponentType</Code> are mapped
to <Code>accessibilityRole</Code>. (This is implemented using ARIA roles.)
</AppText>
}
/>,
<DocItem
name="accessible"
typeInfo="?boolean"
description={
<AppText>
When <Code>true</Code>, 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 <Code>accessibilityRole</Code> of <Code>button</Code> and{' '}
<Code>link</Code> are accessible. (This is implemented using 'tabindex'.)
</AppText>
}
/>,
<DocItem
name="children"
typeInfo="?any"
description={`Child content. Nested text components will inherit the styles of their parents
(only backgroundColor is inherited from non-Text parents). <Text>
only supports other <Text> and raw text (strings) as children.`}
example={{
code: '',
render: () => <PropChildren />
}}
/>,
<DocItem
name="importantForAccessibility"
typeInfo="?enum('auto', 'no', 'no-hide-descendants', 'yes')"
description={
'A value of `no` will remove the element from the tab flow.\n\nA value of `no-hide-descendants` will hide the element and its children from assistive technologies. (This is implemented using `aria-hidden`.)'
}
/>,
<DocItem
name="numberOfLines"
typeInfo="?number"
description="Truncates the text with an ellipsis after this many lines. Currently only supports `1`."
example={{
code: '',
render: () => <PropNumberOfLines />
}}
/>,
<DocItem
name="onLayout"
typeInfo="?function"
description="Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width, height } } }`, where `x` and `y` are the offsets from the parent node."
/>,
<DocItem
name="onPress"
typeInfo="?function"
description="Called when the Text is pressed"
example={{
code: '',
render: () => <PropOnPress />
}}
/>,
<DocItem
name="selectable"
typeInfo="?boolean"
description="When `false`, the text is not selectable."
/>,
<DocItem
name="style"
typeInfo="?style"
description={<StyleList stylePropTypes={stylePropTypes} />}
/>,
<DocItem
name="testID"
typeInfo="?string"
description="Used to locate this view in end-to-end tests. The test ID is rendered to a `data-testid` DOM attribute"
/>
]
}
];
storiesOf('Components', module).add('Text', () =>
<UIExplorer
description={[
<AppText>
Text is component for displaying text. It supports style, basic touch handling, and inherits
typographic styles from ancestor elements.
</AppText>,
<AppText>
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.
</AppText>,
<AppText>NOTE: Text will transfer all other props to the rendered HTML element.</AppText>
]}
sections={sections}
title="Text"
url="components/Text"
/>
);
storiesOf('Components', module).add('Text', TextScreen);

View File

@@ -20,13 +20,28 @@ import TextInputEvents from './examples/TextInputEvents';
import TextInputRewrite, { TextInputRewriteInvalidCharacters } from './examples/Rewrite';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem, StyleList, TextList } from '../../ui-explorer';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
StyleList,
TextList
} from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...View props" />,
const TextInputScreen = () =>
<UIExplorer title="TextInput" url="components/TextInput">
<Description>
<AppText>
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 <Code>multiline</Code>.
</AppText>
</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
name="autoCapitalize"
@@ -54,10 +69,9 @@ const sections = [
/>
]}
example={{
code: '',
render: () => <PropAutoCapitalize />
}}
/>,
/>
<DocItem
label="web"
@@ -75,19 +89,19 @@ const sections = [
</AppText>
</AppText>
}
/>,
/>
<DocItem
name="autoCorrect"
typeInfo="?boolean = true"
description="Automatically correct spelling mistakes (only available in iOS Safari)."
/>,
/>
<DocItem
name="autoFocus"
typeInfo="?boolean = false"
description="If `true`, focuses the input on `componentDidMount`. Only the first form element in a document with `autofocus` is focused."
/>,
/>
<DocItem
name="blurOnSubmit"
@@ -104,7 +118,7 @@ const sections = [
example={{
render: () => <PropBlurOnSubmit />
}}
/>,
/>
<DocItem
name="clearTextOnFocus"
@@ -113,7 +127,7 @@ const sections = [
example={{
render: () => <PropClearTextOnFocus />
}}
/>,
/>
<DocItem
name="defaultValue"
@@ -125,81 +139,76 @@ const sections = [
<Code>value</Code> prop to keep the controlled state in sync.
</AppText>
}
/>,
/>
<DocItem
name="editable"
typeInfo="?boolean = true"
description="If `false`, text is not editable (i.e., read-only). "
example={{
code: '',
render: () => <PropEditable />
}}
/>,
/>
<DocItem
name="keyboardType"
typeInfo="enum('default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search') = 'default'"
description="Determines which keyboard to open on devices with a virtual keyboard. Safari iOS requires an ancestral `<form action>` element to display the `search` keyboard). (Not available when `multiline` is `true`.)"
example={{
code: '',
render: () => <PropKeyboardType />
}}
/>,
/>
<DocItem
name="maxLength"
typeInfo="?number"
description="Limits the maximum number of characters that can be entered."
example={{
code: '',
render: () => <PropMaxLength />
}}
/>,
/>
<DocItem
name="multiline"
typeInfo="?boolean = false"
description="If true, the text input can be multiple lines."
example={{
code: '',
render: () => <PropMultiline />
}}
/>,
/>
<DocItem
name="numberOfLines"
typeInfo="?number"
description="Sets the number of lines for a multiline `TextInput`. (Requires `multiline` to be `true`.)"
example={{
code: '',
render: () => <PropNumberOfLines />
}}
/>,
/>
<DocItem
name="onBlur"
typeInfo="?function"
description="Callback that is called when the text input is blurred."
/>,
/>
<DocItem
name="onChange"
typeInfo="?function"
description="Callback that is called when the text input's text changes."
/>,
/>
<DocItem
name="onChangeText"
typeInfo="?function"
description="Callback that is called when the text input's text changes. The text is passed as an argument to the callback handler."
/>,
/>
<DocItem
name="onFocus"
typeInfo="?function"
description="Callback that is called when the text input is focused."
/>,
/>
<DocItem
name="onKeyPress"
@@ -215,7 +224,7 @@ nativeEvent: { key: keyValue } }`}</Code>{' '}
<Code>nativeEvent</Code>. Fires before <Code>onChange</Code> callbacks.
</AppText>
}
/>,
/>
<DocItem
name="onSelectionChange"
@@ -229,49 +238,46 @@ nativeEvent: { key: keyValue } }`}</Code>{' '}
example={{
render: () => <PropOnSelectionChange />
}}
/>,
/>
<DocItem
name="onSubmitEditing"
typeInfo="?function"
description="Callback that is called when the keyboard's submit button is pressed. When multiline={true}, this is only called if blurOnSubmit={true}."
/>,
/>
<DocItem
name="placeholder"
typeInfo="?string"
description="The string that will be rendered in an empty `TextInput` before text has been entered."
example={{
code: '',
render: () => <PropPlaceholder />
}}
/>,
/>
<DocItem
name="secureTextEntry"
typeInfo="?boolean = false"
description="If true, the text input obscures the text entered so that sensitive text like passwords stay secure. (Not available when `multiline` is `true`.)"
example={{
code: '',
render: () => <PropSecureTextEntry />
}}
/>,
/>
<DocItem
name="selection"
typeInfo="?{ start: number, end: ?number }"
description="The start and end of the text input's selection. Set start and end to the same value to position the cursor."
/>,
/>
<DocItem
name="selectTextOnFocus"
typeInfo="?boolean = false"
description="If `true`, all text will automatically be selected on focus."
example={{
code: '',
render: () => <PropSelectTextOnFocus />
}}
/>,
/>
<DocItem
name="style"
@@ -290,7 +296,7 @@ nativeEvent: { key: keyValue } }`}</Code>{' '}
]}
/>
}
/>,
/>
<DocItem
name="value"
@@ -309,43 +315,40 @@ nativeEvent: { key: keyValue } }`}</Code>{' '}
</AppText>
}
/>
]
},
</Section>
<Section title="Instance methods">
<DocItem name="blur" typeInfo="() => void" description="Blur the underlying DOM input." />
{
title: 'Instance methods',
entries: [
<DocItem name="blur" typeInfo="() => void" description="Blur the underlying DOM input." />,
<DocItem
name="clear"
typeInfo="() => void"
description="Clear the text from the underlying DOM input."
/>,
<DocItem name="focus" typeInfo="() => void" description="Focus the underlying DOM input." />,
/>
<DocItem name="focus" typeInfo="() => void" description="Focus the underlying DOM input." />
<DocItem
name="isFocused"
typeInfo="() => boolean"
description="Returns `true` if the input is currently focused; `false` otherwise."
/>
]
},
</Section>
{
title: 'More examples',
entries: [
<Section title="More examples">
<DocItem
description="TextInput events"
example={{
render: () => <TextInputEvents />
}}
/>,
/>
<DocItem
description="Rewrite (<sp> to '_' with maxLength)"
example={{
render: () => <TextInputRewrite />
}}
/>,
/>
<DocItem
description="Rewrite (no spaces allowed)"
@@ -353,15 +356,7 @@ nativeEvent: { key: keyValue } }`}</Code>{' '}
render: () => <TextInputRewriteInvalidCharacters />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('TextInput', () =>
<UIExplorer
description="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`."
sections={sections}
title="TextInput"
url="components/TextInput"
/>
);
storiesOf('Components', module).add('TextInput', TextInputScreen);

View File

@@ -10,95 +10,84 @@ import FeedbackEvents from './examples/FeedbackEvents';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import { TouchableHighlightDisabled } from './examples/PropDisabled';
import UIExplorer, { AppText, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...TouchableWithoutFeedback props" />,
const TouchableHighlightScreen = () =>
<UIExplorer title="TouchableHighlight" url="components/Touchable">
<Description>
<AppText>
A wrapper for making views respond properly to touches. On press down, the opacity of the
wrapped view is decreased, which allows the underlay color to show through, darkening or
tinting the view.
</AppText>
<AppText>
The underlay comes from wrapping the child in a new View, which can affect layout, and
sometimes cause unwanted visual artifacts if not used correctly, for example if the
backgroundColor of the wrapped view isn't explicitly set to an opaque color.
</AppText>
<AppText>
TouchableHighlight must have one child (not zero or more than one). If you wish to have
several child components, wrap them in a View.
</AppText>
</Description>
<Section title="Props">
<DocItem name="...TouchableWithoutFeedback props" />
<DocItem
name="activeOpacity"
typeInfo="?number = 0.85"
description="Determines what the opacity of the wrapped view should be when touch is active."
/>,
/>
<DocItem
name="onHideUnderlay"
typeInfo="?function"
description="Called immediately after the underlay is hidden."
/>,
/>
<DocItem
name="onShowUnderlay"
typeInfo="?function"
description="Called immediately after the underlay is shown"
/>,
/>
<DocItem
name="underlayColor"
typeInfo="?color = black"
description="The color of the underlay that will show through when the touch is active."
/>
]
},
{
title: 'More examples',
entries: [
</Section>
<Section title="More examples">
<DocItem
description="Disabled"
example={{
code: '',
render: () => <TouchableHighlightDisabled />
}}
/>,
/>
<DocItem
description="Feedback events"
example={{
render: () => <FeedbackEvents touchable="highlight" />
}}
/>,
/>
<DocItem
description="Delay events"
example={{
render: () => <DelayEvents touchable="highlight" />
}}
/>,
/>
<DocItem
description="Custom style overrides"
example={{
code: '',
render: () => <CustomStyleOverrides />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('TouchableHighlight', () =>
<UIExplorer
description={[
<AppText>
A wrapper for making views respond properly to touches. On press down, the opacity of the
wrapped view is decreased, which allows the underlay color to show through, darkening or
tinting the view.
</AppText>,
<AppText>
The underlay comes from wrapping the child in a new View, which can affect layout, and
sometimes cause unwanted visual artifacts if not used correctly, for example if the
backgroundColor of the wrapped view isn't explicitly set to an opaque color.
</AppText>,
<AppText>
TouchableHighlight must have one child (not zero or more than one). If you wish to have
several child components, wrap them in a View.
</AppText>
]}
sections={sections}
title="TouchableHighlight"
url="components/Touchable"
/>
);
storiesOf('Components', module).add('TouchableHighlight', TouchableHighlightScreen);

View File

@@ -9,53 +9,59 @@ import FeedbackEvents from './examples/FeedbackEvents';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import { TouchableOpacityDisabled } from './examples/PropDisabled';
import UIExplorer, { AppText, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...TouchableWithoutFeedback props" />,
const TouchableOpacityScreen = () =>
<UIExplorer title="TouchableOpacity" url="components/Touchable">
<Description>
<AppText>
A wrapper for making views respond properly to touches. On press down, the opacity of the
wrapped view is decreased, dimming it.
</AppText>
<AppText>
Opacity is controlled by wrapping the children in an Animated.View, which is added to the
view hiearchy. Be aware that this can affect layout.
</AppText>
</Description>
<Section title="Props">
<DocItem name="...TouchableWithoutFeedback props" />
<DocItem
name="activeOpacity"
typeInfo="?number = 0.2"
description="Determines what the opacity of the wrapped view should be when touch is active."
/>,
/>
<DocItem
name="focusedOpacity"
typeInfo="?number = 0.7"
description="Determines what the opacity of the wrapped view should be when it is focused."
/>
]
},
{
title: 'Instance methods',
entries: [
</Section>
<Section title="Instance methods">
<DocItem
name="setOpacityTo"
typeInfo="(value: number, duration: number) => void"
description="Transition the touchable to a new opacity."
/>
]
},
{
title: 'More examples',
entries: [
</Section>
<Section title="More examples">
<DocItem
description="Disabled TouchableOpacity"
example={{
render: () => <TouchableOpacityDisabled />
}}
/>,
/>
<DocItem
description="Feedback events"
example={{
render: () => <FeedbackEvents touchable="opacity" />
}}
/>,
/>
<DocItem
description="Delay events"
@@ -63,24 +69,7 @@ const sections = [
render: () => <DelayEvents touchable="opacity" />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('TouchableOpacity', () =>
<UIExplorer
description={[
<AppText>
A wrapper for making views respond properly to touches. On press down, the opacity of the
wrapped view is decreased, dimming it.
</AppText>,
<AppText>
Opacity is controlled by wrapping the children in an Animated.View, which is added to the
view hiearchy. Be aware that this can affect layout.
</AppText>
]}
sections={sections}
title="TouchableOpacity"
url="components/Touchable"
/>
);
storiesOf('Components', module).add('TouchableOpacity', TouchableOpacityScreen);

View File

@@ -10,14 +10,24 @@ import React from 'react';
import PropHitSlop from './examples/PropHitSlop';
import { storiesOf } from '@kadira/storybook';
import { TouchableWithoutFeedbackDisabled } from './examples/PropDisabled';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Props',
entries: [
<DocItem name="...View props" />,
const TouchableWithoutFeedbackScreen = () =>
<UIExplorer title="TouchableWithoutFeedback" url="components/Touchable">
<Description>
<AppText>
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".
</AppText>
<AppText>
NOTE: <Code>TouchableWithoutFeedback</Code> supports only one child. If you wish to have
several child components, wrap them in a <Code>View</Code>.
</AppText>
</Description>
<Section title="Props">
<DocItem name="...View props" />
<DocItem
name="delayLongPress"
typeInfo="?number"
@@ -26,8 +36,7 @@ const sections = [
Delay in ms, from <Code>onPressIn</Code>, before <Code>onLongPress</Code> is called.
</AppText>
}
/>,
/>
<DocItem
name="delayPressIn"
typeInfo="?number"
@@ -36,8 +45,7 @@ const sections = [
Delay in ms, from the start of the touch, before <Code>onPressIn</Code> is called.
</AppText>
}
/>,
/>
<DocItem
name="delayPressOut"
typeInfo="?number"
@@ -46,8 +54,7 @@ const sections = [
Delay in ms, from the release of the touch, before <Code>onPressOut</Code> is called.
</AppText>
}
/>,
/>
<DocItem
name="disabled"
typeInfo="?boolean"
@@ -59,20 +66,15 @@ const sections = [
example={{
render: () => <TouchableWithoutFeedbackDisabled />
}}
/>,
/>
<DocItem name="onLongPress" typeInfo="?function" />,
<DocItem
name="onPress"
typeInfo="?function"
description="Called when the touch is released, but not if cancelled (e.g. by a scroll that steals the responder lock)."
/>,
<DocItem name="onPressIn" typeInfo="?function" />,
<DocItem name="onPressOut" typeInfo="?function" />,
/>
<DocItem name="onPressIn" typeInfo="?function" />
<DocItem name="onPressOut" typeInfo="?function" />
<DocItem
name="pressRetentionOffset"
typeInfo="?{top: number, left: number, bottom: number, right: number}"
@@ -81,49 +83,27 @@ 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.`}
/>,
/>
<DocItem name="style" typeInfo="?style" />
]
},
</Section>
{
title: 'More examples',
entries: [
<Section title="More examples">
<DocItem
description="Feedback events"
example={{
render: () => <FeedbackEvents touchable="withoutFeedback" />
}}
/>,
/>
<DocItem
description="Delay events"
example={{
render: () => <DelayEvents touchable="withoutFeedback" />
}}
/>,
/>
<DocItem description="Hit slop" example={{ render: () => <PropHitSlop /> }} />
]
}
];
</Section>
</UIExplorer>;
storiesOf('Components', module).add('TouchableWithoutFeedback', () =>
<UIExplorer
description={[
<AppText>
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".
</AppText>,
<AppText>
NOTE: <Code>TouchableWithoutFeedback</Code> supports only one child. If you wish to have
several child components, wrap them in a <Code>View</Code>.
</AppText>
]}
sections={sections}
title="TouchableWithoutFeedback"
url="components/Touchable"
/>
);
storiesOf('Components', module).add('TouchableWithoutFeedback', TouchableWithoutFeedbackScreen);

View File

@@ -45,9 +45,11 @@ export default class TouchableDelayEvents extends PureComponent {
onPressIn={this._createPressHandler('pressIn: 400ms delay')}
onPressOut={this._createPressHandler('pressOut: 1000ms delay')}
>
<Text style={styles.touchableText}>
{displayName}
</Text>
<View>
<Text style={styles.touchableText}>
{displayName}
</Text>
</View>
</Touchable>
</View>
<View style={styles.eventLogBox}>
@@ -83,16 +85,9 @@ const styles = StyleSheet.create({
borderStyle: 'solid',
textAlign: 'center'
},
logBox: {
padding: 20,
margin: 10,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9'
},
eventLogBox: {
padding: 10,
margin: 10,
marginTop: 10,
height: 120,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',

View File

@@ -41,7 +41,9 @@ export default class TouchableFeedbackEvents extends PureComponent {
onPressIn={this._createPressHandler('pressIn')}
onPressOut={this._createPressHandler('pressOut')}
>
<Text style={styles.touchableText}>Press Me</Text>
<View>
<Text style={styles.touchableText}>Press Me</Text>
</View>
</Touchable>
</View>
<View style={styles.eventLogBox}>
@@ -77,16 +79,9 @@ const styles = StyleSheet.create({
borderStyle: 'solid',
textAlign: 'center'
},
logBox: {
padding: 20,
margin: 10,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9'
},
eventLogBox: {
padding: 10,
margin: 10,
marginTop: 10,
height: 120,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',

View File

@@ -9,7 +9,286 @@ import transformExamples from './examples/transforms';
import ZIndexExample from './examples/ZIndex';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem, StyleList } from '../../ui-explorer';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
StyleList
} from '../../ui-explorer';
const ViewScreen = () =>
<UIExplorer title="View" url="components/View">
<Description>
<AppText>
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.
</AppText>
<AppText>
Also, refer to React Native's documentation about the Gesture Responder System. NOTE: View
will transfer all other props to the rendered HTML element.
</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. (This is implemented using 'aria-label'.)"
/>
<DocItem
name="accessibilityLiveRegion"
typeInfo="?enum('assertive', 'none', 'polite')"
description={
<AppText>
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 <Code>polite</Code> (recommended), updates take low priority. When regions
are specified as <Code>assertive</Code>, assistive technologies will interrupt and
immediately notify the user. (This is implemented using 'aria-live'.)
</AppText>
}
/>
<DocItem
label="web"
name="accessibilityRole"
typeInfo="?enum(roles)"
description={
<AppText>
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 <Code>accessibilityRole</Code> of{' '}
<Code>button</Code>. For compatibility with React Native{' '}
<Code>accessibilityTraits</Code> and <Code>accessibilityComponentType</Code> are mapped
to <Code>accessibilityRole</Code>. (This is implemented using ARIA roles.)
</AppText>
}
/>
<DocItem
name="accessible"
typeInfo="?boolean"
description={
<AppText>
When <Code>true</Code>, 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 <Code>accessibilityRole</Code> of <Code>button</Code> and{' '}
<Code>link</Code> are accessible. (This is implemented using 'tabindex'.)
</AppText>
}
/>
<DocItem name="children" typeInfo="?element" description="Child content" />
<DocItem
name="hitSlop"
typeInfo="?object"
description={[
<AppText>
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.
</AppText>,
<AppText>
For example, if a touchable view has a height of <Code>20</Code> the touchable height
can be extended to <Code>40</Code> with hitSlop.
</AppText>
]}
example={{
code: '<View hitSlop={{top: 10, bottom: 10, left: 0, right: 0}} />'
}}
/>
<DocItem
name="importantForAccessibility"
typeInfo="?enum('auto', 'no', 'no-hide-descendants', 'yes')"
description={[
<AppText>
A value of <Code>no</Code> will remove the element from the tab flow.
</AppText>,
<AppText>
A value of <Code>no-hide-descendants</Code> will hide the element and its children from
assistive technologies. (This is implemented using 'aria-hidden'.)
</AppText>
]}
/>
<DocItem
name="onLayout"
typeInfo="?function"
description={
<AppText>
Invoked on mount and layout changes with{' '}
<Code>{'{ nativeEvent: { layout: { x, y, width, height } } }'}</Code>, where{' '}
<Code>x</Code> and <Code>y</Code> are the offsets from the parent node.
</AppText>
}
/>
<DocItem
name="onMoveShouldSetResponder"
typeInfo="?function => boolean"
description={
<AppText>
Does this view want to "claim" touch responsiveness? This is called for every touch move
on the <Code>View</Code> when it is not the responder.
</AppText>
}
/>
<DocItem
name="onMoveShouldSetResponderCapture"
typeInfo="?function => boolean"
description={
<AppText>
If a parent <Code>View</Code> wants to prevent a child <Code>View</Code> from becoming
responder on a move, it should have this handler return <Code>true</Code>.
</AppText>
}
/>
<DocItem
name="onResponderGrant"
typeInfo="?function"
description={
<AppText>
The <Code>View</Code> 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 <Code>TouchableHighlight</Code> or <Code>TouchableOpacity</Code>.
</AppText>
}
/>
<DocItem
name="onResponderMove"
typeInfo="?function"
description="The user is moving their finger."
/>
<DocItem
name="onResponderReject"
typeInfo="?function"
description={
<AppText>
Another responder is already active and will not release it to the <Code>View</Code>{' '}
asking to be the responder.
</AppText>
}
/>
<DocItem
name="onResponderRelease"
typeInfo="?function"
description="Fired at the end of the touch."
/>
<DocItem
name="onResponderTerminate"
typeInfo="?function"
description={
<AppText>
The responder has been taken from the <Code>View</Code>.
</AppText>
}
/>
<DocItem
name="onResponderTerminationRequest"
typeInfo="?function"
description={
<AppText>
Some other <Code>View</Code> wants to become responder and is asking this{' '}
<Code>View</Code> to release its responder. Returning <Code>true</Code> allows its
release.
</AppText>
}
/>
<DocItem
name="onStartShouldSetResponder"
typeInfo="?function => boolean"
description="Does this view want to become responder on the start of a touch?"
/>
<DocItem
name="onStartShouldSetResponderCapture"
typeInfo="?function => boolean"
description={
<AppText>
If a parent <Code>View</Code> wants to prevent a child <Code>View</Code> from becoming
the responder on a touch start, it should have this handler return <Code>true</Code>.
</AppText>
}
/>
<DocItem
name="pointerEvents"
typeInfo="?enum('auto', 'box-only', 'box-none', 'none') = 'auto'"
description={
<AppText>
Controls whether the View can be the target of touch events. The enhanced{' '}
<Code>pointerEvents</Code> modes provided are not part of the CSS spec, therefore,{' '}
<Code>pointerEvents</Code> is excluded from <Code>style</Code>.
<Code>box-none</Code> preserves pointer events on the element's children;{' '}
<Code>box-only</Code> disables pointer events on the element's children.
</AppText>
}
example={{
render: () => <PropPointerEvents />
}}
/>
<DocItem
name="style"
typeInfo="?style"
description={<StyleList stylePropTypes={stylePropTypes} />}
/>
<DocItem
name="testID"
typeInfo="?string"
description="Used to locate this view in end-to-end tests. The test ID is rendered to a 'data-testid' DOM attribute"
/>
<DocItem
label="compat"
name="accessibilityComponentType"
typeInfo="?enum(roles)"
description={
<AppText>
(For compatibility with React Native. Equivalent to <Code>accessibilityRole</Code>.)
</AppText>
}
/>
<DocItem
label="compat"
name="accessibilityTraits"
typeInfo="?enum(roles) | Array<role>"
description={
<AppText>
(For compatibility with React Native. Equivalent to <Code>accessibilityRole</Code>.)
</AppText>
}
/>
</Section>
<Section title="More examples">
<DocItem
description="z-index"
example={{
render: () => <ZIndexExample />
}}
/>
{transformExamples.map(({ title, render }, i) =>
<DocItem description={title} key={i} example={{ render }} />
)}
</Section>
</UIExplorer>;
const stylePropTypes = [
{
@@ -463,6 +742,11 @@ const stylePropTypes = [
name: 'shadowRadius',
typeInfo: 'number | string'
},
{
label: 'web',
name: 'touchAction',
typeInfo: 'string'
},
{
name: 'top',
typeInfo: 'number | string'
@@ -521,217 +805,4 @@ const stylePropTypes = [
}
];
const sections = [
{
title: 'Props',
entries: [
<DocItem
name="accessibilityLabel"
typeInfo="?string"
description="Overrides the text that's read by a screen reader when the user interacts with the element. (This is implemented using 'aria-label'.)"
/>,
<DocItem
name="accessibilityLiveRegion"
typeInfo="?enum('assertive', 'none', 'polite')"
description={
<AppText>
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 <Code>polite</Code> (recommended), updates take low priority. When regions
are specified as <Code>assertive</Code>, assistive technologies will interrupt and
immediately notify the user. (This is implemented using 'aria-live'.)
</AppText>
}
/>,
<DocItem
label="web"
name="accessibilityRole"
typeInfo="?enum(roles)"
description={
<AppText>
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 <Code>accessibilityRole</Code> of{' '}
<Code>button</Code>. For compatibility with React Native{' '}
<Code>accessibilityTraits</Code> and <Code>accessibilityComponentType</Code> are mapped
to <Code>accessibilityRole</Code>. (This is implemented using ARIA roles.)
</AppText>
}
/>,
<DocItem
name="accessible"
typeInfo="?boolean"
description={
<AppText>
When <Code>true</Code>, 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 <Code>accessibilityRole</Code> of <Code>button</Code> and{' '}
<Code>link</Code> are accessible. (This is implemented using 'tabindex'.)
</AppText>
}
/>,
<DocItem name="children" typeInfo="?element" description="Child content" />,
<DocItem
name="hitSlop"
typeInfo="?object"
description={
'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.\n\nFor example, if a touchable view has a height of `20` the touchable height can be extended to `40` with hitSlop.'
}
example={{
code: '<View hitSlop={{top: 10, bottom: 10, left: 0, right: 0}} />',
render: () => null
}}
/>,
<DocItem
name="importantForAccessibility"
typeInfo="?enum('auto', 'no', 'no-hide-descendants', 'yes')"
description={
'A value of `no` will remove the element from the tab flow.\n\nA value of `no-hide-descendants` will hide the element and its children from assistive technologies. (This is implemented using `aria-hidden`.)'
}
/>,
<DocItem
name="onLayout"
typeInfo="?function"
description="Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width, height } } }`, where `x` and `y` are the offsets from the parent node."
/>,
<DocItem
name="onMoveShouldSetResponder"
typeInfo="?function => boolean"
description="Does this view want to &quot;claim&quot; touch responsiveness? This is called for every touch move on the `View` when it is not the responder."
/>,
<DocItem
name="onMoveShouldSetResponderCapture"
typeInfo="?function => boolean"
description="If a parent `View` wants to prevent a child `View` from becoming responder on a move, it should have this handler return `true`."
/>,
<DocItem
name="onResponderGrant"
typeInfo="?function"
description="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`."
/>,
<DocItem
name="onResponderMove"
typeInfo="?function"
description="The user is moving their finger."
/>,
<DocItem
name="onResponderReject"
typeInfo="?function"
description="Another responder is already active and will not release it to the `View` asking to be the responder."
/>,
<DocItem
name="onResponderRelease"
typeInfo="?function"
description="Fired at the end of the touch."
/>,
<DocItem
name="onResponderTerminate"
typeInfo="?function"
description="The responder has been taken from the `View`."
/>,
<DocItem
name="onResponderTerminationRequest"
typeInfo="?function"
description="Some other `View` wants to become responder and is asking this `View` to release its responder. Returning `true` allows its release."
/>,
<DocItem
name="onStartShouldSetResponder"
typeInfo="?function => boolean"
description="Does this view want to become responder on the start of a touch?"
/>,
<DocItem
name="onStartShouldSetResponderCapture"
typeInfo="?function => boolean"
description="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`."
/>,
<DocItem
name="pointerEvents"
typeInfo="?enum('auto', 'box-only', 'box-none', 'none') = 'auto'"
description="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` preserves pointer events on the element's children; `box-only` disables pointer events on the element's children"
example={{
code: '',
render: () => <PropPointerEvents />
}}
/>,
<DocItem
name="style"
typeInfo="?style"
description={<StyleList stylePropTypes={stylePropTypes} />}
/>,
<DocItem
name="testID"
typeInfo="?string"
description="Used to locate this view in end-to-end tests. The test ID is rendered to a `data-testid` DOM attribute"
/>,
<DocItem
label="compat"
name="accessibilityComponentType"
typeInfo="?enum(roles)"
description="(For compatibility with React Native. Equivalent to &quot;accessibilityRole&quot;.)"
/>,
<DocItem
label="compat"
name="accessibilityTraits"
typeInfo="?enum(roles) | Array<role>"
description="(For compatibility with React Native. Equivalent to &quot;accessibilityRole&quot;.)"
/>
]
},
{
title: 'More examples',
entries: [
<DocItem
description="z-index"
example={{
render: () => <ZIndexExample />
}}
/>
].concat(
transformExamples.map(({ title, render }) =>
<DocItem description={title} example={{ render }} />
)
)
}
];
storiesOf('Components', module).add('View', () =>
<UIExplorer
description={[
<AppText>
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.
</AppText>,
<AppText>
Also, refer to React Native's documentation about the Gesture Responder System. NOTE: View
will transfer all other props to the rendered HTML element.
</AppText>
]}
sections={sections}
title="View"
url="components/View"
/>
);
storiesOf('Components', module).add('View', ViewScreen);

View File

@@ -4,18 +4,26 @@
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
const AppRegistryScreen = () =>
<UIExplorer title="AppRegistry" url="apis/AppRegistry">
<Description>
<AppText>
AppRegistry is the control point for registering, running, prerendering, and unmounting all
apps. App root components should register themselves with{' '}
<Code>AppRegistry.registerComponent</Code>. Apps can be run by invoking{' '}
<Code>AppRegistry.runApplication</Code>
</AppText>
</Description>
<Section title="Methods">
<DocItem
description="Returns the given application's element and stylesheets. Use this for server-side rendering."
label="web"
name="static getApplication"
typeInfo="(appKey: string, appParameters: ?object) => { element: ReactElement; stylesheets: Array<ReactElement> }"
/>,
/>
<DocItem
description={[
@@ -30,7 +38,7 @@ const sections = [
]}
name="static registerConfig"
typeInfo="(config: Array<AppConfig>) => avoid"
/>,
/>
<DocItem
description={
@@ -43,7 +51,7 @@ const sections = [
}}
name="static registerComponent"
typeInfo="(appKey: string, getComponentFunc: ComponentProvider) => void"
/>,
/>
<DocItem
description={
@@ -54,13 +62,13 @@ const sections = [
}
name="static registerRunnable"
typeInfo="(appKey: string, run: Function) => void"
/>,
/>
<DocItem
description="Returns all registered app keys"
name="static getAppKeys"
typeInfo="() => Array<string>"
/>,
/>
<DocItem
description={
@@ -78,7 +86,7 @@ const sections = [
}}
name="static runApplication"
typeInfo="(appKey: string, appParameters?: object) => void"
/>,
/>
<DocItem
description={
@@ -91,22 +99,7 @@ const sections = [
name="static unmountApplicationComponentAtRootTag"
typeInfo="(rootTag: HTMLElement) => void"
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('AppRegistry', () =>
<UIExplorer
description={
<AppText>
AppRegistry is the control point for registering, running, prerendering, and unmounting all
apps. App root components should register themselves with{' '}
<Code>AppRegistry.registerComponent</Code>. Apps can be run by invoking{' '}
<Code>AppRegistry.runApplication</Code>
</AppText>
}
sections={sections}
title="AppRegistry"
url="apis/AppRegistry"
/>
);
storiesOf('APIs', module).add('AppRegistry', AppRegistryScreen);

View File

@@ -7,26 +7,32 @@
import React from 'react';
import StateChangesExample from './examples/StateChanges';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Properties',
entries: [
const AppStateScreen = () =>
<UIExplorer title="AppState" url="apis/AppState">
<Description>
<AppText>
AppState can tell you if the app is in the foreground or background, and notify you when the
state changes. States: <Code>active</Code> (the app is running in the foreground),{' '}
<Code>background</Code> (the app is running in the background, i.e., the user has not
focused the app's tab).
</AppText>
</Description>
<Section title="Properties">
<DocItem
name="static isAvailable"
description="Determines whether the browser environment supports AppState."
/>,
/>
<DocItem
name="static currentState"
description="Returns the current state of the app: &quot;active&quot; or &quot;background&quot;."
/>
]
},
{
title: 'Methods',
entries: [
</Section>
<Section title="Methods">
<DocItem
name="static addEventListener"
typeInfo="(type: string, handler: Function) => void"
@@ -37,7 +43,7 @@ const sections = [
called with the app state value.
</AppText>
}
/>,
/>
<DocItem
name="static removeEventListener"
@@ -49,32 +55,15 @@ const sections = [
</AppText>
}
/>
]
},
{
title: 'Example',
entries: [
</Section>
<Section title="Example">
<DocItem
example={{
render: () => <StateChangesExample />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('AppState', () =>
<UIExplorer
description={
<AppText>
AppState can tell you if the app is in the foreground or background, and notify you when the
state changes. States: <Code>active</Code> (the app is running in the foreground),{' '}
<Code>background</Code> (the app is running in the background, i.e., the user has not
focused the app's tab).
</AppText>
}
sections={sections}
title="AppState"
url="apis/AppState"
/>
);
storiesOf('APIs', module).add('AppState', AppStateScreen);

View File

@@ -4,12 +4,30 @@
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
const AsyncStorageScreen = () =>
<UIExplorer title="AsyncStorage" url="apis/AsyncStorage">
<Description>
<AppText>
AsyncStorage is a simple, unencrypted, asynchronous, persistent, key-value storage system
that is global to the domain. It's a facade over, and should be used instead of{' '}
<Code>window.localStorage</Code> to provide an asynchronous API and multi functions. Each
method returns a <Code>Promise</Code> object.
</AppText>
<AppText>
It is recommended that you use an abstraction on top of <Code>AsyncStorage</Code> instead of{' '}
<Code>AsyncStorage</Code> directly for anything more than light usage since it operates
globally.
</AppText>
<AppText>
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.
</AppText>
</Description>
<Section title="Methods">
<DocItem
description={
<AppText>
@@ -20,25 +38,25 @@ const sections = [
}
name="static clear"
typeInfo="function"
/>,
/>
<DocItem
description="Gets all known keys. Returns a Promise object."
name="static getAllKeys"
typeInfo=""
/>,
/>
<DocItem
description="Fetches the value of the given key. Returns a Promise object.."
name="static getItem"
typeInfo="(key: string) => {}"
/>,
/>
<DocItem
description="Merges existing value with input value, assuming they are stringified JSON. Returns a Promise object."
name="static mergeItem"
typeInfo="(key: string, value: string) => {}"
/>,
/>
<DocItem
description={
@@ -52,7 +70,7 @@ const sections = [
}}
name="static multiGet"
typeInfo="(keys: Array<string>) => {}"
/>,
/>
<DocItem
description={
@@ -64,13 +82,13 @@ const sections = [
}
name="static multiMerge"
typeInfo="(keyValuePairs: Array<Array<string>>) => {}"
/>,
/>
<DocItem
description="Delete all the keys in the keys array. Returns a Promise object."
name="static multiRemove"
typeInfo="(keys: Array<string>) => {}"
/>,
/>
<DocItem
description={
@@ -84,45 +102,20 @@ const sections = [
}}
name="static multiSet"
typeInfo="(keyValuePairs: Array<Array<string>>) => {}"
/>,
/>
<DocItem
description="Removes the value of the given key. Returns a Promise object."
name="static removeItem"
typeInfo="(key: string) => {}"
/>,
/>
<DocItem
description="Sets the value of the given key. Returns a Promise object."
name="static setItem"
typeInfo="(key: string, value: string) => {}"
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('AsyncStorage', () =>
<UIExplorer
description={[
<AppText>
AsyncStorage is a simple, unencrypted, asynchronous, persistent, key-value storage system
that is global to the domain. It's a facade over, and should be used instead of{' '}
<Code>window.localStorage</Code> to provide an asynchronous API and multi functions. Each
method returns a <Code>Promise</Code> object.
</AppText>,
<AppText>
It is recommended that you use an abstraction on top of <Code>AsyncStorage</Code> instead of{' '}
<Code>AsyncStorage</Code> directly for anything more than light usage since it operates
globally.
</AppText>,
<AppText>
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.
</AppText>
]}
sections={sections}
title="AsyncStorage"
url="apis/AsyncStorage"
/>
);
storiesOf('APIs', module).add('AsyncStorage', AsyncStorageScreen);

View File

@@ -5,18 +5,22 @@
import React from 'react';
import SetStringExample from './examples/SetString';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { DocItem } from '../../ui-explorer';
import UIExplorer, { Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
const ClipboardScreen = () =>
<UIExplorer title="Clipboard" url="apis/Clipboard">
<Description>
Clipboard gives you an interface for setting to the clipboard. (Getting clipboard content is
not supported on web.)
</Description>
<Section title="Methods">
<DocItem
description="Determines whether the browser environment supports Clipboard at all."
label="web"
name="static isAvailable"
typeInfo="() => boolean"
/>,
/>
<DocItem
description={
@@ -27,7 +31,7 @@ const sections = [
}}
name="static setString"
typeInfo="(string) => boolean"
/>,
/>
<DocItem
description="Not properly supported on Web. Returns a `Promise` of an empty string."
@@ -35,15 +39,7 @@ const sections = [
name="static getString"
typeInfo="()"
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('Clipboard', () =>
<UIExplorer
description="Clipboard gives you an interface for setting to the clipboard. (Getting clipboard content is not supported on web.)"
sections={sections}
title="Clipboard"
url="apis/Clipboard"
/>
);
storiesOf('APIs', module).add('Clipboard', ClipboardScreen);

View File

@@ -6,13 +6,25 @@
import DimensionsChange from './examples/DimensionsChange';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem, TextList } from '../../ui-explorer';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
TextList
} from '../../ui-explorer';
import React from 'react';
const sections = [
{
title: 'Methods',
entries: [
const DimensionsScreen = () =>
<UIExplorer title="Dimensions" url="apis/Dimensions">
<Description>
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.
</Description>
<Section title="Methods">
<DocItem
name="static get"
typeInfo="(dimension: string) => Object"
@@ -20,7 +32,8 @@ const sections = [
example={{
code: "const { height, width } = Dimensions.get('window')"
}}
/>,
/>
<DocItem
name="static addEventLitener"
typeInfo="(type: string, handler: function) => void"
@@ -41,21 +54,14 @@ const sections = [
example={{
render: () => <DimensionsChange />
}}
/>,
/>
<DocItem
name="static removeEventLitener"
typeInfo="(type: string, handler: function) => void"
description="Remove an event handler."
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('Dimensions', () =>
<UIExplorer
description="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."
sections={sections}
title="Dimensions"
url="apis/Dimensions"
/>
);
storiesOf('APIs', module).add('Dimensions', DimensionsScreen);

View File

@@ -4,36 +4,34 @@
* @flow
*/
import RTLToggleExample from './examples/RTLToggle';
import RTLToggle from './examples/RTLToggle';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { DocItem } from '../../ui-explorer';
import UIExplorer, { Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Properties',
entries: [
const I18nManagerScreen = () =>
<UIExplorer title="I18nManager" url="apis/I18nManager">
<Description>Control and set the layout and writing direction of the application.</Description>
<Section title="Properties">
<DocItem
name="isRTL"
typeInfo="boolean = false"
description="Whether the application is currently in RTL mode."
/>
]
},
{
title: 'Methods',
entries: [
</Section>
<Section title="Methods">
<DocItem
name="static allowRTL"
typeInfo="(allowRTL: boolean) => void"
description="Allow the application to display in RTL mode."
/>,
/>
<DocItem
name="static forceRTL"
typeInfo="(forceRTL: boolean) => void"
description="Force the application to display in RTL mode."
/>,
/>
<DocItem
label="web"
@@ -41,26 +39,16 @@ const sections = [
typeInfo="(isRTL: boolean) => void"
description="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."
/>
]
},
{
title: 'Examples',
entries: [
</Section>
<Section title="Examples">
<DocItem
description="Toggling LTR/RTL layout at runtime"
example={{
render: () => <RTLToggleExample />
render: () => <RTLToggle />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('I18nManager', () =>
<UIExplorer
description="Control and set the layout and writing direction of the application."
sections={sections}
title="I18nManager"
url="apis/I18nManager"
/>
);
storiesOf('APIs', module).add('I18nManager', I18nManagerScreen);

View File

@@ -4,36 +4,31 @@
* @flow
*/
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { DocItem } from '../../ui-explorer';
import OpenURLExample from './examples/OpenURL';
import OpenURL from './examples/OpenURL';
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
<DocItem name="canOpenURL" typeInfo="(url) => Promise<true>" />,
const LinkingScreen = () =>
<UIExplorer title="Linking" url="apis/Linking">
<Description>
Linking gives you a general interface for securely opening external URLs from JavaScript.
</Description>
<DocItem name="getInitialURL" typeInfo="() => Promise<string>" />,
<Section title="Methods">
<DocItem name="canOpenURL" typeInfo="(url) => Promise<true>" />
<DocItem name="getInitialURL" typeInfo="() => Promise<string>" />
<DocItem
name="openURL"
typeInfo="(url: string) => Promise<>"
description="Try to open the given url in a secure fashion. The method returns a Promise object. If the url opens, the promise is resolved. If not, the promise is rejected."
example={{
render: () => <OpenURLExample />
render: () => <OpenURL />
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('Linking', () =>
<UIExplorer
description="Linking gives you a general interface for securely opening external URLs from JavaScript."
sections={sections}
title="Linking"
url="apis/Linking"
/>
);
storiesOf('APIs', module).add('Linking', LinkingScreen);

View File

@@ -4,12 +4,29 @@
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem, TextList } from '../../ui-explorer';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
TextList
} from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
const NetInfoScreen = () =>
<UIExplorer title="NetInfo" url="apis/NetInfo">
<Description>
<AppText>
NetInfo asynchronously determines the online/offline status of the application.
</AppText>
<AppText>
Note that support for retrieving the connection type depends upon browswer support (and is
limited to mobile browsers). It will default to <Code>unknown</Code> when support is
missing.
</AppText>
</Description>
<Section title="Methods">
<DocItem
description={[
<AppText>
@@ -35,7 +52,7 @@ const sections = [
}}
name="static addEventListener"
typeInfo="(eventName, handler) => void"
/>,
/>
<DocItem
description="Returns a promise that resolves with one of the connectivity types listed above."
@@ -46,18 +63,16 @@ const sections = [
}}
name="static fetch"
typeInfo="() => Promise<string>"
/>,
/>
<DocItem
description="Removes the listener for network status changes."
name="static removeEventListener"
typeInfo="(eventName, handler) => void"
/>
]
},
{
title: 'Properties',
entries: [
</Section>
<Section title="Properties">
<DocItem
description="An object with the same methods as above but the listener receives a boolean which represents the internet connectivity. Use this if you are only interested with whether the device has internet connectivity."
example={{
@@ -68,24 +83,7 @@ const sections = [
name="isConnected"
typeInfo="ObjectExpression"
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('NetInfo', () =>
<UIExplorer
description={[
<AppText>
NetInfo asynchronously determines the online/offline status of the application.
</AppText>,
<AppText>
Note that support for retrieving the connection type depends upon browswer support (and is
limited to mobile browsers). It will default to <Code>unknown</Code> when support is
missing.
</AppText>
]}
sections={sections}
title="NetInfo"
url="apis/NetInfo"
/>
);
storiesOf('APIs', module).add('NetInfo', NetInfoScreen);

View File

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

View File

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

View File

@@ -4,23 +4,23 @@
import React from 'react';
import { storiesOf } from '@kadira/storybook';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
const PixelRatioScreen = () =>
<UIExplorer title="PixelRatio" url="apis/PixelRatio">
<Description>PixelRatio class gives access to the device pixel density.</Description>
<Section title="Methods">
<DocItem
description="Returns the device pixel density"
name="static get"
typeInfo="() => number"
/>,
/>
<DocItem
description="On web this returns the device pixel ratio"
name="static getFontScale"
typeInfo="() => number"
/>,
/>
<DocItem
description="Converts a layout size (dp) to pixel size (px). Guaranteed to return an integer number."
@@ -33,7 +33,7 @@ const sections = [
}}
name="static getPixelSizeForLayoutSize"
typeInfo="(layoutSize: number) => number"
/>,
/>
<DocItem
description={
@@ -47,15 +47,7 @@ const sections = [
name="static roundToNearestPixel"
typeInfo="(layoutSize: number) => number"
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('PixelRatio', () =>
<UIExplorer
description="PixelRatio class gives access to the device pixel density."
sections={sections}
title="PixelRatio"
url="apis/PixelRatio"
/>
);
storiesOf('APIs', module).add('PixelRatio', PixelRatioScreen);

View File

@@ -4,12 +4,15 @@
import { storiesOf } from '@kadira/storybook';
import React from 'react';
import UIExplorer, { DocItem } from '../../ui-explorer';
import UIExplorer, { Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Properties',
entries: [
const PlatformScreen = () =>
<UIExplorer title="Platform" url="apis/Platform">
<Description>
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.
</Description>
<Section title="Properties">
<DocItem
description="`Platform.OS` will be `web` when running in a Web browser."
example={{
@@ -22,11 +25,9 @@ const styles = StyleSheet.create({
name="OS"
typeInfo="string"
/>
]
},
{
title: 'Methods',
entries: [
</Section>
<Section title="Methods">
<DocItem
description="`Platform.select` takes an object containing `Platform.OS` as keys and returns the value for the platform you are currently running on."
example={{
@@ -50,15 +51,7 @@ const containerStyles = {
name="select"
typeInfo="(object) => any"
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('Platform', () =>
<UIExplorer
description="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."
sections={sections}
title="Platform"
url="apis/Platform"
/>
);
storiesOf('APIs', module).add('Platform', PlatformScreen);

View File

@@ -4,12 +4,17 @@
import { storiesOf } from '@kadira/storybook';
import React from 'react';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
const StyleSheetScreen = () =>
<UIExplorer title="StyleSheet" url="apis/StyleSheet">
<Description>
The StyleSheet abstraction converts predefined styles to (vendor-prefixed) CSS without
requiring a compile-time step. Styles that cannot be resolved outside of the render loop
(e.g., dynamic positioning) are usually applied as inline styles.
</Description>
<Section title="Methods">
<DocItem
description="Each key of the object passed to `create` must define a style object. The returned object replaces style objects with IDs"
example={{
@@ -27,7 +32,7 @@ const sections = [
}}
name="create"
typeInfo="(obj: {[key: string]: any})"
/>,
/>
<DocItem
description="Lookup a style object by ID or flatten an array of styles into a single style object."
@@ -37,7 +42,7 @@ StyleSheet.flatten([styles.listItem, styles.selectedListItem]);`
}}
name="flatten"
typeInfo="()"
/>,
/>
<DocItem
description={
@@ -50,11 +55,9 @@ StyleSheet.flatten([styles.listItem, styles.selectedListItem]);`
name="getStyleSheets"
typeInfo="() => Array"
/>
]
},
{
title: 'Properties',
entries: [
</Section>
<Section title="Properties">
<DocItem
description="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."
example={{
@@ -62,7 +65,7 @@ StyleSheet.flatten([styles.listItem, styles.selectedListItem]);`
}}
name="absoluteFill"
typeInfo="number"
/>,
/>
<DocItem
description="Sometimes you may want `absoluteFill` but with a couple tweaks - `absoluteFillObject` can be used to create a customized entry in a `StyleSheet`"
@@ -77,18 +80,10 @@ StyleSheet.flatten([styles.listItem, styles.selectedListItem]);`
}}
name="absoluteFillObject"
typeInfo="object"
/>,
/>
<DocItem name="hairlineWidth" typeInfo="number" />
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('StyleSheet', () =>
<UIExplorer
description="The StyleSheet abstraction converts predefined styles to (vendor-prefixed) CSS without requiring a compile-time step. Styles that cannot be resolved outside of the render loop (e.g., dynamic positioning) are usually applied as inline styles."
sections={sections}
title="StyleSheet"
url="apis/StyleSheet"
/>
);
storiesOf('APIs', module).add('StyleSheet', StyleSheetScreen);

View File

@@ -4,31 +4,41 @@
import { storiesOf } from '@kadira/storybook';
import React from 'react';
import UIExplorer, { AppText, Code, DocItem } from '../../ui-explorer';
import UIExplorer, { AppText, Code, Description, DocItem, Section } from '../../ui-explorer';
const sections = [
{
title: 'Methods',
entries: [
<DocItem description="Stop the vibration" name="static cancel" typeInfo="() => void" />,
const VibrationScreen = () =>
<UIExplorer title="Vibration" url="apis/Vibration">
<Description>
<AppText>
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: <Code>Vibration.vibrate()</Code>.
</AppText>
<AppText>
The vibration is asynchronous so this method will return immediately. There will be no
effect on devices that do not support vibration.
</AppText>
</Description>
<Section title="Methods">
<DocItem description="Stop the vibration" name="static cancel" typeInfo="() => void" />
<DocItem
description="Start the vibration pattern"
name="static vibrate"
typeInfo="(pattern) => void"
/>
]
},
{
title: 'Examples',
entries: [
</Section>
<Section title="Examples">
<DocItem
description="Vibrate once for 200ms"
example={{
code: `Vibration.vibrate(200);
Vibration.vibrate([200]);`
}}
/>,
/>
<DocItem
description="Vibrate for 200ms, pause for 100ms, vibrate for 200ms:"
@@ -36,26 +46,7 @@ Vibration.vibrate([200]);`
code: 'Vibration.vibrate([200, 100, 200]);'
}}
/>
]
}
];
</Section>
</UIExplorer>;
storiesOf('APIs', module).add('Vibration', () =>
<UIExplorer
description={[
<AppText>
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: <Code>Vibration.vibrate()</Code>.
</AppText>,
<AppText>
The vibration is asynchronous so this method will return immediately. There will be no
effect on devices that do not support vibration.
</AppText>
]}
sections={sections}
title="Vibration"
url="apis/Vibration"
/>
);
storiesOf('APIs', module).add('Vibration', VibrationScreen);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,7 +10,7 @@ import { StyleSheet, Text } from 'react-native';
const AppText = ({ style, ...rest }) =>
<Text
{...rest}
accessibilityRole={rest.href ? 'link' : null}
accessibilityRole={rest.href ? 'link' : undefined}
style={[styles.baseText, style, rest.href && styles.link]}
/>;

View File

@@ -19,11 +19,7 @@ const DocItem = ({ description, example = {}, name, typeInfo, label }) =>
</AppText>}
{description &&
<View style={styles.description}>
{Array.isArray(description)
? insertBetween(<Divider />, description)
: <AppText>
{description}
</AppText>}
{insertBetween(() => <Divider key={Math.random()} />, React.Children.toArray(description))}
</View>}
{(example.render || example.code) &&
<View style={styles.renderBox}>

View File

@@ -0,0 +1,32 @@
/* eslint-disable react/prop-types */
/**
* @flow
*/
import AppText from './AppText';
import React from 'react';
import { StyleSheet, View } from 'react-native';
const SectionTitle = ({ children }) =>
<AppText style={styles.sectionTitle}>
{children}
</AppText>;
const Section = ({ children, title }) =>
<View>
<SectionTitle>
{title}
</SectionTitle>
{children}
</View>;
const styles = StyleSheet.create({
sectionTitle: {
fontSize: '1.3125rem',
marginBottom: '1.3125rem',
fontWeight: 'bold'
}
});
export default Section;

View File

@@ -13,13 +13,10 @@ const Title = ({ children }) =>
<AppText style={styles.title}>
{children}
</AppText>;
const Description = ({ children }) =>
export const Description = ({ children }) =>
<AppText style={styles.description}>
{children}
</AppText>;
const SectionTitle = ({ children }) =>
<AppText style={styles.sectionTitle}>
{children}
{insertBetween(() => <Divider key={Math.random()} />, React.Children.toArray(children))}
</AppText>;
const Divider = () => <View style={styles.divider} />;
@@ -34,23 +31,13 @@ const SourceLink = ({ uri }) =>
View source code on GitHub
</AppText>;
const UIExplorer = ({ description, sections, title, url }) =>
const UIExplorer = ({ children, description, sections, title, url }) =>
<View style={styles.root}>
<Title>
{title}
</Title>
{description &&
<Description>
{Array.isArray(description) ? insertBetween(<Divider />, description) : description}
</Description>}
{sections.map(({ entries, title }, i) =>
<View key={i}>
<SectionTitle>
{title}
</SectionTitle>
{entries}
</View>
)}
{description}
{children}
{url && <SourceLink uri={url} />}
</View>;
@@ -65,11 +52,6 @@ const styles = StyleSheet.create({
title: {
fontSize: '2rem'
},
sectionTitle: {
fontSize: '1.3125rem',
marginBottom: '1.3125rem',
fontWeight: 'bold'
},
description: {
color: '#666',
display: 'flex',

View File

@@ -5,9 +5,10 @@
import AppText from './AppText';
import Code from './Code';
import DocItem from './DocItem';
import Section from './Section';
import StyleList from './StyleList';
import TextList from './TextList';
import UIExplorer from './UIExplorer';
import UIExplorer, { Description } from './UIExplorer';
export default UIExplorer;
export { AppText, Code, DocItem, StyleList, TextList };
export { AppText, Code, Description, DocItem, Section, StyleList, TextList };

View File

@@ -1,7 +1,7 @@
const insertBetween = (item, array) =>
array.reduce((acc, curr, i, { length }) => {
if (i && i < length) {
return [...acc, item, curr];
return [...acc, item(), curr];
}
return [...acc, curr];
}, []);

3854
docs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.0.112",
"version": "0.0.115",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
@@ -9,12 +9,12 @@
"!**/__tests__"
],
"scripts": {
"benchmarks": "cd benchmarks && yarn && webpack && open index.html",
"benchmark": "cd benchmarks && yarn && webpack && open index.html",
"build": "webpack --config webpack.config.js --sort-assets-by --progress",
"compile": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
"docs:build": "build-storybook -o ./docs/dist -c ./docs/storybook/.storybook",
"docs:start": "start-storybook -p 9001 -c ./docs/storybook/.storybook --dont-track",
"docs:publish": "yarn docs:build && git checkout gh-pages && rm -rf ./storybook && mv docs/dist storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
"docs:build": "cd docs && yarn build",
"docs:start": "cd docs && yarn && yarn start",
"docs:publish": "cd docs && yarn publish",
"flow": "flow",
"fmt": "find benchmarks docs src -name '*.js' | grep -v -E '(node_modules|dist)' | xargs yarn fmt:cmd",
"fmt:cmd": "prettier --print-width=100 --single-quote --write",
@@ -68,14 +68,13 @@
"react-timer-mixin": "^0.13.3"
},
"devDependencies": {
"@kadira/storybook": "^2.35.3",
"@kadira/storybook-addon-options": "^1.0.2",
"babel-cli": "^6.24.1",
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.6",
"babel-preset-react-native": "^2.1.0",
"caniuse-api": "^2.0.0",
"del-cli": "^1.1.0",
"enzyme": "^2.9.1",
"enzyme-to-json": "^1.5.1",

21
scripts/createPrefixer.js Normal file
View File

@@ -0,0 +1,21 @@
const generateData = require('inline-style-prefixer/generator');
const path = require('path');
const browserList = {
chrome: 38,
android: 4,
firefox: 40,
ios_saf: 7,
safari: 7,
ie: 11,
ie_mob: 11,
edge: 12,
opera: 16,
op_mini: 12,
and_uc: 9,
and_chr: 38
};
generateData(browserList, {
staticPath: path.join(__dirname, '../src/modules/prefixStyles/static.js'),
});

View File

@@ -7,15 +7,19 @@
* LICENSE file in the root directory of this source tree.
*
* @providesModule AppState
* @flow
* @noflow
*/
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import findIndex from 'array-find-index';
import invariant from 'fbjs/lib/invariant';
// Android 4.4 browser
const isPrefixed = !document.hasOwnProperty('hidden') && document.hasOwnProperty('webkitHidden');
const EVENT_TYPES = ['change'];
const VISIBILITY_CHANGE_EVENT = 'visibilitychange';
const VISIBILITY_CHANGE_EVENT = isPrefixed ? 'webkitvisibilitychange' : 'visibilitychange';
const VISIBILITY_STATE_PROPERTY = isPrefixed ? 'webkitVisibilityState' : 'visibilityState';
const AppStates = {
BACKGROUND: 'background',
@@ -25,14 +29,14 @@ const AppStates = {
const listeners = [];
export default class AppState {
static isAvailable = ExecutionEnvironment.canUseDOM && document.visibilityState;
static isAvailable = ExecutionEnvironment.canUseDOM && document[VISIBILITY_STATE_PROPERTY];
static get currentState() {
if (!AppState.isAvailable) {
return AppStates.ACTIVE;
}
switch (document.visibilityState) {
switch (document[VISIBILITY_STATE_PROPERTY]) {
case 'hidden':
case 'prerender':
case 'unloaded':

View File

@@ -113,7 +113,8 @@ export default class StyleManager {
if (prop !== 'pointerEvents') {
Object.keys(cache[prop]).forEach(value => {
const className = this.getClassName(prop, value);
rules.push(createCssRule(className, prop, value));
const rule = createCssRule(className, prop, value);
rules.push(rule);
});
}
return rules;

View File

@@ -28,4 +28,12 @@ describe('apis/StyleSheet/StyleManager', () => {
styleManager.setDeclaration('width', '100px');
expect(styleManager.getStyleSheetHtml()).toMatchSnapshot();
});
test('setDeclaration', () => {
styleManager.mainSheet.sheet.insertRule = (rule, position) => {
// check for regressions in CSS write path (e.g., 0 => 0px)
expect(rule.indexOf('-webkit-flex-shrink:0;')).not.toEqual(-1);
};
styleManager.setDeclaration('flexShrink', 0);
});
});

View File

@@ -9,7 +9,7 @@
*/
import debounce from 'debounce';
import findNodeHandle from '../../modules/findNodeHandle';
import StyleSheet from '../../apis/StyleSheet';
import View from '../View';
import ViewPropTypes from '../View/ViewPropTypes';
import React, { Component } from 'react';
@@ -72,41 +72,22 @@ export default class ScrollViewBase extends Component {
_debouncedOnScrollEnd = debounce(this._handleScrollEnd, 100);
_state = { isScrolling: false, scrollLastTick: 0 };
_node = null;
componentDidMount() {
this._node && this._node.addEventListener('scroll', this._handleScroll);
this._node && this._node.addEventListener('touchmove', this._handlePreventableTouchMove);
this._node && this._node.addEventListener('wheel', this._handlePreventableWheel);
}
componentWillUnmount() {
this._node && this._node.removeEventListener('scroll', this._handleScroll);
this._node && this._node.removeEventListener('touchmove', this._handlePreventableTouchMove);
this._node && this._node.removeEventListener('wheel', this._handlePreventableWheel);
}
_createPreventableScrollHandler = (handler: Function) => {
return (e: Object) => {
if (!this.props.scrollEnabled) {
e.preventDefault();
} else {
if (this.props.scrollEnabled) {
if (handler) {
handler(e);
}
} else {
// To disable scrolling in all browsers except Chrome
e.preventDefault();
}
};
};
_handlePreventableTouchMove = (e: Object) => {
this._createPreventableScrollHandler(this.props.onTouchMove)(e);
};
_handlePreventableWheel = (e: Object) => {
this._createPreventableScrollHandler(this.props.onWheel)(e);
};
_handleScroll = (e: Object) => {
_handleScroll = (e: SyntheticEvent) => {
e.persist();
e.stopPropagation();
const { scrollEventThrottle } = this.props;
// A scroll happened, so the scroll bumps the debounce.
@@ -143,10 +124,6 @@ export default class ScrollViewBase extends Component {
}
}
_setNodeRef = (element: View) => {
this._node = findNodeHandle(element);
};
_shouldEmitScrollEvent(lastTick: number, eventThrottle: number) {
const timeSinceLastTick = Date.now() - lastTick;
return eventThrottle > 0 && timeSinceLastTick >= eventThrottle;
@@ -154,16 +131,14 @@ export default class ScrollViewBase extends Component {
render() {
const {
scrollEnabled,
style,
/* eslint-disable */
onMomentumScrollBegin,
onMomentumScrollEnd,
onScroll,
onScrollBeginDrag,
onScrollEndDrag,
onTouchMove,
onWheel,
removeClippedSubviews,
scrollEnabled,
scrollEventThrottle,
showsHorizontalScrollIndicator,
showsVerticalScrollIndicator,
@@ -171,6 +146,23 @@ export default class ScrollViewBase extends Component {
...other
} = this.props;
return <View {...other} ref={this._setNodeRef} />;
return (
<View
{...other}
onScroll={this._handleScroll}
onTouchMove={this._createPreventableScrollHandler(this.props.onTouchMove)}
onWheel={this._createPreventableScrollHandler(this.props.onWheel)}
style={[style, !scrollEnabled && styles.scrollDisabled]}
/>
);
}
}
// Chrome doesn't support e.preventDefault in this case; touch-action must be
// used to disable scrolling.
// https://developers.google.com/web/updates/2017/01/scrolling-intervention
const styles = StyleSheet.create({
scrollDisabled: {
touchAction: 'none'
}
});

View File

@@ -263,7 +263,7 @@ const TouchableHighlight = createReactClass({
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
ref={this._setUnderlayRef}
style={[styles.root, this.props.disabled && styles.disabled, this.state.underlayStyle]}
style={[styles.root, !this.props.disabled && styles.actionable, this.state.underlayStyle]}
>
{React.cloneElement(React.Children.only(this.props.children), {
ref: this._setChildRef
@@ -283,11 +283,11 @@ const INACTIVE_UNDERLAY_PROPS = {
const styles = StyleSheet.create({
root: {
cursor: 'pointer',
userSelect: 'none'
},
disabled: {
cursor: 'default'
actionable: {
cursor: 'pointer',
touchAction: 'manipulate'
}
});

View File

@@ -187,7 +187,7 @@ const TouchableOpacity = createReactClass({
onResponderTerminate={this.touchableHandleResponderTerminate}
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
style={[styles.root, this.props.disabled && styles.disabled, this.props.style]}
style={[styles.root, !this.props.disabled && styles.actionable, this.props.style]}
>
{this.props.children}
{Touchable.renderDebugView({ color: 'blue', hitSlop: this.props.hitSlop })}
@@ -198,13 +198,13 @@ const TouchableOpacity = createReactClass({
const styles = StyleSheet.create({
root: {
cursor: 'pointer',
transitionProperty: 'opacity',
transitionDuration: '0.15s',
userSelect: 'none'
},
disabled: {
cursor: 'default'
actionable: {
cursor: 'pointer',
touchAction: 'manipulate'
}
});

View File

@@ -179,8 +179,8 @@ const TouchableWithoutFeedback = createReactClass({
}
const style =
Touchable.TOUCH_TARGET_DEBUG && child.type && child.type.displayName === 'Text'
? [styles.root, this.props.disabled && styles.disabled, child.props.style, { color: 'red' }]
: [styles.root, this.props.disabled && styles.disabled, child.props.style];
? [!this.props.disabled && styles.actionable, child.props.style, { color: 'red' }]
: [!this.props.disabled && styles.actionable, child.props.style];
return (React: any).cloneElement(child, {
...other,
accessible: this.props.accessible !== false,
@@ -199,11 +199,9 @@ const TouchableWithoutFeedback = createReactClass({
});
const styles = StyleSheet.create({
root: {
cursor: 'pointer'
},
disabled: {
cursor: 'default'
actionable: {
cursor: 'pointer',
touchAction: 'manipulate'
}
});

View File

@@ -48,6 +48,7 @@ const ViewStylePropTypes = {
outlineColor: ColorPropType,
perspective: oneOfType([number, string]),
perspectiveOrigin: string,
touchAction: string,
transitionDelay: string,
transitionDuration: string,
transitionProperty: string,

View File

@@ -1,7 +1,7 @@
// based on https://github.com/facebook/react/pull/4303/files
import EventPluginHub from 'react-dom/lib/EventPluginHub';
import normalizeNativeEvent from './normalizeNativeEvent';
import normalizeNativeEvent from '../normalizeNativeEvent';
import ResponderEventPlugin from 'react-dom/lib/ResponderEventPlugin';
import ResponderTouchHistoryStore from 'react-dom/lib/ResponderTouchHistoryStore';

View File

@@ -8,7 +8,10 @@
* @flow
*/
import prefixAll from 'inline-style-prefixer/static';
import createPrefixer from 'inline-style-prefixer/static/createPrefixer';
import staticData from './static';
const prefixAll = createPrefixer(staticData);
export default prefixAll;

View File

@@ -0,0 +1,155 @@
import crossFade from 'inline-style-prefixer/static/plugins/crossFade';
import cursor from 'inline-style-prefixer/static/plugins/cursor';
import filter from 'inline-style-prefixer/static/plugins/filter';
import flex from 'inline-style-prefixer/static/plugins/flex';
import flexboxOld from 'inline-style-prefixer/static/plugins/flexboxOld';
import gradient from 'inline-style-prefixer/static/plugins/gradient';
import imageSet from 'inline-style-prefixer/static/plugins/imageSet';
import position from 'inline-style-prefixer/static/plugins/position';
import sizing from 'inline-style-prefixer/static/plugins/sizing';
import transition from 'inline-style-prefixer/static/plugins/transition';
const w = ['Webkit'];
const m = ['Moz'];
const ms = ['ms'];
const wm = ['Webkit', 'Moz'];
const wms = ['Webkit', 'ms'];
const wmms = ['Webkit', 'Moz', 'ms'];
export default {
plugins: [
crossFade,
cursor,
filter,
flex,
flexboxOld,
gradient,
imageSet,
position,
sizing,
transition
],
prefixMap: {
animation: w,
animationDelay: w,
animationDirection: w,
animationFillMode: w,
animationDuration: w,
animationIterationCount: w,
animationName: w,
animationPlayState: w,
animationTimingFunction: w,
appearance: wm,
userSelect: wmms,
textEmphasisPosition: w,
textEmphasis: w,
textEmphasisStyle: w,
textEmphasisColor: w,
boxDecorationBreak: w,
clipPath: w,
maskImage: w,
maskMode: w,
maskRepeat: w,
maskPosition: w,
maskClip: w,
maskOrigin: w,
maskSize: w,
maskComposite: w,
mask: w,
maskBorderSource: w,
maskBorderMode: w,
maskBorderSlice: w,
maskBorderWidth: w,
maskBorderOutset: w,
maskBorderRepeat: w,
maskBorder: w,
maskType: w,
textDecorationStyle: w,
textDecorationSkip: w,
textDecorationLine: w,
textDecorationColor: w,
filter: w,
fontFeatureSettings: w,
breakAfter: wmms,
breakBefore: wmms,
breakInside: wmms,
columnCount: wm,
columnFill: wm,
columnGap: wm,
columnRule: wm,
columnRuleColor: wm,
columnRuleStyle: wm,
columnRuleWidth: wm,
columns: wm,
columnSpan: wm,
columnWidth: wm,
flex: w,
flexBasis: w,
flexDirection: w,
flexGrow: w,
flexFlow: w,
flexShrink: w,
flexWrap: w,
alignContent: w,
alignItems: w,
alignSelf: w,
justifyContent: w,
order: w,
transform: w,
transformOrigin: w,
transformOriginX: w,
transformOriginY: w,
backfaceVisibility: w,
perspective: w,
perspectiveOrigin: w,
transformStyle: w,
transformOriginZ: w,
backdropFilter: w,
fontKerning: w,
scrollSnapType: wms,
scrollSnapPointsX: wms,
scrollSnapPointsY: wms,
scrollSnapDestination: wms,
scrollSnapCoordinate: wms,
shapeImageThreshold: w,
shapeImageMargin: w,
shapeImageOutside: w,
hyphens: wmms,
flowInto: wms,
flowFrom: wms,
regionFragment: wms,
textAlignLast: m,
tabSize: m,
wrapFlow: ms,
wrapThrough: ms,
wrapMargin: ms,
gridTemplateColumns: ms,
gridTemplateRows: ms,
gridTemplateAreas: ms,
gridTemplate: ms,
gridAutoColumns: ms,
gridAutoRows: ms,
gridAutoFlow: ms,
grid: ms,
gridRowStart: ms,
gridColumnStart: ms,
gridRowEnd: ms,
gridRow: ms,
gridColumn: ms,
gridColumnEnd: ms,
gridColumnGap: ms,
gridRowGap: ms,
gridArea: ms,
gridGap: ms,
textSizeAdjust: wms,
borderImage: w,
borderImageOutset: w,
borderImageRepeat: w,
borderImageSlice: w,
borderImageSource: w,
borderImageWidth: w,
transitionDelay: w,
transitionDuration: w,
transitionProperty: w,
transitionTimingFunction: w
}
};

View File

@@ -46,4 +46,18 @@ const unitlessNumbers = {
shadowOpacity: true
};
/**
* Support style names that may come passed in prefixed by adding permutations
* of vendor prefixes.
*/
const prefixes = ['ms', 'Moz', 'O', 'Webkit'];
const prefixKey = (prefix: string, key: string) => {
return prefix + key.charAt(0).toUpperCase() + key.substring(1);
};
Object.keys(unitlessNumbers).forEach(prop => {
prefixes.forEach(prefix => {
unitlessNumbers[prefixKey(prefix, prop)] = unitlessNumbers[prop];
});
});
export default unitlessNumbers;

1796
yarn.lock

File diff suppressed because it is too large Load Diff