Compare commits

...

33 Commits

Author SHA1 Message Date
Nicolas Gallagher
3e12ddfb2b 0.2.1 2017-12-20 17:31:25 +00:00
Johannes
3ecf5d2ed2 [fix] corrupt hydrate import 2017-12-20 17:45:45 +01:00
Nicolas Gallagher
0a5acdb996 Update version in benchmark results table 2017-12-20 15:05:58 +00:00
Nicolas Gallagher
a712a58eba 0.2.0 2017-12-20 15:01:12 +00:00
Nicolas Gallagher
6de892c92b [add] CheckBox component
Implements the CheckBox component and adds a web-only 'color' prop to
allow the color of the checkbox to be customized.
2017-12-20 14:51:44 +00:00
Nicolas Gallagher
495defd69b [fix] StyleSheet.hairlineWidth on retina screens 2017-12-20 11:54:13 +00:00
Nicolas Gallagher
1a20fcfce6 [add] StyleSheet.compose
As per the recent addition to React Native.
2017-12-20 11:27:57 +00:00
Nicolas Gallagher
ed1e45a43d Link to yarnpkg website instead of npmjs 2017-12-20 11:13:17 +00:00
Nicolas Gallagher
556dc8926e [fix] ScrollView animated scrollTo
Rely on web's native smooth scrolling mechanism when implemented in the
browser: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior

Fix #593
2017-12-19 15:52:30 +00:00
Nicolas Gallagher
66cf45b90b Update benchmark libraries and results
* Update all packages.
* Remove 'react-native-stylesheet'; since React 16 it is equivalent in
  performance to using the full 'react-native-web' View implementation.
* Remove 'styled-components/primitives'; it's as slow as
  'styled-components'.
* Record latest benchmark results and hardware.
2017-12-19 15:27:05 +00:00
Nicolas Gallagher
d1e49e06e6 Remove unmaintained starter kit 2017-12-18 17:49:51 +00:00
Nicolas Gallagher
8bf28dbe43 Document more instance methods in Direct Manipulation guide
Fix #704
2017-12-18 17:45:23 +00:00
Nicolas Gallagher
9ae95d0797 Add further details to Getting Started guide
Close #728
2017-12-18 17:33:44 +00:00
Nicolas Gallagher
321051b723 [add] ART export
Improve API compatibility with React Native by exporting 'react-art' as
'ART'.

Fix #602
2017-12-18 17:16:09 +00:00
Nicolas Gallagher
5f68542529 [fix] Touchable use with react-test-renderer
The object returned by 'ReactDOM.findDOMNode' when rendered by
'react-test-renderer' doesn't match the DOM API for an element. Only
attempt to bind the listener if 'addEventListener' is present on the
object.

Fix #720
2017-12-18 16:35:45 +00:00
Nicolas Gallagher
82c044ee33 [fix] use ReactDOM.hydrate in AppRegistry.runApplication
Allows AppRegistry to hydrate server-side rendered apps.

Fix #733
2017-12-18 16:15:47 +00:00
Nicolas Gallagher
9bcc67e73a [fix] top-level API exports
Also fixes importing these APIs from 'react-native' when used with the
Babel plugin.
2017-12-18 16:15:03 +00:00
Nicolas Gallagher
f1ce6c2acb [fix] AppRegistry.getApplication style element keys
Fix #734
2017-12-18 15:26:18 +00:00
Nicolas Gallagher
034108a2a0 [add] SafeAreaView component 2017-12-06 14:34:22 -08:00
Nicolas Gallagher
f96d7b868f [change] update PanResponder implementation
Fix #171
2017-12-06 14:23:35 -08:00
Nicolas Gallagher
0dfe319d41 [change] update the Animated implementation
Replaces the 'animated' package with the latest implementation from
React Native. Requires a few imports to be replaced.

Close #716
Fix #714
Fix #688
2017-12-06 14:01:36 -08:00
Kenneth Kufluk
b7e970f4e6 [add] Picker and Picker.Item components
Close #705
2017-12-04 16:15:23 -08:00
Nicolas Gallagher
02e62ad5d6 Lint fixes 2017-12-02 16:08:56 -08:00
Nicolas Gallagher
541d2458fb [change] Image no longer accepts children
Align with recent changes to the React Native API.
2017-12-02 16:04:27 -08:00
Nicolas Gallagher
b1e860ab40 Add ImageBackground docs 2017-12-02 16:04:27 -08:00
Louis Lagrange
e8eab9b3ec [add] ImageBackground component
Close #696
2017-12-02 16:04:19 -08:00
Nicolas Gallagher
6bc76c3c92 Update yarn script syntax 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
2acd8e477c Update raf and debounce modules 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
ff2b0c9bdc Update to React@16.2 dev dependencies 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
79208720d1 Update webpack tools 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
fca04c4125 Update enzyme 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
5b5b72cc19 Update eslint and prettier 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
217ad97bfd [change] Update Flow and types 2017-12-02 15:58:47 -08:00
124 changed files with 6891 additions and 1196 deletions

View File

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

View File

@@ -107,7 +107,6 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
* [Glitch](https://glitch.com/edit/#!/react-native-web-playground)
* [create-react-app](https://github.com/facebookincubator/create-react-app)
* [re-start](https://github.com/react-everywhere/re-start)
## Related projects
@@ -121,7 +120,7 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
React Native for Web is [BSD licensed](LICENSE).
[npm-image]: https://badge.fury.io/js/react-native-web.svg
[npm-url]: https://npmjs.org/package/react-native-web
[npm-url]: https://yarnpkg.com/en/package/react-native-web
[react-native-url]: https://facebook.github.io/react-native/
[travis-image]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[travis-url]: https://travis-ci.org/necolas/react-native-web

View File

@@ -27,6 +27,7 @@ const getDistLocation = importName => {
// components
case 'ActivityIndicator':
case 'ART':
case 'Button':
case 'FlatList':
case 'Image':

View File

@@ -14,12 +14,9 @@ Append `?fastest` to the URL to include the fastest "other libraries", and
The components used in the render benchmarks are simple enough to be
implemented by multiple UI or style libraries. The implementations are not
equivalent in functionality. For example, React Native for Web's stylesheet is
unique in that it also converts React Native styles to DOM styles, has
deterministic resolution, and supports RTL layout.
`react-native-web/stylesheet` is a comparative baseline that implements a
simple `View` without much of React Native's functionality.
equivalent in functionality. For example, the "React Native for Web" benchmark includes a
complete `View` implementation and the `StyleSheet` also converts React Native
styles to DOM styles, has deterministic resolution, and supports RTL layout.
## Benchmark results
@@ -27,26 +24,24 @@ Typical render timings*: mean ± two standard deviations.
| Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `88.83` 18.63` | `198.79` 22.98` | |
| `react-native-web/stylesheet@0.0.121` | `91.17` 19.29` | `209.67` 32.38` | |
| `react-native-web@0.0.121` | `124.21` `±16.84` | `264.55` `±38.75` | `16.90` `±7.30ms` |
| `css-modules` | `80.47` 25.13` | `144.87` 32.70` | |
| `react-native-web@0.2.1` | `88.68` `±28.78` | `178.17` 39.90` | `13.78` `±2.90ms` |
Other libraries
| Implementation | Deep tree (ms) | Wide tree (ms) |
| Implementation | Deep tree (ms) | Wide tree (ms) |
| :--- | ---: | ---: |
| `aphrodite@1.2.3` | `91.73` 41.63` | `197.72` 44.90` |
| `styletron@2.5.1` | `94.73` 37.58` | `201.81` `±57.93` |
| `glamor@2.20.40` | `146.60` `±26.73` | `277.46` 29.17` |
| `emotion@7.2.2` | `150.79` 38.29` | `282.18` 41.79` |
| `react-jss@7.1.0` | `201.83` 34.65` | `428.61` 47.8` |
| `reactxp@0.42.1` | `262.69` `±24.14` | `595.20` 66.17` |
| `styled-components@2.1.2` | `280.59` 31.77` | `599.00` 62.99` |
| `styled-components/primitives@2.1.2` | `291.74` 48.96` | `606.57` 78.18` |
| `radium@0.19.4` | `563.94` `±69.91` | `1139.18` `±152.59` |
| `styletron@3.0.0-rc.5` | `79.41` 27.49` | `152.95` 29.46` |
| `aphrodite@1.2.5` | `85.13` 25.39` | `162.87` 25.91` |
| `glamor@2.20.40` | `109.92` `±29.88` | `193.01` 32.03` |
| `react-jss@8.2.0` | `134.28` 49.00` | `278.78` 50.39` |
| `emotion@8.0.12` | `139.08` 46.18` | `253.45` 52.69` |
| `styled-components@2.3.2` | `194.43` `416.28` | `404.86` 56.59` |
| `reactxp@0.46.6` | `219.46` 57.24` | `424.18` 76.10` |
| `radium@0.19.6` | `359.32` 90.27` | `795.91` 88.93` |
These results indicate that style render performance is not a significant
differentiating factor between `aphrodite`, `css-modules`, `react-native-web`,
and `styletron`.
These results indicate that render times when using `react-native-web`,
`css-modules`, `aphrodite`, and `styletron` are roughly equivalent and
significantly faster than alternatives.
*MacBook Pro (13-inch, Early 2015); 3.1 GHz Intel Core i7; 16 GB 1867 MHz DDR3. Google Chrome 58 (2x CPU slowdown).
*MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3. Google Chrome 62.

View File

@@ -5,7 +5,6 @@ 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';
import styletron from './src/styletron';
import xp from './src/reactxp';
@@ -22,35 +21,33 @@ const coreTests = [
() => renderDeepTree('css-modules', cssModules),
() => renderWideTree('css-modules', cssModules),
() => renderDeepTree('react-native-web/stylesheet', reactNativeStyleSheet),
() => renderWideTree('react-native-web/stylesheet', reactNativeStyleSheet),
() => renderDeepTree('react-native-web', reactNative),
() => renderWideTree('react-native-web', reactNative)
];
const fastestTests = [
() => renderDeepTree('aphrodite', aphrodite),
() => renderWideTree('aphrodite', aphrodite),
() => renderDeepTree('styletron', styletron),
() => renderWideTree('styletron', styletron)
() => renderWideTree('styletron', styletron),
() => renderDeepTree('aphrodite', aphrodite),
() => renderWideTree('aphrodite', aphrodite)
];
/**
* Optionally run tests using other libraries
*/
const restTests = [
() => renderDeepTree('emotion', emotion),
() => renderWideTree('emotion', emotion),
() => renderDeepTree('glamor', glamor),
() => renderWideTree('glamor', glamor),
() => renderDeepTree('radium', radium),
() => renderWideTree('radium', radium),
() => renderDeepTree('reactxp', xp),
() => renderWideTree('reactxp', xp),
() => renderDeepTree('react-jss', jss),
() => renderWideTree('react-jss', jss),
() => renderDeepTree('emotion', emotion),
() => renderWideTree('emotion', emotion),
() => renderDeepTree('styled-components', styledComponents),
() => renderWideTree('styled-components', styledComponents)
() => renderWideTree('styled-components', styledComponents),
() => renderDeepTree('reactxp', xp),
() => renderWideTree('reactxp', xp),
() => renderDeepTree('radium', radium),
() => renderWideTree('radium', radium)
];
const tests = [...coreTests];

View File

@@ -4,19 +4,18 @@
"dependencies": {
"aphrodite": "^1.2.5",
"classnames": "^2.2.5",
"emotion": "^8.0.9",
"emotion": "^8.0.12",
"glamor": "^2.20.40",
"marky": "^1.2.0",
"radium": "^0.19.6",
"react-jss": "^7.2.0",
"react-primitives": "^0.4.3",
"reactxp": "^0.42.11",
"styled-components": "^2.2.3",
"styletron-client": "^2.5.7",
"styletron-utils": "^2.5.4"
"react-jss": "^8.2.0",
"reactxp": "^0.46.6",
"styled-components": "^2.3.2",
"styletron-client": "^3.0.0-rc.5",
"styletron-utils": "^3.0.0-rc.3"
},
"devDependencies": {
"css-loader": "^0.28.7",
"style-loader": "^0.19.0"
"style-loader": "^0.19.1"
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,14 +2,44 @@
# yarn lockfile v1
"@types/lodash@4.14.66":
version "4.14.66"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.66.tgz#3dbb83477becf130611f8fac82a8fdb199805981"
"@babel/helper-module-imports@7.0.0-beta.32":
version "7.0.0-beta.32"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.32.tgz#8126fc024107c226879841b973677a4f4e510a03"
dependencies:
"@babel/types" "7.0.0-beta.32"
lodash "^4.2.0"
"@babel/types@7.0.0-beta.32":
version "7.0.0-beta.32"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.32.tgz#c317d0ecc89297b80bbcb2f50608e31f6452a5ff"
dependencies:
esutils "^2.0.2"
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@types/lodash@^4.14.64":
version "4.14.74"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.74.tgz#ac3bd8db988e7f7038e5d22bd76a7ba13f876168"
"@types/lodash@^4.14.78":
version "4.14.91"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.91.tgz#794611b28056d16b5436059c6d800b39d573cd3a"
"@types/node@*":
version "8.5.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.5.1.tgz#4ec3020bcdfe2abffeef9ba3fbf26fca097514b5"
"@types/react-dom@^16.0.0":
version "16.0.3"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.3.tgz#8accad7eabdab4cca3e1a56f5ccb57de2da0ff64"
dependencies:
"@types/node" "*"
"@types/react" "*"
"@types/react@*", "@types/react@^16.0.0":
version "16.0.31"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.0.31.tgz#5285da62f3ac62b797f6d0729a1d6181f3180c3e"
abbrev@1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
@@ -27,20 +57,6 @@ alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
animated@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/animated/-/animated-0.1.5.tgz#83df8dc443d57abab7b0bb04818b0b655b31c9b9"
dependencies:
invariant "^2.2.0"
normalize-css-color "^1.0.1"
animated@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/animated/-/animated-0.2.0.tgz#1a0e96f097b3fbc5b64d7eddc723bcc0a6f97633"
dependencies:
invariant "^2.2.0"
normalize-css-color "^1.0.1"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@@ -63,15 +79,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
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.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
@@ -104,40 +116,22 @@ babel-code-frame@^6.11.0:
esutils "^2.0.2"
js-tokens "^3.0.0"
babel-generator@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
dependencies:
babel-messages "^6.23.0"
babel-runtime "^6.26.0"
babel-types "^6.26.0"
detect-indent "^4.0.0"
jsesc "^1.3.0"
lodash "^4.17.4"
source-map "^0.5.6"
trim-right "^1.0.1"
babel-macros@^1.0.2:
babel-macros@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/babel-macros/-/babel-macros-1.2.0.tgz#39e47ed6d286d4a98f1948d8bab45dac17e4e2d4"
dependencies:
cosmiconfig "3.1.0"
babel-messages@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
babel-plugin-emotion@^8.0.12:
version "8.0.12"
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-8.0.12.tgz#2ed844001416b0ae2ff787a06b1804ec5f531c89"
dependencies:
babel-runtime "^6.22.0"
babel-plugin-emotion@^8.0.9:
version "8.0.9"
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-8.0.9.tgz#65a9ead1e9a574fa1b0390ebcea942739761713c"
dependencies:
babel-generator "^6.26.0"
babel-macros "^1.0.2"
"@babel/helper-module-imports" "7.0.0-beta.32"
babel-macros "^1.2.0"
babel-plugin-syntax-jsx "^6.18.0"
convert-source-map "^1.5.0"
emotion-utils "^8.0.9"
emotion-utils "^8.0.12"
find-root "^1.1.0"
source-map "^0.5.7"
touch "^1.0.0"
@@ -145,29 +139,6 @@ babel-plugin-syntax-jsx@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
babel-runtime@^6.22.0, babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
babel-runtime@^6.23.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.25.0.tgz#33b98eaa5d482bb01a8d1aa6b437ad2b01aec41c"
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
babel-types@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
dependencies:
babel-runtime "^6.26.0"
esutils "^2.0.2"
lodash "^4.17.4"
to-fast-properties "^1.0.3"
balanced-match@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
@@ -188,9 +159,13 @@ bowser@^1.6.0:
version "1.7.1"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.1.tgz#a4de8f18a1a0dc9531eb2a92a1521fb6a9ba96a5"
brcast@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brcast/-/brcast-2.0.1.tgz#4311508f0634a6f5a2465b6cf2db27f06902aaca"
bowser@^1.7.3:
version "1.8.1"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.8.1.tgz#49785777e7302febadb1a5b71d9a646520ed310d"
brcast@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/brcast/-/brcast-3.0.1.tgz#6256a8349b20de9eed44257a9b24d71493cd48dd"
browserslist@^1.0.1, browserslist@^1.5.2, browserslist@^1.7.5:
version "1.7.5"
@@ -297,10 +272,6 @@ core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
core-js@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
cosmiconfig@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-3.1.0.tgz#640a94bf9847f321800403cd273af60665c73397"
@@ -310,14 +281,6 @@ cosmiconfig@3.1.0:
parse-json "^3.0.0"
require-from-string "^2.0.1"
create-react-class@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4"
dependencies:
fbjs "^0.8.9"
loose-envify "^1.3.1"
object-assign "^4.1.1"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
@@ -332,6 +295,12 @@ css-in-js-utils@^1.0.3:
dependencies:
hyphenate-style-name "^1.0.2"
css-in-js-utils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.0.tgz#5af1dd70f4b06b331f48d22a3d86e0786c0b9435"
dependencies:
hyphenate-style-name "^1.0.2"
css-loader@^0.28.7:
version "0.28.7"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.7.tgz#5f2ee989dd32edd907717f953317656160999c1b"
@@ -429,34 +398,14 @@ csso@~2.3.1:
clap "^1.0.9"
source-map "^0.5.3"
debounce@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.0.2.tgz#503cc674d8d7f737099664fb75ddbd36b9626dc6"
decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
deep-assign@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/deep-assign/-/deep-assign-2.0.0.tgz#ebe06b1f07f08dae597620e3dd1622f371a1c572"
dependencies:
is-obj "^1.0.0"
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
deline@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/deline/-/deline-1.0.4.tgz#6c05c87836926e1a1c63e47882f3d2eb2c6f14c9"
detect-indent@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
dependencies:
repeating "^2.0.0"
electron-to-chromium@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.2.4.tgz#9751cbea89fa120bf88c226ba41eb8d0b6f1b597"
@@ -465,16 +414,16 @@ emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
emotion-utils@^8.0.9:
version "8.0.9"
resolved "https://registry.yarnpkg.com/emotion-utils/-/emotion-utils-8.0.9.tgz#458c7676de2f5206b0b796f7c96c53a5970ed9f2"
emotion-utils@^8.0.12:
version "8.0.12"
resolved "https://registry.yarnpkg.com/emotion-utils/-/emotion-utils-8.0.12.tgz#5e0fd72db3008f26ce4f80b1972df08841df2168"
emotion@^8.0.9:
version "8.0.9"
resolved "https://registry.yarnpkg.com/emotion/-/emotion-8.0.9.tgz#788cf2c3ccd59becbd3e78eb01bef22ff20cf381"
emotion@^8.0.12:
version "8.0.12"
resolved "https://registry.yarnpkg.com/emotion/-/emotion-8.0.12.tgz#03de11ce26b1b2401c334b94d438652124c514c6"
dependencies:
babel-plugin-emotion "^8.0.9"
emotion-utils "^8.0.9"
babel-plugin-emotion "^8.0.12"
emotion-utils "^8.0.12"
stylis "^3.3.2"
stylis-rule-sheet "^0.0.5"
@@ -530,6 +479,18 @@ fbjs@^0.8.12:
setimmediate "^1.0.5"
ua-parser-js "^0.7.9"
fbjs@^0.8.16:
version "0.8.16"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.9"
fbjs@^0.8.5, fbjs@^0.8.9:
version "0.8.12"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04"
@@ -542,14 +503,14 @@ fbjs@^0.8.5, fbjs@^0.8.9:
setimmediate "^1.0.5"
ua-parser-js "^0.7.9"
find-root@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
flexibility@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/flexibility/-/flexibility-2.0.1.tgz#ad323aafc40f469ce624286518fc4d7cd72b7c77"
function-bind@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
@@ -588,6 +549,10 @@ hoist-non-react-statics@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
hoist-non-react-statics@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0"
html-comment-regex@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
@@ -626,7 +591,7 @@ inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
inline-style-prefixer@^2.0.1, inline-style-prefixer@^2.0.5:
inline-style-prefixer@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz#c153c7e88fd84fef5c602e95a8168b2770671fe7"
dependencies:
@@ -640,11 +605,12 @@ inline-style-prefixer@^3.0.1, inline-style-prefixer@^3.0.6:
bowser "^1.6.0"
css-in-js-utils "^1.0.3"
invariant@^2.2.0, invariant@^2.2.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
inline-style-prefixer@^3.0.3:
version "3.0.8"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz#8551b8e5b4d573244e66a34b04f7d32076a2b534"
dependencies:
loose-envify "^1.0.0"
bowser "^1.7.3"
css-in-js-utils "^2.0.0"
is-absolute-url@^2.0.0:
version "2.1.0"
@@ -658,12 +624,6 @@ is-directory@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
is-finite@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
dependencies:
number-is-nan "^1.0.0"
is-function@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
@@ -672,9 +632,9 @@ is-in-browser@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.0.2.tgz#f688bea8f1e5aadc3244ebc870d188cfb9b613cf"
is-obj@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
is-in-browser@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
is-plain-obj@^1.0.0:
version "1.1.0"
@@ -729,10 +689,6 @@ js-yaml@~3.7.0:
argparse "^1.0.7"
esprima "^2.6.0"
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
@@ -755,69 +711,77 @@ jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
jss-camel-case@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/jss-camel-case/-/jss-camel-case-5.0.0.tgz#886c1fe56a8a11577454d6a8b4133caa6c1f53a0"
jss-compose@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jss-compose/-/jss-compose-4.0.0.tgz#f0109e8e8301a2678279301c24523dbc76115b9b"
dependencies:
warning "^3.0.0"
jss-default-unit@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/jss-default-unit/-/jss-default-unit-7.0.0.tgz#176c1db91da870e3ad16301f6f4b4cfc6fe1e90a"
jss-expand@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jss-expand/-/jss-expand-4.0.0.tgz#71ec15386d7839bb23892acf9dcaa40b7fe9c785"
jss-extend@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/jss-extend/-/jss-extend-5.0.0.tgz#08a1d4015d05dfe011e3a281457d471226865387"
dependencies:
warning "^3.0.0"
jss-global@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/jss-global/-/jss-global-2.0.0.tgz#a162f822f17e5d760151d908bdb41d7f2824c28f"
jss-nested@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/jss-nested/-/jss-nested-5.0.0.tgz#c0752f31f2d465110d7de6ac83583dbed669faa0"
dependencies:
warning "^3.0.0"
jss-preset-default@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jss-preset-default/-/jss-preset-default-3.0.0.tgz#e43ee1ac526f689baf2bfd28ae95a6fdc3a02663"
dependencies:
jss-camel-case "^5.0.0"
jss-compose "^4.0.0"
jss-default-unit "^7.0.0"
jss-expand "^4.0.0"
jss-extend "^5.0.0"
jss-global "^2.0.0"
jss-nested "^5.0.0"
jss-props-sort "^5.0.0"
jss-vendor-prefixer "^6.0.0"
jss-props-sort@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/jss-props-sort/-/jss-props-sort-5.0.0.tgz#8839c88433f64e8c1dab1a7068796f19b84f9195"
jss-vendor-prefixer@^6.0.0:
jss-camel-case@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/jss-vendor-prefixer/-/jss-vendor-prefixer-6.0.0.tgz#be58124f0cbed76e98cc8eb5219dbb260f057d0b"
resolved "https://registry.yarnpkg.com/jss-camel-case/-/jss-camel-case-6.0.0.tgz#7cf8453e395c31fed931d11efbc885edcd61132e"
jss-compose@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/jss-compose/-/jss-compose-5.0.0.tgz#ce01b2e4521d65c37ea42cf49116e5f7ab596484"
dependencies:
warning "^3.0.0"
jss-default-unit@^8.0.0:
version "8.0.2"
resolved "https://registry.yarnpkg.com/jss-default-unit/-/jss-default-unit-8.0.2.tgz#cc1e889bae4c0b9419327b314ab1c8e2826890e6"
jss-expand@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/jss-expand/-/jss-expand-5.1.0.tgz#b1ad74ec18631f34f65a2124fcfceb6400610e3d"
jss-extend@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jss-extend/-/jss-extend-6.1.0.tgz#85f3d39944018e8f44b322c14fa316068aa7bb0b"
dependencies:
warning "^3.0.0"
jss-global@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jss-global/-/jss-global-3.0.0.tgz#e19e5c91ab2b96353c227e30aa2cbd938cdaafa2"
jss-nested@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/jss-nested/-/jss-nested-6.0.1.tgz#ef992b79d6e8f63d939c4397b9d99b5cbbe824ca"
dependencies:
warning "^3.0.0"
jss-preset-default@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/jss-preset-default/-/jss-preset-default-4.0.1.tgz#822cecb87c27ff91633774422f4c221d61486b65"
dependencies:
jss-camel-case "^6.0.0"
jss-compose "^5.0.0"
jss-default-unit "^8.0.0"
jss-expand "^5.0.0"
jss-extend "^6.0.1"
jss-global "^3.0.0"
jss-nested "^6.0.1"
jss-props-sort "^6.0.0"
jss-template "^1.0.0"
jss-vendor-prefixer "^7.0.0"
jss-props-sort@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/jss-props-sort/-/jss-props-sort-6.0.0.tgz#9105101a3b5071fab61e2d85ea74cc22e9b16323"
jss-template@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/jss-template/-/jss-template-1.0.0.tgz#4b874608706ddceecacdb5567e254aecb6ea69b3"
dependencies:
warning "^3.0.0"
jss-vendor-prefixer@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/jss-vendor-prefixer/-/jss-vendor-prefixer-7.0.0.tgz#0166729650015ef19d9f02437c73667231605c71"
dependencies:
css-vendor "^0.3.8"
jss@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/jss/-/jss-8.1.0.tgz#b32f15efcce22446dfda4c2be09a04f38431da0a"
jss@^9.3.2:
version "9.4.0"
resolved "https://registry.yarnpkg.com/jss/-/jss-9.4.0.tgz#fbfd1a63556c5afd5bfcffd98df3c50eb2614ed3"
dependencies:
is-in-browser "^1.0.2"
is-in-browser "^1.1.3"
symbol-observable "^1.1.0"
warning "^3.0.0"
loader-utils@^1.0.2:
@@ -840,11 +804,11 @@ lodash.uniq@^4.3.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
lodash@^4.17.1, lodash@^4.17.4:
lodash@^4.17.1, lodash@^4.17.4, lodash@^4.2.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
loose-envify@^1.0.0, loose-envify@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
@@ -885,10 +849,6 @@ nopt@~1.0.10:
dependencies:
abbrev "1"
normalize-css-color@^1.0.1, normalize-css-color@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/normalize-css-color/-/normalize-css-color-1.0.2.tgz#02991e97cccec6623fe573afbbf0de6a1f3e9f8d"
normalize-range@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
@@ -906,10 +866,6 @@ num2fraction@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
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"
@@ -1183,6 +1139,14 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9:
fbjs "^0.8.9"
loose-envify "^1.3.1"
prop-types@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.3.1"
object-assign "^4.1.1"
q@^1.1.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
@@ -1203,75 +1167,30 @@ radium@^0.19.6:
inline-style-prefixer "^2.0.5"
prop-types "^15.5.8"
react-jss@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-7.2.0.tgz#30a5ed51d8388a33767c6d19790b222c1f33f48f"
react-jss@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-8.2.0.tgz#8440f08aef27d408ba31f63df09167ed22a5b99b"
dependencies:
hoist-non-react-statics "^1.2.0"
jss "^8.1.0"
jss-preset-default "^3.0.0"
prop-types "^15.5.8"
theming "^1.1.0"
hoist-non-react-statics "^2.3.1"
jss "^9.3.2"
jss-preset-default "^4.0.1"
prop-types "^15.6.0"
theming "^1.3.0"
react-native-web@0.0.x:
version "0.0.116"
resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.0.116.tgz#e05e376b34617a54d61826e4bc06b0bdbfd3f4b2"
reactxp@^0.46.6:
version "0.46.6"
resolved "https://registry.yarnpkg.com/reactxp/-/reactxp-0.46.6.tgz#166a503a7147f3a1e29efc4469bda32603471a5f"
dependencies:
animated "^0.2.0"
array-find-index "^1.0.2"
babel-runtime "^6.23.0"
create-react-class "^15.6.0"
debounce "1.0.2"
deep-assign "^2.0.0"
fbjs "^0.8.12"
hyphenate-style-name "^1.0.2"
inline-style-prefixer "^3.0.6"
normalize-css-color "^1.0.2"
prop-types "^15.5.10"
react-timer-mixin "^0.13.3"
react-primitives@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/react-primitives/-/react-primitives-0.4.3.tgz#4970afda5a32dccf5ea180380e3a0e16192e4b83"
dependencies:
animated "^0.1.5"
asap "^2.0.5"
deline "^1.0.4"
flexibility "^2.0.1"
inline-style-prefixer "^2.0.5"
invariant "^2.2.1"
normalize-css-color "^1.0.1"
prop-types "^15.5.10"
react-native-web "0.0.x"
react-timer-mixin "^0.13.3"
string-hash "^1.1.3"
react-timer-mixin@^0.13.3:
version "0.13.3"
resolved "https://registry.yarnpkg.com/react-timer-mixin/-/react-timer-mixin-0.13.3.tgz#0da8b9f807ec07dc3e854d082c737c65605b3d22"
react@^15.5.4:
version "15.6.1"
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
dependencies:
create-react-class "^15.6.0"
fbjs "^0.8.9"
loose-envify "^1.1.0"
object-assign "^4.1.0"
prop-types "^15.5.10"
reactxp@^0.42.11:
version "0.42.11"
resolved "https://registry.yarnpkg.com/reactxp/-/reactxp-0.42.11.tgz#ec88014e354ddc627fea61ab6639e5970edb85ae"
dependencies:
"@types/lodash" "4.14.66"
"@types/lodash" "^4.14.78"
"@types/react" "^16.0.0"
"@types/react-dom" "^16.0.0"
assert "^1.3.0"
ifvisible.js "^1.0.6"
lodash "^4.17.1"
prop-types "^15.5.9"
rebound "^0.0.13"
subscribableevent "^1.0.0"
synctasks "^0.2.9"
synctasks "^0.3.0"
rebound@^0.0.13:
version "0.0.13"
@@ -1295,14 +1214,6 @@ regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
regenerator-runtime@^0.10.0:
version "0.10.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
regenerator-runtime@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
regexpu-core@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
@@ -1321,12 +1232,6 @@ regjsparser@^0.1.4:
dependencies:
jsesc "~0.5.0"
repeating@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
dependencies:
is-finite "^1.0.0"
require-from-string@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff"
@@ -1381,51 +1286,54 @@ strip-ansi@^3.0.0:
dependencies:
ansi-regex "^2.0.0"
style-loader@^0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.19.0.tgz#7258e788f0fee6a42d710eaf7d6c2412a4c50759"
style-loader@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.19.1.tgz#591ffc80bcefe268b77c5d9ebc0505d772619f85"
dependencies:
loader-utils "^1.0.2"
schema-utils "^0.3.0"
styled-components@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.2.3.tgz#154575c269880c840f903f580287dab155cf684c"
styled-components@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.3.2.tgz#323d86cc9ac89f3fd233a2dfa0849da236f143e3"
dependencies:
buffer "^5.0.3"
css-to-react-native "^2.0.3"
fbjs "^0.8.9"
hoist-non-react-statics "^1.2.0"
is-function "^1.0.1"
is-plain-object "^2.0.1"
prop-types "^15.5.4"
stylis "3.x"
stylis "^3.4.0"
supports-color "^3.2.3"
styletron-client@^2.5.7:
version "2.5.7"
resolved "https://registry.yarnpkg.com/styletron-client/-/styletron-client-2.5.7.tgz#104fa4dc564cd3fe78eb92488e5ef9039c9e242f"
styletron-client@^3.0.0-rc.5:
version "3.0.0-rc.5"
resolved "https://registry.yarnpkg.com/styletron-client/-/styletron-client-3.0.0-rc.5.tgz#275ca0b5f971d244f0e42079ad570be9c31a2a70"
dependencies:
styletron-core "^2.5.7"
styletron-core "^3.0.0-rc.3"
styletron-core@^2.5.7:
version "2.5.7"
resolved "https://registry.yarnpkg.com/styletron-core/-/styletron-core-2.5.7.tgz#2c4a1fae537b42235462e438c24ab619bbf8993e"
styletron-core@^3.0.0-rc.3:
version "3.0.0-rc.3"
resolved "https://registry.yarnpkg.com/styletron-core/-/styletron-core-3.0.0-rc.3.tgz#9468e275d9085d2e5d6d6468cc6d8733dbfa3cba"
styletron-utils@^2.5.4:
version "2.5.4"
resolved "https://registry.yarnpkg.com/styletron-utils/-/styletron-utils-2.5.4.tgz#f08cca7d58ee0338ce85e408cb32900e65620240"
styletron-utils@^3.0.0-rc.3:
version "3.0.0-rc.3"
resolved "https://registry.yarnpkg.com/styletron-utils/-/styletron-utils-3.0.0-rc.3.tgz#21fef2099f1c368e6ff2b8f76bf7a64bb547b760"
dependencies:
inline-style-prefixer "^2.0.1"
inline-style-prefixer "^3.0.3"
stylis-rule-sheet@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.5.tgz#ebae935cc1f6fb31b9b62dba47f2ea8b833dad9f"
stylis@3.x, stylis@^3.3.2:
stylis@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.4.0.tgz#55c6530ebceeca5976d54fb4adc67578afee828d"
stylis@^3.4.0:
version "3.4.5"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.4.5.tgz#d7b9595fc18e7b9c8775eca8270a9a1d3e59806e"
subscribableevent@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/subscribableevent/-/subscribableevent-1.0.0.tgz#bde9500fa9009c7740c924109bac6119cd9898e6"
@@ -1461,27 +1369,30 @@ svgo@^0.7.0:
sax "~1.2.1"
whet.extend "~0.9.9"
synctasks@^0.2.9:
version "0.2.17"
resolved "https://registry.yarnpkg.com/synctasks/-/synctasks-0.2.17.tgz#38852f008878de2e941b6e458ddf552245268da1"
theming@^1.1.0:
symbol-observable@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/theming/-/theming-1.1.0.tgz#0562760b55a1b919c2d5eeb94130351f8958e13a"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32"
synctasks@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/synctasks/-/synctasks-0.3.1.tgz#1f9012b23792ad775ba2693e0cafcfcd65b80d97"
theming@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/theming/-/theming-1.3.0.tgz#286d5bae80be890d0adc645e5ca0498723725bdc"
dependencies:
brcast "^2.0.0"
brcast "^3.0.1"
is-function "^1.0.1"
is-plain-object "^2.0.1"
prop-types "^15.5.8"
react "^15.5.4"
through@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
to-fast-properties@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
touch@^1.0.0:
version "1.0.0"
@@ -1489,10 +1400,6 @@ touch@^1.0.0:
dependencies:
nopt "~1.0.10"
trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
ua-parser-js@^0.7.9:
version "0.7.12"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"

View File

@@ -1,13 +1,13 @@
# Direct manipulation
React Native for Web provides several methods to directly access the underlying
DOM node. This can be useful when you need to make changes directly to a
component without using state/props to trigger a re-render of the entire
subtree, or when you want to focus a view or measure its on-screen dimensions.
React Native provides several methods to directly access the underlying host
node. This can be useful when you need to make changes directly to a component
without using state/props to trigger a re-render of the entire subtree, or when
you want to focus a view or measure its on-screen dimensions.
The methods described are available on most of the default components provided
by React Native for Web. Note, however, that they are *not* available on the
composite components that you define in your own app.
by React Native for Web. Note, however, that they are *not* available on the composite
components that you define in your own app.
## Instance methods
@@ -35,11 +35,17 @@ Like `measure`, but measures the view relative to another view, specified as
`relativeToNativeNode`. This means that the returned `x`, `y` are relative to
the origin `x`, `y` of the ancestor view.
As always, to obtain a native node handle for a component, you can use
`findNodeHandle(component)`.
**measureInWindow**(callback: (x, y, width, height) => void)
Determines the location of the given view in the window and returns the values
via an async callback.
**setNativeProps**(nativeProps: Object)
This function sends props straight to the underlying DOM node. See the [direct
manipulation](../guides/direct-manipulation.md) guide for cases where
`setNativeProps` should be used.
This function sends props straight to the underlying DOM node.
## About `setNativeProps`

View File

@@ -10,14 +10,26 @@ polyfill.
## Web packager
[Webpack](https://webpack.js.org) is a popular build tool for web apps. Below is an
example of how to configure a build that uses [Babel](https://babeljs.io/) to
compile your JavaScript for the web.
_example_ of how to configure a build that uses [Babel](https://babeljs.io/) to
compile your JavaScript for the web. Please refer to the webpack documentation
when setting up your project.
Install webpack-related dependencies, for example:
```
yarn add --dev babel-loader url-loader webpack webpack-dev-server
```
Create a `web/webpack.config.js` file:
```js
// web/webpack.config.js
const path = require('path');
const webpack = require('webpack');
const appDirectory = path.resolve(__dirname, '../');
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
@@ -25,25 +37,26 @@ Create a `web/webpack.config.js` file:
// `node_module`.
const babelLoaderConfiguration = {
test: /\.js$/,
// Add every directory that needs to be compiled by Babel during the build
// Add every directory that needs to be compiled by Babel during the build.
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules/react-native-uncompiled')
path.resolve(appDirectory, 'index.web.js'),
path.resolve(appDirectory, 'src'),
path.resolve(appDirectory, 'node_modules/react-native-uncompiled')
],
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// This aliases 'react-native' to 'react-native-web' and includes only
// the modules needed by the app
plugins: ['react-native-web/babel'],
// The 'react-native' preset is recommended (or use your own .babelrc)
// the modules needed by the app.
plugins: ['react-native-web/babel', 'transform-runtime'],
// The 'react-native' preset is recommended (or use your own .babelrc).
presets: ['react-native']
}
}
};
// This is needed for webpack to import static images in JavaScript files
// This is needed for webpack to import static images in JavaScript files.
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png|svg)$/,
use: {
@@ -55,6 +68,15 @@ const imageLoaderConfiguration = {
};
module.exports = {
// your web-specific entry file
entry: path.resolve(appDirectory, 'index.web.js'),
// configures where the build ends up
output: {
filename: 'bundle.web.js',
path: path.resolve(appDirectory, 'dist')
},
// ...the rest of your config
module: {
@@ -69,7 +91,8 @@ module.exports = {
// builds to eliminate development checks and reduce build size. You may
// wish to include additional optimizations.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
__DEV__: process.env.NODE_ENV === 'production' || true
})
],
@@ -82,24 +105,20 @@ module.exports = {
}
```
To run in development:
To run in development from the root of your application:
```
./node_modules/.bin/webpack-dev-server -d --config web/webpack.config.js --inline --hot --colors
./node_modules/.bin/webpack-dev-server -d --config ./web/webpack.config.js --inline --hot --colors
```
To build for production:
```
./node_modules/.bin/webpack -p --config web/webpack.config.js
./node_modules/.bin/webpack -p --config ./web/webpack.config.js
```
Please refer to the Webpack documentation for more information on configuration.
## Web entry
Create a `index.web.js` file (or simply `index.js` for web-only apps).
### Client-side rendering
Rendering using `AppRegistry`:

View File

@@ -0,0 +1,93 @@
/* eslint-disable react/jsx-sort-props */
/**
* @flow
*/
import CustomSize from './examples/CustomSize';
import PropColor from './examples/PropColor';
import PropDisabled from './examples/PropDisabled';
import PropOnValueChange from './examples/PropOnValueChange';
import PropValue from './examples/PropValue';
import React from 'react';
import UIExplorer, {
AppText,
Code,
Description,
DocItem,
Section,
storiesOf
} from '../../ui-explorer';
const CheckBoxScreen = () => (
<UIExplorer title="CheckBox" url="components/CheckBox">
<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="Customize the color of the checkbox."
example={{
render: () => <PropColor />
}}
label="web"
name="color"
typeInfo="?color"
/>
<DocItem
description="If true, the user won't be able to interact with the checkbox."
example={{
render: () => <PropDisabled />
}}
name="disabled"
typeInfo="?boolean = false"
/>
<DocItem
description="Invoked with the event when the value changes."
name="onChange"
typeInfo="?function"
/>
<DocItem
description="Invoked with the new value when the value changes."
example={{
render: () => <PropOnValueChange />
}}
name="onValueChange"
typeInfo="?function"
/>
<DocItem
description="The value of the checkbox. If `true` the checkbox will be checked."
example={{
render: () => <PropValue />
}}
name="value"
typeInfo="?boolean = false"
/>
</Section>
<Section title="More examples">
<DocItem
description="The checkbox size can be controlled by the 'height' and 'width' style properties"
example={{
code: '<CheckBox style={{ height: 32, width: 32 }} />',
render: () => <CustomSize />
}}
name="Custom size"
/>
</Section>
</UIExplorer>
);
storiesOf('Components', module).add('CheckBox', CheckBoxScreen);

View File

@@ -0,0 +1,20 @@
/**
* @flow
*/
import React from 'react';
import styles from './styles';
import { CheckBox, View } from 'react-native';
const CustomSizeExample = () => (
<View style={styles.row}>
<View style={styles.marginRight}>
<CheckBox style={{ height: 20, width: 20 }} value />
</View>
<View style={styles.marginRight}>
<CheckBox style={{ height: 32, width: 32 }} value />
</View>
</View>
);
export default CustomSizeExample;

View File

@@ -0,0 +1,20 @@
/**
* @flow
*/
import React from 'react';
import styles from './styles';
import { CheckBox, View } from 'react-native';
const CheckBoxColorExample = () => (
<View style={styles.row}>
<View style={styles.marginRight}>
<CheckBox color="#1DA1F2" value />
</View>
<View style={styles.marginRight}>
<CheckBox color="#F45D22" value />
</View>
</View>
);
export default CheckBoxColorExample;

View File

@@ -0,0 +1,20 @@
/**
* @flow
*/
import React from 'react';
import styles from './styles';
import { CheckBox, View } from 'react-native';
const CheckBoxDisabledExample = () => (
<View style={styles.row}>
<View style={styles.marginRight}>
<CheckBox disabled value={false} />
</View>
<View style={styles.marginRight}>
<CheckBox disabled value />
</View>
</View>
);
export default CheckBoxDisabledExample;

View File

@@ -0,0 +1,59 @@
/**
* @flow
*/
import styles from './styles';
import React, { PureComponent } from 'react';
import { CheckBox, Text, View } from 'react-native';
class CheckBoxOnValueChangeExample extends PureComponent {
state = {
eventSwitchIsOn: false,
eventSwitchRegressionIsOn: true
};
render() {
const { eventSwitchIsOn, eventSwitchRegressionIsOn } = this.state;
return (
<View style={styles.row}>
<View style={[styles.alignCenter, styles.marginRight]}>
<CheckBox
onValueChange={this._handleEventSwitch}
style={styles.marginBottom}
value={eventSwitchIsOn}
/>
<CheckBox
onValueChange={this._handleEventSwitch}
style={styles.marginBottom}
value={eventSwitchIsOn}
/>
<Text>{eventSwitchIsOn ? 'On' : 'Off'}</Text>
</View>
<View style={styles.alignCenter}>
<CheckBox
onValueChange={this._handleEventSwitchRegression}
style={styles.marginBottom}
value={eventSwitchRegressionIsOn}
/>
<CheckBox
onValueChange={this._handleEventSwitchRegression}
style={styles.marginBottom}
value={eventSwitchRegressionIsOn}
/>
<Text>{eventSwitchRegressionIsOn ? 'On' : 'Off'}</Text>
</View>
</View>
);
}
_handleEventSwitch = value => {
this.setState({ eventSwitchIsOn: value });
};
_handleEventSwitchRegression = value => {
this.setState({ eventSwitchRegressionIsOn: value });
};
}
export default CheckBoxOnValueChangeExample;

View File

@@ -0,0 +1,20 @@
/**
* @flow
*/
import React from 'react';
import styles from './styles';
import { CheckBox, View } from 'react-native';
const CheckBoxValueExample = () => (
<View style={styles.row}>
<View style={styles.marginRight}>
<CheckBox value={false} />
</View>
<View style={styles.marginRight}>
<CheckBox value />
</View>
</View>
);
export default CheckBoxValueExample;

View File

@@ -0,0 +1,23 @@
/**
* @flow
*/
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
flexWrap: 'wrap'
},
marginRight: {
marginRight: 10
},
marginBottom: {
marginBottom: 10
},
alignCenter: {
alignItems: 'center'
}
});
export default styles;

View File

@@ -0,0 +1,36 @@
/**
* @flow
*/
import React from 'react';
import { StyleSheet, View } from 'react-native';
const DividerHorizontal = () => <View style={styles.horizontalDivider} />;
const DividerVertical = () => <View style={styles.verticalDivider} />;
export const styles = StyleSheet.create({
horizontalDivider: {
width: '0.6rem'
},
verticalDivider: {
height: '1.3125rem'
},
row: {
flexDirection: 'row',
flexWrap: 'wrap'
},
marginRight: {
marginRight: 10
},
marginBottom: {
marginBottom: 10
},
marginVertical: {
marginVertical: 5
},
alignCenter: {
alignItems: 'center'
}
});
export { DividerHorizontal, DividerVertical };

View File

@@ -0,0 +1,32 @@
/* eslint-disable react/jsx-sort-props */
/**
* @flow
*/
import React from 'react';
import PropChildren from './examples/PropChildren';
import UIExplorer, { Description, DocItem, Section, storiesOf } from '../../ui-explorer';
const ImageBackgroundScreen = () => (
<UIExplorer title="ImageBackground" url="1-components/ImageBackground">
<Description>A image component with support for child content.</Description>
<Section title="Props">
<DocItem name="...Image props" />
<DocItem
name="children"
typeInfo="?any"
description="Content to display over the image."
example={{
render: () => <PropChildren />
}}
/>
<DocItem name="imageStyle" typeInfo="?style" description="Styles for the inner image." />
</Section>
</UIExplorer>
);
storiesOf('Components', module).add('ImageBackground', ImageBackgroundScreen);

View File

@@ -5,7 +5,6 @@
*/
import React from 'react';
import PropChildren from './examples/PropChildren';
import PropDefaultSource from './examples/PropDefaultSource';
import PropDraggable from './examples/PropDraggable';
import PropOnError from './examples/PropOnError';
@@ -35,15 +34,6 @@ const ImageScreen = () => (
<Section title="Props">
<DocItem name="...View props" />
<DocItem
name="children"
typeInfo="?any"
description="Content to display over the image."
example={{
render: () => <PropChildren />
}}
/>
<DocItem
name="defaultSource"
typeInfo="?object"

View File

@@ -32,6 +32,7 @@ class NetworkImageExample extends PureComponent {
return (
<View>
{loader}
<Image
defaultSource={sources.placeholder}
onError={this._handleError}
@@ -40,9 +41,7 @@ class NetworkImageExample extends PureComponent {
onLoadStart={this._handleLoadStart}
source={this.props.source}
style={helpers.styles.base}
>
{loader}
</Image>
/>
{this.state.message && <Text style={helpers.styles.marginTop}>{this.state.message}</Text>}
</View>
);

View File

@@ -4,12 +4,12 @@
import sources from '../sources';
import React from 'react';
import { Image, StyleSheet, Text } from 'react-native';
import { ImageBackground, StyleSheet, Text } from 'react-native';
const ImageChildrenExample = () => (
<Image source={sources.large} style={styles.image}>
<ImageBackground source={sources.large} style={styles.image}>
<Text style={styles.text}>Child content</Text>
</Image>
</ImageBackground>
);
const styles = StyleSheet.create({

View File

@@ -0,0 +1,113 @@
/* eslint-disable react/jsx-sort-props, react/jsx-no-bind, no-alert */
/**
* @flow
*/
import React from 'react';
import PickerExample from './examples/PickerExample';
import UIExplorer, {
AppText,
Description,
DocItem,
Section,
StyleList,
storiesOf
} from '../../ui-explorer';
import { View } from 'react-native';
const PickerScreen = () => (
<View>
<UIExplorer title="Picker">
<Description>
<AppText>Renders the native &lt;select&gt; component.</AppText>
</Description>
<Section title="Props">
<DocItem
name="children"
typeInfo="?Array<Picker.Item>"
description="The items to display in the picker."
example={{
code: `<Picker>
<Picker.Item label="Goblet of Fire" />
<Picker.Item label="Order of the Phoenix" />
</Picker>`
}}
/>
<DocItem
name="enabled"
typeInfo="?boolean"
description="If set to false, the picker will be disabled, i.e., the user will not be able to make a selection."
example={{
render: () => <PickerExample enabled={false} />
}}
/>
<DocItem
name="onValueChange"
typeInfo="?(itemValue, itemIndex) => void"
description="Callback for when an item is selected. This is called with the value and index prop of the item that was selected."
example={{
render: () => (
<PickerExample
onValueChange={(itemValue, itemPosition) => {
window.alert(`itemValue: ${itemValue}, itemPosition: ${itemPosition}`);
}}
/>
)
}}
/>
<DocItem
name="selectedValue"
typeInfo="?string"
description="Select the item with the matching value."
example={{
render: () => <PickerExample selectedValue="book-3" />
}}
/>
<DocItem
name="style"
typeInfo="?style"
description={
<StyleList
stylePropTypes={[
{
name: '…View#style'
},
{
name: 'color',
typeInfo: 'color'
}
]}
/>
}
/>
<DocItem
name="testID"
typeInfo="?string"
description="Used to locate this view in end-to-end tests."
/>
</Section>
</UIExplorer>
<UIExplorer title="Picker.Item" url="1-components/Picker">
<Description>Individual selectable item in a Picker.</Description>
<Section title="Props">
<DocItem name="label" typeInfo="string" description="Text to display for this item" />
<DocItem name="testID" typeInfo="?string" />
<DocItem
name="value"
typeInfo="?number | string"
description="The value to be passed to the picker's 'onValueChange' callback when this item is selected."
/>
</Section>
</UIExplorer>
</View>
);
storiesOf('Components', module).add('Picker', PickerScreen);

View File

@@ -0,0 +1,28 @@
/**
* @flow
*/
import React from 'react';
import { Picker, StyleSheet, View } from 'react-native';
const PickerExample = props => (
<View style={styles.root}>
<Picker {...props}>
<Picker.Item label="Sourcerer's Stone" value="book-1" />
<Picker.Item label="Chamber of Secrets" value="book-2" />
<Picker.Item label="Prisoner of Azkaban" value="book-3" />
<Picker.Item label="Goblet of Fire" value="book-4" />
<Picker.Item label="Order of the Phoenix" value="book-5" />
<Picker.Item label="Half-Blood Prince" value="book-6" />
<Picker.Item label="Deathly Hallows" value="book-7" />
</Picker>
</View>
);
const styles = StyleSheet.create({
rootl: {
alignItems: 'flex-start'
}
});
export default PickerExample;

View File

@@ -46,9 +46,11 @@ export default class TextEventsExample extends React.Component {
event.nativeEvent.selection.start +
',' +
event.nativeEvent.selection.end
)}
)
}
onSubmitEditing={event =>
this.updateText('onSubmitEditing text: ' + event.nativeEvent.text)}
this.updateText('onSubmitEditing text: ' + event.nativeEvent.text)
}
placeholder="Enter text to see events"
style={[helperStyles.textinput, { maxWidth: 200 }]}
/>

View File

@@ -21,6 +21,22 @@ const StyleSheetScreen = () => (
</Description>
<Section title="Methods">
<DocItem
description={
<AppText>
Combines two styles such that <Code>style2</Code> will override any styles in{' '}
<Code>style1</Code>. If either style is falsy, the other one is returned without
allocating an array, saving allocations and maintaining reference equality for{' '}
<Code>PureComponent</Code> checks.
</AppText>
}
example={{
code: 'StyleSheet.compose(style1, style2);'
}}
name="compose"
typeInfo="(style1, style2) => style"
/>
<DocItem
description="Each key of the object passed to `create` must define a style object. The returned object replaces style objects with IDs"
example={{
@@ -88,7 +104,11 @@ StyleSheet.flatten([styles.listItem, styles.selectedListItem]);`
typeInfo="object"
/>
<DocItem name="hairlineWidth" typeInfo="number" />
<DocItem
description="Enables borders of just one physical pixel on retina screens, otherwise it is equal to a CSS value of 1px."
name="hairlineWidth"
typeInfo="number"
/>
</Section>
</UIExplorer>
);

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.1.16",
"version": "0.2.1",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
@@ -19,12 +19,12 @@
"docs:start": "cd docs && yarn && yarn start",
"docs:release": "cd docs && yarn release",
"flow": "flow",
"fmt": "find babel benchmarks docs jest src -name '*.js' | grep -v -E '(node_modules|dist)' | xargs yarn fmt:cmd",
"fmt": "find babel benchmarks docs jest src -name '*.js' | grep -v -E '(node_modules|dist|vendor)' | xargs yarn fmt:cmd",
"fmt:cmd": "prettier --print-width=100 --single-quote --write",
"jest": "jest",
"jest:watch": "yarn test -- --watch",
"lint": "yarn lint:cmd -- babel benchmarks docs jest src",
"lint:cmd": "eslint --ignore-path .gitignore --fix",
"jest:watch": "yarn test --watch",
"lint": "yarn lint:cmd babel benchmarks docs jest src",
"lint:cmd": "eslint --ignore-path .gitignore --ignore-pattern '/src/vendor/*' --fix",
"precommit": "lint-staged",
"release": "yarn lint && yarn test && yarn build && npm publish",
"test": "flow && jest"
@@ -61,48 +61,48 @@
]
},
"dependencies": {
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"babel-runtime": "^6.26.0",
"create-react-class": "^15.6.2",
"debounce": "1.0.2",
"debounce": "^1.1.0",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.16",
"hyphenate-style-name": "^1.0.2",
"inline-style-prefixer": "^3.0.8",
"normalize-css-color": "^1.0.2",
"prop-types": "^15.6.0",
"react-art": "^16.2.0",
"react-timer-mixin": "^0.13.3"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "^7.2.3",
"babel-eslint": "^8.0.3",
"babel-loader": "^7.1.2",
"babel-plugin-tester": "^4.0.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.9",
"babel-plugin-transform-react-remove-prop-types": "^0.4.10",
"babel-preset-react-native": "^4.0.0",
"caniuse-api": "^2.0.0",
"del-cli": "^1.1.0",
"enzyme": "^3.1.0",
"enzyme-adapter-react-16": "^1.0.2",
"enzyme-to-json": "^3.1.4",
"eslint": "^4.6.1",
"eslint-config-prettier": "^2.6.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.4.0",
"file-loader": "^1.1.4",
"flow-bin": "^0.49.1",
"enzyme": "^3.2.0",
"enzyme-adapter-react-16": "^1.1.0",
"enzyme-to-json": "^3.2.2",
"eslint": "^4.12.1",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.5.1",
"file-loader": "^1.1.5",
"flow-bin": "^0.60.1",
"jest": "^21.2.1",
"lint-staged": "^4.1.3",
"prettier": "^1.7.3",
"raf": "^3.3.2",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-test-renderer": "^16.0.0",
"url-loader": "^0.5.9",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0"
"prettier": "^1.8.2",
"raf": "^3.4.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0",
"url-loader": "^0.6.2",
"webpack": "^3.9.1",
"webpack-bundle-analyzer": "^2.9.1"
},
"peerDependencies": {
"react": "16.x.x",

View File

@@ -3,27 +3,25 @@
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule Animated
* @noflow
* @flow
*/
import Animated from 'animated';
import AnimatedImplementation from '../../vendor/Animated/AnimatedImplementation';
import Image from '../../components/Image';
import ScrollView from '../../components/ScrollView';
import StyleSheet from '../StyleSheet';
import Text from '../../components/Text';
import View from '../../components/View';
Animated.inject.FlattenStyle(StyleSheet.flatten);
const AnimatedImplementation = {
...Animated,
Image: Animated.createAnimatedComponent(Image),
ScrollView: Animated.createAnimatedComponent(ScrollView),
Text: Animated.createAnimatedComponent(Text),
View: Animated.createAnimatedComponent(View)
const Animated = {
...AnimatedImplementation,
Image: AnimatedImplementation.createAnimatedComponent(Image),
ScrollView: AnimatedImplementation.createAnimatedComponent(ScrollView),
View: AnimatedImplementation.createAnimatedComponent(View),
Text: AnimatedImplementation.createAnimatedComponent(Text)
};
export default AnimatedImplementation;
export default Animated;

View File

@@ -19,6 +19,7 @@ type Context = {
};
type Props = {
// $FlowFixMe
children?: React.Children,
rootTag: any
};
@@ -27,10 +28,7 @@ type State = {
mainKey: number
};
export default class AppContainer extends Component {
props: Props;
state: State = { mainKey: 1 };
export default class AppContainer extends Component<Props, State> {
static childContextTypes = {
rootTag: any
};

View File

@@ -39,6 +39,9 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
.rn-backgroundColor-wib322{background-color:transparent}
.rn-backgroundColor-8ndhhv{background-color:rgba(33,150,243,1)}
.rn-backgroundColor-15al3ab{background-color:rgba(223,223,223,1)}
.rn-backgroundColor-44z8sh{background-color:rgba(255,255,255,1)}
.rn-backgroundColor-5itogg{background-color:rgba(0,150,136,1)}
.rn-backgroundColor-1v82r4u{background-color:rgba(170,184,194,1)}
.rn-backgroundColor-1hj8efq{background-color:rgba(213,213,213,1)}
.rn-backgroundColor-1bgzomc{background-color:rgba(189,189,189,1)}
.rn-color-homxoj{color:inherit}
@@ -56,9 +59,13 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
.rn-borderLeftStyle-mm0ijv{border-left-style:solid}
.rn-borderTopWidth-13yce4e{border-top-width:0px}
.rn-borderTopWidth-1jxfwug{border-top-width:2px}
.rn-borderRightWidth-fnigne{border-right-width:0px}
.rn-borderRightWidth-18p6if4{border-right-width:2px}
.rn-borderBottomWidth-ndvcnb{border-bottom-width:0px}
.rn-borderBottomWidth-wgabs5{border-bottom-width:2px}
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.rn-borderLeftWidth-dwliz8{border-left-width:2px}
.rn-boxSizing-deolkf{box-sizing:border-box}
.rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
.rn-display-xoduu5{display:-webkit-inline-box;display:-moz-inline-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}
@@ -98,6 +105,7 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
.rn-height-1pi2tsx{height:100%}
.rn-height-z80fyv{height:20px}
.rn-height-1r8g8re{height:36px}
.rn-height-10ptun7{height:16px}
.rn-height-4v7adb{height:5px}
.rn-height-1dernwh{height:70%}
.rn-opacity-1272l3b{opacity:0}
@@ -105,6 +113,7 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
.rn-width-13qz1uu{width:100%}
.rn-width-19wmn03{width:20px}
.rn-width-1acpoxo{width:36px}
.rn-width-1janqcz{width:16px}
.rn-touchAction-19z077z{-ms-touch-action:none;touch-action:none}
.rn-touchAction-1gvxusu{-ms-touch-action:manipulate;touch-action:manipulate}
.rn-WebkitOverflowScrolling-150rngu{-webkit-overflow-scrolling:touch}
@@ -152,11 +161,28 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
.rn-borderBottomLeftRadius-pm2fo{border-bottom-left-radius:0px}
.rn-fontWeight-majxgm{font-weight:500}
.rn-textTransform-tsynxw{text-transform:uppercase}
.rn-borderTopColor-j4x2lb{border-top-color:rgba(101,119,134,1)}
.rn-borderTopColor-gj2eto{border-top-color:rgba(0,150,136,1)}
.rn-borderTopColor-1j7vz2b{border-top-color:rgba(204,214,221,1)}
.rn-borderTopColor-2dclza{border-top-color:rgba(170,184,194,1)}
.rn-borderTopColor-kqr9px{border-top-color:black}
.rn-borderRightColor-12i18q1{border-right-color:rgba(101,119,134,1)}
.rn-borderRightColor-31ud7z{border-right-color:rgba(0,150,136,1)}
.rn-borderRightColor-10fg1ub{border-right-color:rgba(204,214,221,1)}
.rn-borderRightColor-8jf312{border-right-color:rgba(170,184,194,1)}
.rn-borderRightColor-q0dj5p{border-right-color:black}
.rn-borderBottomColor-mg3rfb{border-bottom-color:rgba(101,119,134,1)}
.rn-borderBottomColor-1bgnb8i{border-bottom-color:rgba(0,150,136,1)}
.rn-borderBottomColor-zxuuv6{border-bottom-color:rgba(204,214,221,1)}
.rn-borderBottomColor-1yeakrt{border-bottom-color:rgba(170,184,194,1)}
.rn-borderBottomColor-1ah7hsa{border-bottom-color:black}
.rn-borderLeftColor-vnhemr{border-left-color:rgba(101,119,134,1)}
.rn-borderLeftColor-tbzcuz{border-left-color:rgba(0,150,136,1)}
.rn-borderLeftColor-1p34dw6{border-left-color:rgba(204,214,221,1)}
.rn-borderLeftColor-bluj2i{border-left-color:rgba(170,184,194,1)}
.rn-borderLeftColor-137uh4u{border-left-color:black}
.rn-backgroundImage-rs94m5{background-image:url(\\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\\")}
.rn-alignSelf-k200y{-ms-flex-item-align:start;-webkit-align-self:flex-start;align-self:flex-start}
.rn-boxShadow-1ewcgjf{box-shadow:0px 1px 3px rgba(0,0,0,0.5)}
.rn-borderTopColor-kqr9px{border-top-color:black}
.rn-borderRightColor-q0dj5p{border-right-color:black}
.rn-borderBottomColor-1ah7hsa{border-bottom-color:black}
.rn-borderLeftColor-137uh4u{border-left-color:black}
.rn-resize-1dz5y72{resize:none}</style>"
`;

View File

@@ -11,18 +11,20 @@
*/
import invariant from 'fbjs/lib/invariant';
import { unmountComponentAtNode } from 'react-dom';
import { unmountComponentAtNode } from '../../modules/unmountComponentAtNode';
import renderApplication, { getApplication } from './renderApplication';
import type { ComponentType } from 'react';
const emptyObject = {};
const runnables = {};
export type ComponentProvider = () => ReactClass<any>;
export type ComponentProvider = () => ComponentType<any>;
export type AppConfig = {
appKey: string,
component?: ComponentProvider,
run?: Function
run?: Function,
section?: boolean
};
/**
@@ -90,7 +92,7 @@ export default class AppRegistry {
runnables[appKey].run(appParameters);
}
static unmountApplicationComponentAtRootTag(rootTag) {
static unmountApplicationComponentAtRootTag(rootTag: Object) {
unmountComponentAtNode(rootTag);
}
}

View File

@@ -10,19 +10,19 @@
*/
import invariant from 'fbjs/lib/invariant';
import { render } from 'react-dom';
import hydrate from '../../modules/hydrate';
import AppContainer from './AppContainer';
import StyleSheet from '../../apis/StyleSheet';
import React from 'react';
import React, { type ComponentType } from 'react';
export default function renderApplication(
RootComponent: ReactClass<Object>,
initialProps: Object,
export default function renderApplication<Props: Object>(
RootComponent: ComponentType<Props>,
initialProps: Props,
rootTag: any
) {
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
render(
hydrate(
<AppContainer rootTag={rootTag}>
<RootComponent {...initialProps} />
</AppContainer>,
@@ -30,7 +30,7 @@ export default function renderApplication(
);
}
export function getApplication(RootComponent: ReactClass<Object>, initialProps: Object): Object {
export function getApplication(RootComponent: ComponentType<Object>, initialProps: Object): Object {
const element = (
<AppContainer rootTag={{}}>
<RootComponent {...initialProps} />
@@ -38,7 +38,7 @@ export function getApplication(RootComponent: ReactClass<Object>, initialProps:
);
const stylesheets = StyleSheet.getStyleSheets().map(sheet => (
// ensure that CSS text is not escaped
<style dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} />
<style dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} key={sheet.id} />
));
return { element, stylesheets };
}

View File

@@ -20,7 +20,7 @@ const mergeLocalStorageItem = (key, value) => {
window.localStorage.setItem(key, nextValue);
};
const createPromise = (getValue, callback) => {
const createPromise = (getValue, callback): Promise<*> => {
return new Promise((resolve, reject) => {
try {
const value = getValue();
@@ -37,7 +37,7 @@ const createPromise = (getValue, callback) => {
});
};
const createPromiseAll = (promises, callback, processResult) => {
const createPromiseAll = (promises, callback, processResult): Promise<*> => {
return Promise.all(promises).then(
result => {
const value = processResult ? processResult(result) : null;
@@ -55,7 +55,7 @@ export default class AsyncStorage {
/**
* Erases *all* AsyncStorage for the domain.
*/
static clear(callback) {
static clear(callback?: Function): Promise<*> {
return createPromise(() => {
window.localStorage.clear();
}, callback);
@@ -69,7 +69,7 @@ export default class AsyncStorage {
/**
* Gets *all* keys known to the app, for all callers, libraries, etc.
*/
static getAllKeys(callback) {
static getAllKeys(callback?: Function): Promise<*> {
return createPromise(() => {
const numberOfKeys = window.localStorage.length;
const keys = [];
@@ -84,7 +84,7 @@ export default class AsyncStorage {
/**
* Fetches `key` value.
*/
static getItem(key: string, callback) {
static getItem(key: string, callback?: Function): Promise<*> {
return createPromise(() => {
return window.localStorage.getItem(key);
}, callback);
@@ -96,7 +96,7 @@ export default class AsyncStorage {
*
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
*/
static multiGet(keys: Array<string>, callback) {
static multiGet(keys: Array<string>, callback?: Function): Promise<*> {
const promises = keys.map(key => AsyncStorage.getItem(key));
const processResult = result => result.map((value, i) => [keys[i], value]);
return createPromiseAll(promises, callback, processResult);
@@ -105,7 +105,7 @@ export default class AsyncStorage {
/**
* Sets `value` for `key`.
*/
static setItem(key: string, value: string, callback) {
static setItem(key: string, value: string, callback?: Function): Promise<*> {
return createPromise(() => {
window.localStorage.setItem(key, value);
}, callback);
@@ -115,7 +115,7 @@ export default class AsyncStorage {
* Takes an array of key-value array pairs.
* multiSet([['k1', 'val1'], ['k2', 'val2']])
*/
static multiSet(keyValuePairs: Array<Array<string>>, callback) {
static multiSet(keyValuePairs: Array<Array<string>>, callback?: Function): Promise<*> {
const promises = keyValuePairs.map(item => AsyncStorage.setItem(item[0], item[1]));
return createPromiseAll(promises, callback);
}
@@ -123,7 +123,7 @@ export default class AsyncStorage {
/**
* Merges existing value with input value, assuming they are stringified JSON.
*/
static mergeItem(key: string, value: string, callback) {
static mergeItem(key: string, value: string, callback?: Function): Promise<*> {
return createPromise(() => {
mergeLocalStorageItem(key, value);
}, callback);
@@ -135,7 +135,7 @@ export default class AsyncStorage {
*
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
*/
static multiMerge(keyValuePairs: Array<Array<string>>, callback) {
static multiMerge(keyValuePairs: Array<Array<string>>, callback?: Function): Promise<*> {
const promises = keyValuePairs.map(item => AsyncStorage.mergeItem(item[0], item[1]));
return createPromiseAll(promises, callback);
}
@@ -143,7 +143,7 @@ export default class AsyncStorage {
/**
* Removes a `key`
*/
static removeItem(key: string, callback) {
static removeItem(key: string, callback?: Function): Promise<*> {
return createPromise(() => {
return window.localStorage.removeItem(key);
}, callback);
@@ -152,7 +152,7 @@ export default class AsyncStorage {
/**
* Delete all the keys in the `keys` array.
*/
static multiRemove(keys: Array<string>, callback) {
static multiRemove(keys: Array<string>, callback?: Function): Promise<*> {
const promises = keys.map(key => AsyncStorage.removeItem(key));
return createPromiseAll(promises, callback);
}

View File

@@ -17,11 +17,11 @@ export default class Clipboard {
);
}
static getString() {
static getString(): Promise<string> {
return Promise.resolve('');
}
static setString(text) {
static setString(text: string) {
let success = false;
const body = document.body;

View File

@@ -66,12 +66,12 @@ export default class Dimensions {
}
}
static addEventListener(type, handler): void {
static addEventListener(type: string, handler: Function): void {
listeners[type] = listeners[type] || [];
listeners[type].push(handler);
}
static removeEventListener(type, handler): void {
static removeEventListener(type: string, handler: Function): void {
if (Array.isArray(listeners[type])) {
listeners[type] = listeners[type].filter(_handler => _handler !== handler);
}

View File

@@ -6,8 +6,8 @@
* LICENSE file in the root directory of this source tree.
*
* @providesModule Easing
* @noflow
* @flow
*/
import Easing from 'animated/lib/Easing';
import Easing from '../../vendor/Animated/Easing';
export default Easing;

View File

@@ -17,13 +17,13 @@ const initialURL = canUseDOM ? window.location.href : '';
const Linking = {
addEventListener() {},
removeEventListener() {},
canOpenURL() {
canOpenURL(): Promise<boolean> {
return Promise.resolve(true);
},
getInitialURL() {
getInitialURL(): Promise<string> {
return Promise.resolve(initialURL);
},
openURL(url: string) {
openURL(url: string): Promise<Object | void> {
try {
iframeOpen(url);
return Promise.resolve();

View File

@@ -1,394 +1,2 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule PanResponder
* @noflow
*/
import TouchHistoryMath from '../../vendor/TouchHistoryMath';
const currentCentroidXOfTouchesChangedAfter =
TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
const currentCentroidYOfTouchesChangedAfter =
TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
const previousCentroidXOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
const previousCentroidYOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
const currentCentroidX = TouchHistoryMath.currentCentroidX;
const currentCentroidY = TouchHistoryMath.currentCentroidY;
/**
* `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.
*
* It provides a predictable wrapper of the responder handlers provided by the
* [gesture responder system](docs/gesture-responder-system.html).
* For each handler, it provides a new `gestureState` object alongside the
* native event object:
*
* ```
* onPanResponderMove: (event, gestureState) => {}
* ```
*
* A native event is a synthetic touch event with the following form:
*
* - `nativeEvent`
* + `changedTouches` - Array of all touch events that have changed since the last event
* + `identifier` - The ID of the touch
* + `locationX` - The X position of the touch, relative to the element
* + `locationY` - The Y position of the touch, relative to the element
* + `pageX` - The X position of the touch, relative to the root element
* + `pageY` - The Y position of the touch, relative to the root element
* + `target` - The node id of the element receiving the touch event
* + `timestamp` - A time identifier for the touch, useful for velocity calculation
* + `touches` - Array of all current touches on the screen
*
* A `gestureState` object has the following:
*
* - `stateID` - ID of the gestureState- persisted as long as there at least
* one touch on screen
* - `moveX` - the latest screen coordinates of the recently-moved touch
* - `moveY` - the latest screen coordinates of the recently-moved touch
* - `x0` - the screen coordinates of the responder grant
* - `y0` - the screen coordinates of the responder grant
* - `dx` - accumulated distance of the gesture since the touch started
* - `dy` - accumulated distance of the gesture since the touch started
* - `vx` - current velocity of the gesture
* - `vy` - current velocity of the gesture
* - `numberActiveTouches` - Number of touches currently on screen
*
* ### Basic Usage
*
* ```
* componentWillMount: function() {
* this._panResponder = PanResponder.create({
* // Ask to be the responder:
* onStartShouldSetPanResponder: (evt, gestureState) => true,
* onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
* onMoveShouldSetPanResponder: (evt, gestureState) => true,
* onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
*
* onPanResponderGrant: (evt, gestureState) => {
* // The guesture has started. Show visual feedback so the user knows
* // what is happening!
*
* // gestureState.{x,y}0 will be set to zero now
* },
* onPanResponderMove: (evt, gestureState) => {
* // The most recent move distance is gestureState.move{X,Y}
*
* // The accumulated gesture distance since becoming responder is
* // gestureState.d{x,y}
* },
* onPanResponderTerminationRequest: (evt, gestureState) => true,
* onPanResponderRelease: (evt, gestureState) => {
* // The user has released all touches while this view is the
* // responder. This typically means a gesture has succeeded
* },
* onPanResponderTerminate: (evt, gestureState) => {
* // Another component has become the responder, so this gesture
* // should be cancelled
* },
* onShouldBlockNativeResponder: (evt, gestureState) => {
* // Returns whether this component should block native components from becoming the JS
* // responder. Returns true by default. Is currently only supported on android.
* return true;
* },
* });
* },
*
* render: function() {
* return (
* <View {...this._panResponder.panHandlers} />
* );
* },
*
* ```
*
* ### Working Example
*
* To see it in action, try the
* [PanResponder example in UIExplorer](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/PanResponderExample.js)
*/
const PanResponder = {
/**
*
* A graphical explanation of the touch data flow:
*
* +----------------------------+ +--------------------------------+
* | ResponderTouchHistoryStore | |TouchHistoryMath |
* +----------------------------+ +----------+---------------------+
* |Global store of touchHistory| |Allocation-less math util |
* |including activeness, start | |on touch history (centroids |
* |position, prev/cur position.| |and multitouch movement etc) |
* | | | |
* +----^-----------------------+ +----^---------------------------+
* | |
* | (records relevant history |
* | of touches relevant for |
* | implementing higher level |
* | gestures) |
* | |
* +----+-----------------------+ +----|---------------------------+
* | ResponderEventPlugin | | | Your App/Component |
* +----------------------------+ +----|---------------------------+
* |Negotiates which view gets | Low level | | High level |
* |onResponderMove events. | events w/ | +-+-------+ events w/ |
* |Also records history into | touchHistory| | Pan | multitouch + |
* |ResponderTouchHistoryStore. +---------------->Responder+-----> accumulative|
* +----------------------------+ attached to | | | distance and |
* each event | +---------+ velocity. |
* | |
* | |
* +--------------------------------+
*
*
*
* Gesture that calculates cumulative movement over time in a way that just
* "does the right thing" for multiple touches. The "right thing" is very
* nuanced. When moving two touches in opposite directions, the cumulative
* distance is zero in each dimension. When two touches move in parallel five
* pixels in the same direction, the cumulative distance is five, not ten. If
* two touches start, one moves five in a direction, then stops and the other
* touch moves fives in the same direction, the cumulative distance is ten.
*
* This logic requires a kind of processing of time "clusters" of touch events
* so that two touch moves that essentially occur in parallel but move every
* other frame respectively, are considered part of the same movement.
*
* Explanation of some of the non-obvious fields:
*
* - moveX/moveY: If no move event has been observed, then `(moveX, moveY)` is
* invalid. If a move event has been observed, `(moveX, moveY)` is the
* centroid of the most recently moved "cluster" of active touches.
* (Currently all move have the same timeStamp, but later we should add some
* threshold for what is considered to be "moving"). If a palm is
* accidentally counted as a touch, but a finger is moving greatly, the palm
* will move slightly, but we only want to count the single moving touch.
* - x0/y0: Centroid location (non-cumulative) at the time of becoming
* responder.
* - dx/dy: Cumulative touch distance - not the same thing as sum of each touch
* distance. Accounts for touch moves that are clustered together in time,
* moving the same direction. Only valid when currently responder (otherwise,
* it only represents the drag distance below the threshold).
* - vx/vy: Velocity.
*/
_initializeGestureState: function(gestureState) {
gestureState.moveX = 0;
gestureState.moveY = 0;
gestureState.x0 = 0;
gestureState.y0 = 0;
gestureState.dx = 0;
gestureState.dy = 0;
gestureState.vx = 0;
gestureState.vy = 0;
gestureState.numberActiveTouches = 0;
// All `gestureState` accounts for timeStamps up until:
gestureState._accountsForMovesUpTo = 0;
},
/**
* This is nuanced and is necessary. It is incorrect to continuously take all
* active *and* recently moved touches, find the centroid, and track how that
* result changes over time. Instead, we must take all recently moved
* touches, and calculate how the centroid has changed just for those
* recently moved touches, and append that change to an accumulator. This is
* to (at least) handle the case where the user is moving three fingers, and
* then one of the fingers stops but the other two continue.
*
* This is very different than taking all of the recently moved touches and
* storing their centroid as `dx/dy`. For correctness, we must *accumulate
* changes* in the centroid of recently moved touches.
*
* There is also some nuance with how we handle multiple moved touches in a
* single event. With the way `ReactNativeEventEmitter` dispatches touches as
* individual events, multiple touches generate two 'move' events, each of
* them triggering `onResponderMove`. But with the way `PanResponder` works,
* all of the gesture inference is performed on the first dispatch, since it
* looks at all of the touches (even the ones for which there hasn't been a
* native dispatch yet). Therefore, `PanResponder` does not call
* `onResponderMove` passed the first dispatch. This diverges from the
* typical responder callback pattern (without using `PanResponder`), but
* avoids more dispatches than necessary.
*/
_updateGestureStateOnMove: function(gestureState, touchHistory) {
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
gestureState.moveX = currentCentroidXOfTouchesChangedAfter(
touchHistory,
gestureState._accountsForMovesUpTo
);
gestureState.moveY = currentCentroidYOfTouchesChangedAfter(
touchHistory,
gestureState._accountsForMovesUpTo
);
const movedAfter = gestureState._accountsForMovesUpTo;
const prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
const x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
const prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
const y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
const nextDX = gestureState.dx + (x - prevX);
const nextDY = gestureState.dy + (y - prevY);
// TODO: This must be filtered intelligently.
const dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
gestureState.vx = (nextDX - gestureState.dx) / dt;
gestureState.vy = (nextDY - gestureState.dy) / dt;
gestureState.dx = nextDX;
gestureState.dy = nextDY;
gestureState._accountsForMovesUpTo = touchHistory.mostRecentTimeStamp;
},
/**
* @param {object} config Enhanced versions of all of the responder callbacks
* that provide not only the typical `ResponderSyntheticEvent`, but also the
* `PanResponder` gesture state. Simply replace the word `Responder` with
* `PanResponder` in each of the typical `onResponder*` callbacks. For
* example, the `config` object would look like:
*
* - `onMoveShouldSetPanResponder: (e, gestureState) => {...}`
* - `onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}`
* - `onStartShouldSetPanResponder: (e, gestureState) => {...}`
* - `onStartShouldSetPanResponderCapture: (e, gestureState) => {...}`
* - `onPanResponderReject: (e, gestureState) => {...}`
* - `onPanResponderGrant: (e, gestureState) => {...}`
* - `onPanResponderStart: (e, gestureState) => {...}`
* - `onPanResponderEnd: (e, gestureState) => {...}`
* - `onPanResponderRelease: (e, gestureState) => {...}`
* - `onPanResponderMove: (e, gestureState) => {...}`
* - `onPanResponderTerminate: (e, gestureState) => {...}`
* - `onPanResponderTerminationRequest: (e, gestureState) => {...}`
* - `onShouldBlockNativeResponder: (e, gestureState) => {...}`
*
* In general, for events that have capture equivalents, we update the
* gestureState once in the capture phase and can use it in the bubble phase
* as well.
*
* Be careful with onStartShould* callbacks. They only reflect updated
* `gestureState` for start/end events that bubble/capture to the Node.
* Once the node is the responder, you can rely on every start/end event
* being processed by the gesture and `gestureState` being updated
* accordingly. (numberActiveTouches) may not be totally accurate unless you
* are the responder.
*/
create: function(config) {
const gestureState = {
// Useful for debugging
stateID: Math.random()
};
PanResponder._initializeGestureState(gestureState);
const panHandlers = {
onStartShouldSetResponder: function(e) {
return config.onStartShouldSetPanResponder === undefined
? false
: config.onStartShouldSetPanResponder(e, gestureState);
},
onMoveShouldSetResponder: function(e) {
return config.onMoveShouldSetPanResponder === undefined
? false
: config.onMoveShouldSetPanResponder(e, gestureState);
},
onStartShouldSetResponderCapture: function(e) {
// TODO: Actually, we should reinitialize the state any time
// touches.length increases from 0 active to > 0 active.
if (e.nativeEvent.touches) {
if (e.nativeEvent.touches.length === 1) {
PanResponder._initializeGestureState(gestureState);
}
} else if (
e.nativeEvent.originalEvent &&
e.nativeEvent.originalEvent.type === 'mousedown'
) {
PanResponder._initializeGestureState(gestureState);
}
gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;
return config.onStartShouldSetPanResponderCapture !== undefined
? config.onStartShouldSetPanResponderCapture(e, gestureState)
: false;
},
onMoveShouldSetResponderCapture: function(e) {
const touchHistory = e.touchHistory;
// Responder system incorrectly dispatches should* to current responder
// Filter out any touch moves past the first one - we would have
// already processed multi-touch geometry during the first event.
if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
return false;
}
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
return config.onMoveShouldSetPanResponderCapture
? config.onMoveShouldSetPanResponderCapture(e, gestureState)
: false;
},
onResponderGrant: function(e) {
gestureState.x0 = currentCentroidX(e.touchHistory);
gestureState.y0 = currentCentroidY(e.touchHistory);
gestureState.dx = 0;
gestureState.dy = 0;
config.onPanResponderGrant && config.onPanResponderGrant(e, gestureState);
// TODO: t7467124 investigate if this can be removed
return config.onShouldBlockNativeResponder === undefined
? true
: config.onShouldBlockNativeResponder();
},
onResponderReject: function(e) {
config.onPanResponderReject && config.onPanResponderReject(e, gestureState);
},
onResponderRelease: function(e) {
config.onPanResponderRelease && config.onPanResponderRelease(e, gestureState);
PanResponder._initializeGestureState(gestureState);
},
onResponderStart: function(e) {
const touchHistory = e.touchHistory;
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
config.onPanResponderStart && config.onPanResponderStart(e, gestureState);
},
onResponderMove: function(e) {
const touchHistory = e.touchHistory;
// Guard against the dispatch of two touch moves when there are two
// simultaneously changed touches.
if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
return;
}
// Filter out any touch moves past the first one - we would have
// already processed multi-touch geometry during the first event.
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
config.onPanResponderMove && config.onPanResponderMove(e, gestureState);
},
onResponderEnd: function(e) {
const touchHistory = e.touchHistory;
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
config.onPanResponderEnd && config.onPanResponderEnd(e, gestureState);
},
onResponderTerminate: function(e) {
config.onPanResponderTerminate && config.onPanResponderTerminate(e, gestureState);
PanResponder._initializeGestureState(gestureState);
},
onResponderTerminationRequest: function(e) {
return config.onPanResponderTerminationRequest === undefined
? true
: config.onPanResponderTerminationRequest(e, gestureState);
}
};
return { panHandlers: panHandlers };
}
};
import PanResponder from '../../vendor/PanResponder';
export default PanResponder;

View File

@@ -182,7 +182,7 @@ export default class StyleRegistry {
}
/**
* Caching layer over 'resolveStyle'
* Caching layer over 'resolveStyle'
*/
_resolveStyleIfNeeded(style, options, key) {
const dir = I18nManager.isRTL ? 'rtl' : 'ltr';

View File

@@ -24,7 +24,7 @@ import { number, oneOf, string } from 'prop-types';
const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
export default class StyleSheetValidation {
static validateStyleProp(prop, style, caller) {
static validateStyleProp(prop: string, style: Object, caller: string) {
if (process.env.NODE_ENV !== 'production') {
const isCustomProperty = prop.indexOf('--') === 0;
if (isCustomProperty) return;
@@ -51,7 +51,7 @@ export default class StyleSheetValidation {
}
}
static validateStyle(name, styles) {
static validateStyle(name: string, styles: Object) {
if (process.env.NODE_ENV !== 'production') {
for (const prop in styles[name]) {
StyleSheetValidation.validateStyleProp(prop, styles[name], 'StyleSheet ' + name);
@@ -59,7 +59,7 @@ export default class StyleSheetValidation {
}
}
static addValidStylePropTypes(stylePropTypes) {
static addValidStylePropTypes(stylePropTypes: Object) {
for (const key in stylePropTypes) {
allStylePropTypes[key] = stylePropTypes[key];
}

View File

@@ -34,6 +34,9 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-backgroundColor-wib322{background-color:transparent}
.rn-backgroundColor-8ndhhv{background-color:rgba(33,150,243,1)}
.rn-backgroundColor-15al3ab{background-color:rgba(223,223,223,1)}
.rn-backgroundColor-44z8sh{background-color:rgba(255,255,255,1)}
.rn-backgroundColor-5itogg{background-color:rgba(0,150,136,1)}
.rn-backgroundColor-1v82r4u{background-color:rgba(170,184,194,1)}
.rn-backgroundColor-1hj8efq{background-color:rgba(213,213,213,1)}
.rn-backgroundColor-1bgzomc{background-color:rgba(189,189,189,1)}
.rn-color-homxoj{color:inherit}
@@ -51,9 +54,13 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
.rn-borderLeftStyle-mm0ijv{border-left-style:solid}
.rn-borderTopWidth-13yce4e{border-top-width:0px}
.rn-borderTopWidth-1jxfwug{border-top-width:2px}
.rn-borderRightWidth-fnigne{border-right-width:0px}
.rn-borderRightWidth-18p6if4{border-right-width:2px}
.rn-borderBottomWidth-ndvcnb{border-bottom-width:0px}
.rn-borderBottomWidth-wgabs5{border-bottom-width:2px}
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.rn-borderLeftWidth-dwliz8{border-left-width:2px}
.rn-boxSizing-deolkf{box-sizing:border-box}
.rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
.rn-display-xoduu5{display:-webkit-inline-box;display:-moz-inline-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}
@@ -93,6 +100,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-height-1pi2tsx{height:100%}
.rn-height-z80fyv{height:20px}
.rn-height-1r8g8re{height:36px}
.rn-height-10ptun7{height:16px}
.rn-height-4v7adb{height:5px}
.rn-height-1dernwh{height:70%}
.rn-opacity-1272l3b{opacity:0}
@@ -100,6 +108,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-width-13qz1uu{width:100%}
.rn-width-19wmn03{width:20px}
.rn-width-1acpoxo{width:36px}
.rn-width-1janqcz{width:16px}
.rn-touchAction-19z077z{-ms-touch-action:none;touch-action:none}
.rn-touchAction-1gvxusu{-ms-touch-action:manipulate;touch-action:manipulate}
.rn-WebkitOverflowScrolling-150rngu{-webkit-overflow-scrolling:touch}
@@ -147,12 +156,29 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-borderBottomLeftRadius-pm2fo{border-bottom-left-radius:0px}
.rn-fontWeight-majxgm{font-weight:500}
.rn-textTransform-tsynxw{text-transform:uppercase}
.rn-borderTopColor-j4x2lb{border-top-color:rgba(101,119,134,1)}
.rn-borderTopColor-gj2eto{border-top-color:rgba(0,150,136,1)}
.rn-borderTopColor-1j7vz2b{border-top-color:rgba(204,214,221,1)}
.rn-borderTopColor-2dclza{border-top-color:rgba(170,184,194,1)}
.rn-borderTopColor-kqr9px{border-top-color:black}
.rn-borderRightColor-12i18q1{border-right-color:rgba(101,119,134,1)}
.rn-borderRightColor-31ud7z{border-right-color:rgba(0,150,136,1)}
.rn-borderRightColor-10fg1ub{border-right-color:rgba(204,214,221,1)}
.rn-borderRightColor-8jf312{border-right-color:rgba(170,184,194,1)}
.rn-borderRightColor-q0dj5p{border-right-color:black}
.rn-borderBottomColor-mg3rfb{border-bottom-color:rgba(101,119,134,1)}
.rn-borderBottomColor-1bgnb8i{border-bottom-color:rgba(0,150,136,1)}
.rn-borderBottomColor-zxuuv6{border-bottom-color:rgba(204,214,221,1)}
.rn-borderBottomColor-1yeakrt{border-bottom-color:rgba(170,184,194,1)}
.rn-borderBottomColor-1ah7hsa{border-bottom-color:black}
.rn-borderLeftColor-vnhemr{border-left-color:rgba(101,119,134,1)}
.rn-borderLeftColor-tbzcuz{border-left-color:rgba(0,150,136,1)}
.rn-borderLeftColor-1p34dw6{border-left-color:rgba(204,214,221,1)}
.rn-borderLeftColor-bluj2i{border-left-color:rgba(170,184,194,1)}
.rn-borderLeftColor-137uh4u{border-left-color:black}
.rn-backgroundImage-rs94m5{background-image:url(\\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\\")}
.rn-alignSelf-k200y{-ms-flex-item-align:start;-webkit-align-self:flex-start;align-self:flex-start}
.rn-boxShadow-1ewcgjf{box-shadow:0px 1px 3px rgba(0,0,0,0.5)}
.rn-borderTopColor-kqr9px{border-top-color:black}
.rn-borderRightColor-q0dj5p{border-right-color:black}
.rn-borderBottomColor-1ah7hsa{border-bottom-color:black}
.rn-borderLeftColor-137uh4u{border-left-color:black}
.rn-resize-1dz5y72{resize:none}",
},
]

View File

@@ -22,6 +22,18 @@ describe('apis/StyleSheet', () => {
expect(isPlainObject(StyleSheet.absoluteFillObject) === true).toBeTruthy();
});
describe('compose', () => {
test('returns array when neither style is falsey', () => {
expect(StyleSheet.compose(1, 2)).toEqual([1, 2]);
});
test('returns style1 when style2 is falsey', () => {
expect(StyleSheet.compose(1, null)).toBe(1);
});
test('returns style2 when style1 is falsey', () => {
expect(StyleSheet.compose(null, 2)).toBe(2);
});
});
describe('create', () => {
test('replaces styles with numbers', () => {
const style = StyleSheet.create({ root: { position: 'absolute' } });

View File

@@ -0,0 +1,22 @@
/**
* Based on http://dieulot.net/css-retina-hairline
* @noflow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
const getHairlineWidth = () => {
let hairlineWidth = 1;
if (canUseDOM && window.devicePixelRatio && window.devicePixelRatio >= 2) {
const node = document.createElement('div');
node.style.border = '.5px solid transparent';
document.body.appendChild(node);
if (node.offsetHeight === 1) {
hairlineWidth = 0.5;
}
document.body.removeChild(node);
}
return hairlineWidth;
};
export default getHairlineWidth;

View File

@@ -10,6 +10,7 @@
*/
import flattenStyle from './flattenStyle';
import getHairlineWidth from './getHairlineWidth';
import modality from '../../modules/modality';
import StyleRegistry from './registry';
@@ -36,6 +37,13 @@ const absoluteFill = StyleRegistry.register(absoluteFillObject);
const StyleSheet = {
absoluteFill,
absoluteFillObject,
compose(style1, style2) {
if (style1 && style2) {
return [style1, style2];
} else {
return style1 || style2;
}
},
create(styles) {
const result = {};
Object.keys(styles).forEach(key => {
@@ -51,7 +59,7 @@ const StyleSheet = {
getStyleSheets() {
return StyleRegistry.getStyleSheets();
},
hairlineWidth: 1
hairlineWidth: getHairlineWidth()
};
export default StyleSheet;

View File

@@ -0,0 +1,2 @@
import ART from 'react-art';
export default ART;

View File

@@ -21,7 +21,7 @@ const createSvgCircle = style => (
<circle cx="16" cy="16" fill="none" r="14" strokeWidth="4" style={style} />
);
class ActivityIndicator extends Component {
class ActivityIndicator extends Component<*> {
static displayName = 'ActivityIndicator';
static propTypes = {

View File

@@ -1,4 +1,5 @@
/* eslint-env jasmine, jest */
/* eslint-disable react/jsx-no-bind */
import React from 'react';
import Button from '..';

View File

@@ -17,7 +17,7 @@ import Text from '../Text';
import { bool, func, string } from 'prop-types';
import React, { Component } from 'react';
class Button extends Component {
class Button extends Component<*> {
static propTypes = {
accessibilityLabel: string,
color: ColorPropType,

View File

@@ -0,0 +1,60 @@
/* eslint-env jest */
import CheckBox from '../';
import React from 'react';
import { shallow } from 'enzyme';
const checkboxSelector = 'input[type="checkbox"]';
describe('CheckBox', () => {
describe('disabled', () => {
test('when "false" a default checkbox is rendered', () => {
const component = shallow(<CheckBox />);
expect(component.find(checkboxSelector).prop('disabled')).toBe(false);
});
test('when "true" a disabled checkbox is rendered', () => {
const component = shallow(<CheckBox disabled />);
expect(component.find(checkboxSelector).prop('disabled')).toBe(true);
});
});
describe('onChange', () => {
test('is called with the event object', () => {
const onChange = jest.fn();
const component = shallow(<CheckBox onChange={onChange} value={false} />);
component.find('input').simulate('change', { nativeEvent: { target: { checked: true } } });
expect(onChange).toHaveBeenCalledWith({
nativeEvent: { target: { checked: true }, value: true }
});
});
});
describe('onValueChange', () => {
test('when value is "false" it receives "true"', () => {
const onValueChange = jest.fn();
const component = shallow(<CheckBox onValueChange={onValueChange} value={false} />);
component.find('input').simulate('change', { nativeEvent: { target: { checked: true } } });
expect(onValueChange).toHaveBeenCalledWith(true);
});
test('when value is "true" it receives "false"', () => {
const onValueChange = jest.fn();
const component = shallow(<CheckBox onValueChange={onValueChange} value />);
component.find('input').simulate('change', { nativeEvent: { target: { checked: false } } });
expect(onValueChange).toHaveBeenCalledWith(false);
});
});
describe('value', () => {
test('when "false" an unchecked checkbox is rendered', () => {
const component = shallow(<CheckBox value={false} />);
expect(component.find(checkboxSelector).prop('checked')).toBe(false);
});
test('when "true" a checked checkbox is rendered', () => {
const component = shallow(<CheckBox value />);
expect(component.find(checkboxSelector).prop('checked')).toBe(true);
});
});
});

View File

@@ -0,0 +1,162 @@
/**
* Copyright (c) 2017-present, Nicolas Gallagher.
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule CheckBox
* @flow
*/
import applyNativeMethods from '../../modules/applyNativeMethods';
import ColorPropType from '../../propTypes/ColorPropType';
import createElement from '../../modules/createElement';
import StyleSheet from '../../apis/StyleSheet';
import UIManager from '../../apis/UIManager';
import View from '../View';
import ViewPropTypes, { type ViewProps } from '../View/ViewPropTypes';
import React, { Component } from 'react';
import { bool, func } from 'prop-types';
type Props = ViewProps & {
color?: ColorPropType,
disabled?: boolean,
onChange?: Function,
onValueChange?: Function,
value?: boolean
};
class CheckBox extends Component<Props> {
_checkboxElement: HTMLInputElement;
static displayName = 'CheckBox';
static propTypes = {
...ViewPropTypes,
color: ColorPropType,
disabled: bool,
onChange: func,
onValueChange: func,
value: bool
};
static defaultProps = {
disabled: false,
value: false
};
blur() {
UIManager.blur(this._checkboxElement);
}
focus() {
UIManager.focus(this._checkboxElement);
}
render() {
const {
color,
disabled,
/* eslint-disable */
onChange,
onValueChange,
/* eslint-enable */
style,
value,
...other
} = this.props;
const fakeControl = (
<View
style={[
styles.fakeControl,
value && styles.fakeControlChecked,
// custom color
value && color && { backgroundColor: color, borderColor: color },
disabled && styles.fakeControlDisabled,
value && disabled && styles.fakeControlCheckedAndDisabled
]}
/>
);
const nativeControl = createElement('input', {
checked: value,
disabled: disabled,
onChange: this._handleChange,
ref: this._setCheckboxRef,
style: [styles.nativeControl, styles.cursorInherit],
type: 'checkbox'
});
return (
<View {...other} style={[styles.root, style, disabled && styles.cursorDefault]}>
{fakeControl}
{nativeControl}
</View>
);
}
_handleChange = (event: Object) => {
const { onChange, onValueChange } = this.props;
const value = event.nativeEvent.target.checked;
event.nativeEvent.value = value;
onChange && onChange(event);
onValueChange && onValueChange(value);
};
_setCheckboxRef = element => {
this._checkboxElement = element;
};
}
const styles = StyleSheet.create({
root: {
cursor: 'pointer',
height: 16,
userSelect: 'none',
width: 16
},
cursorDefault: {
cursor: 'default'
},
cursorInherit: {
cursor: 'inherit'
},
fakeControl: {
alignItems: 'center',
backgroundColor: '#fff',
borderColor: '#657786',
borderRadius: 2,
borderStyle: 'solid',
borderWidth: 2,
height: '100%',
justifyContent: 'center',
width: '100%'
},
fakeControlChecked: {
backgroundColor: '#009688',
backgroundImage:
'url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K")',
backgroundRepeat: 'no-repeat',
borderColor: '#009688'
},
fakeControlDisabled: {
borderColor: '#CCD6DD'
},
fakeControlCheckedAndDisabled: {
backgroundColor: '#AAB8C2',
borderColor: '#AAB8C2'
},
nativeControl: {
...StyleSheet.absoluteFillObject,
height: '100%',
margin: 0,
opacity: 0,
padding: 0,
width: '100%'
}
});
export default applyNativeMethods(CheckBox);

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/
import ensureComponentIsNative from '../Touchable/ensureComponentIsNative';
import Image from './';
import StyleSheet from '../../apis/StyleSheet';
import View from '../View';
import ViewPropTypes from '../View/ViewPropTypes';
import React, { Component } from 'react';
const emptyObject = {};
/**
* Very simple drop-in replacement for <Image> which supports nesting views.
*/
class ImageBackground extends Component<*> {
static propTypes = {
...Image.propTypes,
imageStyle: Image.propTypes.style,
style: ViewPropTypes.style
};
static defaultProps = {
style: emptyObject
};
setNativeProps(props: Object) {
// Work-around flow
const viewRef = this._viewRef;
if (viewRef) {
ensureComponentIsNative(viewRef);
viewRef.setNativeProps(props);
}
}
_viewRef: ?View = null;
_captureRef = (ref: View) => {
this._viewRef = ref;
};
render() {
const { children, style, imageStyle, imageRef, ...props } = this.props;
return (
<View ref={this._captureRef} style={style}>
<Image
{...props}
ref={imageRef}
style={[
StyleSheet.absoluteFill,
{
// Temporary Workaround:
// Current (imperfect yet) implementation of <Image> overwrites width and height styles
// (which is not quite correct), and these styles conflict with explicitly set styles
// of <ImageBackground> and with our internal layout model here.
// So, we have to proxy/reapply these styles explicitly for actual <Image> component.
// This workaround should be removed after implementing proper support of
// intrinsic content size of the <Image>.
width: style.width,
height: style.height
},
imageStyle
]}
/>
{children}
</View>
);
}
}
export default ImageBackground;

View File

@@ -0,0 +1,36 @@
/* eslint-env jasmine, jest */
import Image from '..';
import ImageBackground from '../ImageBackground';
import React from 'react';
import { shallow } from 'enzyme';
import Text from '../../Text';
describe('components/ImageBackground', () => {
describe('prop "children"', () => {
it('render child content', () => {
const component = shallow(
<ImageBackground>
<Text>Hello World!</Text>
</ImageBackground>
);
expect(component.find(Text)).toBeDefined();
});
});
describe('prop "imageStyle"', () => {
it('sets the style of the underlying Image', () => {
const imageStyle = { width: 40, height: 60 };
const component = shallow(<ImageBackground imageStyle={imageStyle} />);
expect(component.find(Image).prop('style')).toContain(imageStyle);
});
});
describe('prop "style"', () => {
it('sets the style of the container View', () => {
const style = { margin: 40 };
const component = shallow(<ImageBackground style={style} />);
expect(component.prop('style')).toEqual(style);
});
});
});

View File

@@ -1,4 +1,5 @@
/* eslint-env jasmine, jest */
/* eslint-disable react/jsx-no-bind */
import Image from '../';
import ImageLoader from '../../../modules/ImageLoader';
@@ -32,12 +33,6 @@ describe('components/Image', () => {
expect(component.prop('accessible')).toBe(false);
});
test('prop "children"', () => {
const children = <div className="unique" />;
const component = shallow(<Image children={children} />);
expect(component.find('.unique').length).toBe(1);
});
describe('prop "defaultSource"', () => {
test('sets background image when value is an object', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
@@ -120,7 +115,7 @@ describe('components/Image', () => {
const onLoadStub = jest.fn();
const uri = 'https://test.com/img.jpg';
const component = mount(<Image onLoad={onLoadStub} source={uri} />);
component.setProps({ source: `https://blah.com/img.png` });
component.setProps({ source: 'https://blah.com/img.png' });
jest.runOnlyPendingTimers();
expect(onLoadStub.mock.calls.length).toBe(2);
});

View File

@@ -22,7 +22,7 @@ import StyleSheet from '../../apis/StyleSheet';
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
import View from '../View';
import ViewPropTypes from '../View/ViewPropTypes';
import { any, bool, func, number, oneOf, oneOfType, shape, string } from 'prop-types';
import { bool, func, number, oneOf, oneOfType, shape, string } from 'prop-types';
import React, { Component } from 'react';
const emptyObject = {};
@@ -83,9 +83,11 @@ const resolveAssetSource = source => {
return uri;
};
class Image extends Component {
state: { shouldDisplaySource: boolean };
type State = {
shouldDisplaySource: boolean
};
class Image extends Component<*, State> {
static displayName = 'Image';
static contextTypes = {
@@ -94,7 +96,6 @@ class Image extends Component {
static propTypes = {
...ViewPropTypes,
children: any,
defaultSource: ImageSourcePropType,
draggable: bool,
onError: func,
@@ -174,7 +175,6 @@ class Image extends Component {
const {
accessibilityLabel,
accessible,
children,
defaultSource,
draggable,
onLayout,
@@ -219,6 +219,18 @@ class Image extends Component {
})
: null;
if (process.env.NODE_ENV !== 'production') {
if (this.props.src) {
console.warn('The <Image> component requires a `source` property rather than `src`.');
}
if (this.props.children) {
throw new Error(
'The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.'
);
}
}
return (
<View
{...other}
@@ -229,7 +241,6 @@ class Image extends Component {
testID={testID}
>
{hiddenImage}
{children}
</View>
);
}

View File

@@ -17,7 +17,7 @@ import React, { Component } from 'react';
import type { ViewLayout, ViewLayoutEvent } from '../View/ViewPropTypes';
class KeyboardAvoidingView extends Component {
class KeyboardAvoidingView extends Component<*> {
static propTypes = {
...ViewPropTypes,
behavior: oneOf(['height', 'padding', 'position']),

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2017-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import React from 'react';
import Picker from './';
const PickerItemPropType = (props: Object, propName: string, componentName: string) => {
const prop = props[propName];
let error = null;
React.Children.forEach(prop, function(child) {
if (child.type !== Picker.Item) {
error = new Error('`Picker` children must be of type `Picker.Item`.');
}
});
return error;
};
export default PickerItemPropType;

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2017-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import ColorPropType from '../../propTypes/ColorPropType';
import ViewStylePropTypes from '../View/ViewStylePropTypes';
const PickerStylePropTypes = {
...ViewStylePropTypes,
color: ColorPropType
};
export default PickerStylePropTypes;

View File

@@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/Picker prop "children" renders items 1`] = `
<select
className="rn-fontFamily-poiln3 rn-fontSize-7cikom rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw"
onChange={[Function]}
>
<PickerItem
label="label-1"
value="value-1"
/>
<PickerItem
label="label-2"
value="value-2"
/>
</select>
`;

View File

@@ -0,0 +1,74 @@
/* eslint-env jasmine, jest */
import React from 'react';
import { shallow } from 'enzyme';
import Picker from '..';
describe('components/Picker', () => {
describe('prop "children"', () => {
test('renders items', () => {
const picker = (
<Picker>
<Picker.Item label="label-1" value="value-1" />
<Picker.Item label="label-2" value="value-2" />
</Picker>
);
const component = shallow(picker);
expect(component).toMatchSnapshot();
});
});
describe('prop "enabled"', () => {
test('picker is disabled if false', () => {
const picker = (
<Picker enabled={false}>
<Picker.Item label="label-1" value="value-1" />
<Picker.Item label="label-2" value="value-2" />
</Picker>
);
const component = shallow(picker);
expect(component.find('select').props().disabled).toBe(true);
});
});
describe('prop "onValueChange"', () => {
test('is called with (value, index)', () => {
const onValueChange = jest.fn();
const picker = (
<Picker onValueChange={onValueChange} selectedValue="value-1">
<Picker.Item label="label-1" value="value-1" />
<Picker.Item label="label-2" value="value-2" />
</Picker>
);
const component = shallow(picker);
component.find('select').simulate('change', {
target: { selectedIndex: '1', value: 'value-2' }
});
expect(onValueChange).toHaveBeenCalledWith('value-2', '1');
});
});
describe('prop "selectedValue"', () => {
test('selects the correct item (string)', () => {
const picker = (
<Picker selectedValue="value-2">
<Picker.Item label="label-1" value="value-1" />
<Picker.Item label="label-2" value="value-2" />
</Picker>
);
const component = shallow(picker);
expect(component.find('select').prop('value')).toBe('value-2');
});
test('selects the correct item (number)', () => {
const picker = (
<Picker selectedValue={22}>
<Picker.Item label="label-1" value={11} />
<Picker.Item label="label-2" value={22} />
</Picker>
);
const component = shallow(picker);
expect(component.find('select').prop('value')).toBe(22);
});
});
});

View File

@@ -1,4 +1,114 @@
import UnimplementedView from '../UnimplementedView';
const Picker = UnimplementedView;
Picker.Item = UnimplementedView;
export default Picker;
/**
* Copyright (c) 2017-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Picker
* @flow
*/
import applyNativeMethods from '../../modules/applyNativeMethods';
import { Component } from 'react';
import ColorPropType from '../../propTypes/ColorPropType';
import createElement from '../../modules/createElement';
import PickerItemPropType from './PickerItemPropType';
import PickerStylePropTypes from './PickerStylePropTypes';
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
import StyleSheet from '../../apis/StyleSheet';
import TextStylePropTypes from '../Text/TextStylePropTypes';
import { arrayOf, bool, func, number, oneOfType, string } from 'prop-types';
const pickerStyleType = StyleSheetPropType(PickerStylePropTypes);
const itemStylePropType = StyleSheetPropType(TextStylePropTypes);
type ItemProps = {
color?: ColorPropType,
label: string,
testID?: string,
value?: number | string
};
class PickerItem extends Component<ItemProps> {
static propTypes = {
color: ColorPropType,
label: string.isRequired,
testID: string,
value: oneOfType([number, string])
};
render() {
const { label, testID, value } = this.props;
return createElement('option', { label, testID, value });
}
}
type Props = {
children?: Array<typeof PickerItem>,
enabled?: boolean,
onValueChange?: Function,
selectedValue?: number | string,
style?: pickerStyleType,
testID?: string,
/* compat */
itemStyle?: itemStylePropType,
mode?: string,
prompt?: string
};
class Picker extends Component<Props> {
static propTypes = {
children: arrayOf(PickerItemPropType),
enabled: bool,
onValueChange: func,
selectedValue: oneOfType([number, string]),
style: pickerStyleType,
testID: string
};
static Item = PickerItem;
render() {
const {
children,
enabled,
selectedValue,
style,
testID,
/* eslint-disable */
itemStyle,
mode,
prompt
/* eslint-enable */
} = this.props;
return createElement('select', {
children,
disabled: enabled === false ? true : undefined,
onChange: this._handleChange,
style: [styles.initial, style],
testID,
value: selectedValue
});
}
_handleChange = (e: Object) => {
const { onValueChange } = this.props;
const { selectedIndex, value } = e.target;
if (onValueChange) {
onValueChange(value, selectedIndex);
}
};
}
const styles = StyleSheet.create({
initial: {
fontFamily: 'inherit',
fontSize: 'inherit',
margin: 0
}
});
export default applyNativeMethods(Picker);

View File

@@ -17,7 +17,7 @@ import ViewPropTypes from '../View/ViewPropTypes';
import React, { Component } from 'react';
import { bool, number } from 'prop-types';
class ProgressBar extends Component {
class ProgressBar extends Component<*> {
_progressElement: View;
static displayName = 'ProgressBar';

View File

@@ -16,7 +16,7 @@ import ViewPropTypes from '../View/ViewPropTypes';
import { arrayOf, bool, func, number, oneOf, string } from 'prop-types';
import React, { Component } from 'react';
class RefreshControl extends Component {
class RefreshControl extends Component<*> {
static propTypes = {
...ViewPropTypes,
colors: arrayOf(ColorPropType),

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule SafeAreaView
* @flow
*/
import View from '../View';
export default View;

View File

@@ -48,7 +48,7 @@ const normalizeScrollEvent = e => ({
/**
* Encapsulates the Web-specific scroll throttling and disabling logic
*/
export default class ScrollViewBase extends Component {
export default class ScrollViewBase extends Component<*> {
_viewRef: View;
static propTypes = {
@@ -148,7 +148,7 @@ export default class ScrollViewBase extends Component {
};
};
_handleScroll = (e: SyntheticEvent) => {
_handleScroll = (e: Object) => {
e.persist();
e.stopPropagation();
const { scrollEventThrottle } = this.props;

View File

@@ -28,7 +28,13 @@ import { Children, Component } from 'react';
* Typically, you will not need to use this component and should opt for normal
* React reconciliation.
*/
export default class StaticContainer extends Component {
type Props = {
children: any,
shouldUpdate: boolean
};
export default class StaticContainer extends Component<Props> {
static propTypes = {
children: any.isRequired,
shouldUpdate: bool.isRequired

View File

@@ -27,7 +27,12 @@ import { bool, func } from 'prop-types';
* React reconciliation.
*/
export default class StaticRenderer extends Component {
type Props = {
render: Function,
shouldUpdate: boolean
};
export default class StaticRenderer extends Component<Props> {
static propTypes = {
render: func.isRequired,
shouldUpdate: bool.isRequired

View File

@@ -11,7 +11,7 @@
import { Component } from 'react';
export default class StatusBar extends Component {
export default class StatusBar extends Component<*> {
static setBackgroundColor() {}
static setBarStyle() {}
static setHidden() {}

View File

@@ -1,4 +1,10 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Switch
* @flow
*/
@@ -11,14 +17,14 @@ import StyleSheet from '../../apis/StyleSheet';
import UIManager from '../../apis/UIManager';
import View from '../View';
import ViewPropTypes from '../View/ViewPropTypes';
import React, { PureComponent } from 'react';
import React, { Component } from 'react';
import { bool, func } from 'prop-types';
const emptyObject = {};
const thumbDefaultBoxShadow = '0px 1px 3px rgba(0,0,0,0.5)';
const thumbFocusedBoxShadow = `${thumbDefaultBoxShadow}, 0 0 0 10px rgba(0,0,0,0.1)`;
class Switch extends PureComponent {
class Switch extends Component<*> {
_checkboxElement: HTMLInputElement;
_thumbElement: View;

View File

@@ -1,4 +1,5 @@
/* eslint-env jasmine, jest */
/* eslint-disable react/jsx-no-bind */
import React from 'react';
import { render, shallow } from 'enzyme';

View File

@@ -18,7 +18,7 @@ import createElement from '../../modules/createElement';
import StyleSheet from '../../apis/StyleSheet';
import TextPropTypes from './TextPropTypes';
class Text extends Component {
class Text extends Component<*> {
static displayName = 'Text';
static propTypes = TextPropTypes;

View File

@@ -68,7 +68,7 @@ const setSelection = (node, selection) => {
} catch (e) {}
};
class TextInput extends Component {
class TextInput extends Component<*> {
_node: HTMLInputElement;
static displayName = 'TextInput';

View File

@@ -316,27 +316,32 @@ const LONG_PRESS_ALLOWED_MOVEMENT = 10;
* @lends Touchable.prototype
*/
const TouchableMixin = {
// HACK (part 1): basic support for touchable interactions using a keyboard
componentDidMount: function() {
this._touchableNode = findNodeHandle(this);
this._touchableBlurListener = e => {
if (this._isTouchableKeyboardActive) {
if (
this.state.touchable.touchState &&
this.state.touchable.touchState !== States.NOT_RESPONDER
) {
this.touchableHandleResponderTerminate({ nativeEvent: e });
if (this._touchableNode && this._touchableNode.addEventListener) {
this._touchableBlurListener = e => {
if (this._isTouchableKeyboardActive) {
if (
this.state.touchable.touchState &&
this.state.touchable.touchState !== States.NOT_RESPONDER
) {
this.touchableHandleResponderTerminate({ nativeEvent: e });
}
this._isTouchableKeyboardActive = false;
}
this._isTouchableKeyboardActive = false;
}
};
this._touchableNode.addEventListener('blur', this._touchableBlurListener);
};
this._touchableNode.addEventListener('blur', this._touchableBlurListener);
}
},
/**
* Clear all timeouts on unmount
*/
componentWillUnmount: function() {
this._touchableNode.removeEventListener('blur', this._touchableBlurListener);
if (this._touchableNode && this._touchableNode.addEventListener) {
this._touchableNode.removeEventListener('blur', this._touchableBlurListener);
}
this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout);
this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout);
this.pressOutDelayTimeout && clearTimeout(this.pressOutDelayTimeout);
@@ -791,7 +796,7 @@ const TouchableMixin = {
}
},
// HACK: basic support for touchable interactions using a keyboard (including
// HACK (part 2): basic support for touchable interactions using a keyboard (including
// delays and longPress)
touchableHandleKeyEvent: function(e: Event) {
const ENTER = 13;

View File

@@ -14,12 +14,12 @@ import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes';
import createReactClass from 'create-react-class';
import EdgeInsetsPropType from '../../propTypes/EdgeInsetsPropType';
import ensurePositiveDelayProps from './ensurePositiveDelayProps';
import React from 'react';
import React, { type Element } from 'react';
import StyleSheet from '../../apis/StyleSheet';
import TimerMixin from 'react-timer-mixin';
import Touchable from './Touchable';
import warning from 'fbjs/lib/warning';
import { bool, func, number, string } from 'prop-types';
import { any, bool, func, number, string } from 'prop-types';
type Event = Object;
@@ -44,6 +44,7 @@ const TouchableWithoutFeedback = createReactClass({
accessibilityRole: BaseComponentPropTypes.accessibilityRole,
accessibilityTraits: BaseComponentPropTypes.accessibilityTraits,
accessible: bool,
children: any,
/**
* Delay in ms, from onPressIn, before onLongPress is called.
*/
@@ -144,7 +145,7 @@ const TouchableWithoutFeedback = createReactClass({
return this.props.delayPressOut || 0;
},
render: function(): React.Element<any> {
render: function(): Element<any> {
const {
/* eslint-disable */
delayLongPress,

View File

@@ -17,7 +17,7 @@ import React, { Component } from 'react';
* Common implementation for a simple stubbed view.
*/
/* eslint-disable react/prop-types */
class UnimplementedView extends Component {
class UnimplementedView extends Component<*, *> {
setNativeProps() {
// Do nothing.
// This method is required in order to use this view as a Touchable* child.

View File

@@ -11,11 +11,13 @@
*/
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes';
import EdgeInsetsPropType from '../../propTypes/EdgeInsetsPropType';
import EdgeInsetsPropType, { type EdgeInsetsProp } from '../../propTypes/EdgeInsetsPropType';
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
import ViewStylePropTypes from './ViewStylePropTypes';
import { any, bool, func, oneOf } from 'prop-types';
const stylePropType = StyleSheetPropType(ViewStylePropTypes);
export type ViewLayout = {
x: number,
y: number,
@@ -29,6 +31,46 @@ export type ViewLayoutEvent = {
}
};
export type ViewProps = {
accessibilityComponentType?: string,
accessibilityLabel?: string,
accessibilityLiveRegion?: 'none' | 'polite' | 'assertive',
accessibilityRole?: string,
accessibilityTraits?: string | Array<string>,
accessible?: boolean,
children?: any,
collapsable?: boolean,
hitSlop?: EdgeInsetsProp,
importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants',
onAccessibilityTap?: Function,
onClick?: Function,
onClickCapture?: Function,
onLayout?: (event: ViewLayoutEvent) => void,
onMagicTap?: Function,
onResponderGrant?: Function,
onResponderMove?: Function,
onResponderReject?: Function,
onResponderRelease?: Function,
onResponderTerminate?: Function,
onResponderTerminationRequest?: Function,
onStartShouldSetResponder?: Function,
onStartShouldSetResponderCapture?: Function,
onMoveShouldSetResponder?: Function,
onMoveShouldSetResponderCapture?: Function,
onTouchCancel?: Function,
onTouchCancelCapture?: Function,
onTouchEnd?: Function,
onTouchEndCapture?: Function,
onTouchMove?: Function,
onTouchMoveCapture?: Function,
onTouchStart?: Function,
onTouchStartCapture?: Function,
pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto',
removeClippedSubviews?: boolean,
style?: stylePropType,
testID?: string
};
const ViewPropTypes = {
...BaseComponentPropTypes,
children: any,
@@ -56,7 +98,7 @@ const ViewPropTypes = {
onTouchStart: func,
onTouchStartCapture: func,
pointerEvents: oneOf(['auto', 'box-none', 'box-only', 'none']),
style: StyleSheetPropType(ViewStylePropTypes)
style: stylePropType
};
export default ViewPropTypes;

View File

@@ -13,7 +13,7 @@ import { bool } from 'prop-types';
import createElement from '../../modules/createElement';
import invariant from 'fbjs/lib/invariant';
import StyleSheet from '../../apis/StyleSheet';
import ViewPropTypes from './ViewPropTypes';
import ViewPropTypes, { type ViewProps } from './ViewPropTypes';
import React, { Component } from 'react';
const calculateHitSlopStyle = hitSlop => {
@@ -27,7 +27,7 @@ const calculateHitSlopStyle = hitSlop => {
return hitStyle;
};
class View extends Component {
class View extends Component<ViewProps> {
static displayName = 'View';
static contextTypes = {
@@ -39,7 +39,6 @@ class View extends Component {
render() {
const {
hitSlop,
style,
/* eslint-disable */
collapsable,
onAccessibilityTap,
@@ -61,7 +60,7 @@ class View extends Component {
const { isInAParentText } = this.context;
otherProps.style = [styles.initial, isInAParentText && styles.inline, style];
otherProps.style = [styles.initial, isInAParentText && styles.inline, this.props.style];
if (hitSlop) {
const hitSlopStyle = calculateHitSlopStyle(hitSlop);

View File

@@ -2,7 +2,8 @@ import createElement from './modules/createElement';
import findNodeHandle from './modules/findNodeHandle';
import NativeModules from './modules/NativeModules';
import processColor from './modules/processColor';
import { hydrate, render, unmountComponentAtNode } from 'react-dom';
import render from './modules/render';
import unmountComponentAtNode from './modules/unmountComponentAtNode';
// APIs
import Animated from './apis/Animated';
@@ -27,15 +28,19 @@ import Vibration from './apis/Vibration';
// components
import ActivityIndicator from './components/ActivityIndicator';
import ART from './components/ART';
import Button from './components/Button';
import CheckBox from './components/CheckBox';
import FlatList from './components/FlatList';
import Image from './components/Image';
import ImageBackground from './components/Image/ImageBackground';
import KeyboardAvoidingView from './components/KeyboardAvoidingView';
import ListView from './components/ListView';
import Modal from './components/Modal';
import Picker from './components/Picker';
import ProgressBar from './components/ProgressBar';
import RefreshControl from './components/RefreshControl';
import SafeAreaView from './components/SafeAreaView';
import ScrollView from './components/ScrollView';
import SectionList from './components/SectionList';
import Slider from './components/Slider';
@@ -61,7 +66,6 @@ import ViewPropTypes from './components/View/ViewPropTypes';
export {
// top-level API
findNodeHandle,
hydrate,
render,
unmountComponentAtNode,
// modules
@@ -90,15 +94,19 @@ export {
Vibration,
// components
ActivityIndicator,
ART,
Button,
CheckBox,
FlatList,
Image,
ImageBackground,
KeyboardAvoidingView,
ListView,
Modal,
Picker,
ProgressBar,
RefreshControl,
SafeAreaView,
ScrollView,
SectionList,
Slider,
@@ -124,7 +132,6 @@ export {
const ReactNative = {
// top-level API
findNodeHandle,
hydrate,
render,
unmountComponentAtNode,
@@ -156,15 +163,19 @@ const ReactNative = {
// components
ActivityIndicator,
ART,
Button,
CheckBox,
FlatList,
Image,
ImageBackground,
KeyboardAvoidingView,
ListView,
Modal,
Picker,
ProgressBar,
RefreshControl,
SafeAreaView,
ScrollView,
SectionList,
Slider,

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeEventEmitter
* @noflow
*/
'use strict';
class NativeEventEmitter {
addListener() {}
emit() {}
listeners() {}
once() {}
removeAllListeners() {}
removeCurrentListener() {}
removeListener() {}
removeSubscription() {}
}
module.exports = NativeEventEmitter;

View File

@@ -1,3 +1,3 @@
// NativeModules shim
const NativeModules = {};
export default NativeModules;
module.exports = NativeModules;

View File

@@ -354,7 +354,7 @@ const ScrollResponderMixin = {
},
/**
* A helper function to scroll to a specific point in the scrollview.
* A helper function to scroll to a specific point in the scrollview.
* This is currently used to help focus on child textviews, but can also
* be used to quickly scroll to any element we want to focus. Syntax:
*
@@ -377,6 +377,7 @@ const ScrollResponderMixin = {
({ x, y, animated } = x || emptyObject);
}
const node = this.scrollResponderGetScrollableNode();
UIManager.updateView(node, { style: { scrollBehavior: !animated ? 'auto' : 'smooth' } }, this);
node.scrollLeft = x || 0;
node.scrollTop = y || 0;
},

View File

@@ -5,7 +5,7 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @noflow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
@@ -38,7 +38,7 @@ const safeOverride = (original, next) => {
return next;
};
const applyLayout = (Component: ReactClass<any>) => {
const applyLayout = Component => {
const componentDidMount = Component.prototype.componentDidMount;
const componentDidUpdate = Component.prototype.componentDidUpdate;
const componentWillUnmount = Component.prototype.componentWillUnmount;

View File

@@ -5,12 +5,12 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @noflow
*/
import NativeMethodsMixin from '../NativeMethodsMixin';
const applyNativeMethods = (Component: ReactClass<any>) => {
const applyNativeMethods = Component => {
Object.keys(NativeMethodsMixin).forEach(method => {
if (!Component.prototype[method]) {
Component.prototype[method] = NativeMethodsMixin[method];

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import { hydrate } from 'react-dom';
export default hydrate;

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import { render } from 'react-dom';
export default render;

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import { unmountComponentAtNode } from 'react-dom';
export default unmountComponentAtNode;

View File

@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ColorPropType
* @flow
* @noflow
*/
const isWebColor = (color: string) =>

196
src/vendor/Animated/AnimatedEvent.js vendored Normal file
View File

@@ -0,0 +1,196 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule AnimatedEvent
* @noflow
* @format
*/
'use strict';
const AnimatedValue = require('./nodes/AnimatedValue');
const NativeAnimatedHelper = require('./NativeAnimatedHelper');
const findNodeHandle = require('../../modules/findNodeHandle').default;
const invariant = require('fbjs/lib/invariant');
const {shouldUseNativeDriver} = require('./NativeAnimatedHelper');
export type Mapping = {[key: string]: Mapping} | AnimatedValue;
export type EventConfig = {
listener?: ?Function,
useNativeDriver?: boolean,
};
function attachNativeEvent(
viewRef: any,
eventName: string,
argMapping: Array<?Mapping>,
) {
// Find animated values in `argMapping` and create an array representing their
// key path inside the `nativeEvent` object. Ex.: ['contentOffset', 'x'].
const eventMappings = [];
const traverse = (value, path) => {
if (value instanceof AnimatedValue) {
value.__makeNative();
eventMappings.push({
nativeEventPath: path,
animatedValueTag: value.__getNativeTag(),
});
} else if (typeof value === 'object') {
for (const key in value) {
traverse(value[key], path.concat(key));
}
}
};
invariant(
argMapping[0] && argMapping[0].nativeEvent,
'Native driven events only support animated values contained inside `nativeEvent`.',
);
// Assume that the event containing `nativeEvent` is always the first argument.
traverse(argMapping[0].nativeEvent, []);
const viewTag = findNodeHandle(viewRef);
eventMappings.forEach(mapping => {
NativeAnimatedHelper.API.addAnimatedEventToView(
viewTag,
eventName,
mapping,
);
});
return {
detach() {
eventMappings.forEach(mapping => {
NativeAnimatedHelper.API.removeAnimatedEventFromView(
viewTag,
eventName,
mapping.animatedValueTag,
);
});
},
};
}
class AnimatedEvent {
_argMapping: Array<?Mapping>;
_listeners: Array<Function> = [];
_callListeners: Function;
_attachedEvent: ?{
detach: () => void,
};
__isNative: boolean;
constructor(argMapping: Array<?Mapping>, config?: EventConfig = {}) {
this._argMapping = argMapping;
if (config.listener) {
this.__addListener(config.listener);
}
this._callListeners = this._callListeners.bind(this);
this._attachedEvent = null;
this.__isNative = shouldUseNativeDriver(config);
if (process.env.NODE_ENV !== 'production') {
this._validateMapping();
}
}
__addListener(callback: Function): void {
this._listeners.push(callback);
}
__removeListener(callback: Function): void {
this._listeners = this._listeners.filter(listener => listener !== callback);
}
__attach(viewRef: any, eventName: string) {
invariant(
this.__isNative,
'Only native driven events need to be attached.',
);
this._attachedEvent = attachNativeEvent(
viewRef,
eventName,
this._argMapping,
);
}
__detach(viewTag: any, eventName: string) {
invariant(
this.__isNative,
'Only native driven events need to be detached.',
);
this._attachedEvent && this._attachedEvent.detach();
}
__getHandler() {
if (this.__isNative) {
return this._callListeners;
}
return (...args: any) => {
const traverse = (recMapping, recEvt, key) => {
if (typeof recEvt === 'number' && recMapping instanceof AnimatedValue) {
recMapping.setValue(recEvt);
} else if (typeof recMapping === 'object') {
for (const mappingKey in recMapping) {
/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error when upgrading Flow's support for
* React. To see the error delete this comment and run Flow. */
traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey);
}
}
};
if (!this.__isNative) {
this._argMapping.forEach((mapping, idx) => {
traverse(mapping, args[idx], 'arg' + idx);
});
}
this._callListeners(...args);
};
}
_callListeners(...args) {
this._listeners.forEach(listener => listener(...args));
}
_validateMapping() {
const traverse = (recMapping, recEvt, key) => {
if (typeof recEvt === 'number') {
invariant(
recMapping instanceof AnimatedValue,
'Bad mapping of type ' +
typeof recMapping +
' for key ' +
key +
', event value must map to AnimatedValue',
);
return;
}
invariant(
typeof recMapping === 'object',
'Bad mapping of type ' + typeof recMapping + ' for key ' + key,
);
invariant(
typeof recEvt === 'object',
'Bad event of type ' + typeof recEvt + ' for key ' + key,
);
for (const mappingKey in recMapping) {
traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey);
}
};
}
}
module.exports = {AnimatedEvent, attachNativeEvent};

View File

@@ -0,0 +1,676 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule AnimatedImplementation
* @flow
* @format
* @preventMunge
*/
'use strict';
const {AnimatedEvent, attachNativeEvent} = require('./AnimatedEvent');
const AnimatedAddition = require('./nodes/AnimatedAddition');
const AnimatedDiffClamp = require('./nodes/AnimatedDiffClamp');
const AnimatedDivision = require('./nodes/AnimatedDivision');
const AnimatedInterpolation = require('./nodes/AnimatedInterpolation');
const AnimatedModulo = require('./nodes/AnimatedModulo');
const AnimatedMultiplication = require('./nodes/AnimatedMultiplication');
const AnimatedNode = require('./nodes/AnimatedNode');
const AnimatedProps = require('./nodes/AnimatedProps');
const AnimatedTracking = require('./nodes/AnimatedTracking');
const AnimatedValue = require('./nodes/AnimatedValue');
const AnimatedValueXY = require('./nodes/AnimatedValueXY');
const DecayAnimation = require('./animations/DecayAnimation');
const SpringAnimation = require('./animations/SpringAnimation');
const TimingAnimation = require('./animations/TimingAnimation');
const createAnimatedComponent = require('./createAnimatedComponent');
import type {
AnimationConfig,
EndCallback,
EndResult,
} from './animations/Animation';
import type {TimingAnimationConfig} from './animations/TimingAnimation';
import type {DecayAnimationConfig} from './animations/DecayAnimation';
import type {SpringAnimationConfig} from './animations/SpringAnimation';
import type {Mapping, EventConfig} from './AnimatedEvent';
type CompositeAnimation = {
start: (callback?: ?EndCallback) => void,
stop: () => void,
reset: () => void,
_startNativeLoop: (iterations?: number) => void,
_isUsingNativeDriver: () => boolean,
};
const add = function(
a: AnimatedNode | number,
b: AnimatedNode | number,
): AnimatedAddition {
return new AnimatedAddition(a, b);
};
const divide = function(
a: AnimatedNode | number,
b: AnimatedNode | number,
): AnimatedDivision {
return new AnimatedDivision(a, b);
};
const multiply = function(
a: AnimatedNode | number,
b: AnimatedNode | number,
): AnimatedMultiplication {
return new AnimatedMultiplication(a, b);
};
const modulo = function(a: AnimatedNode, modulus: number): AnimatedModulo {
return new AnimatedModulo(a, modulus);
};
const diffClamp = function(
a: AnimatedNode,
min: number,
max: number,
): AnimatedDiffClamp {
return new AnimatedDiffClamp(a, min, max);
};
const _combineCallbacks = function(
callback: ?EndCallback,
config: AnimationConfig,
) {
if (callback && config.onComplete) {
return (...args) => {
config.onComplete && config.onComplete(...args);
callback && callback(...args);
};
} else {
return callback || config.onComplete;
}
};
const maybeVectorAnim = function(
value: AnimatedValue | AnimatedValueXY,
config: Object,
anim: (value: AnimatedValue, config: Object) => CompositeAnimation,
): ?CompositeAnimation {
if (value instanceof AnimatedValueXY) {
const configX = {...config};
const configY = {...config};
for (const key in config) {
const {x, y} = config[key];
if (x !== undefined && y !== undefined) {
configX[key] = x;
configY[key] = y;
}
}
const aX = anim((value: AnimatedValueXY).x, configX);
const aY = anim((value: AnimatedValueXY).y, configY);
// We use `stopTogether: false` here because otherwise tracking will break
// because the second animation will get stopped before it can update.
return parallel([aX, aY], {stopTogether: false});
}
return null;
};
const spring = function(
value: AnimatedValue | AnimatedValueXY,
config: SpringAnimationConfig,
): CompositeAnimation {
const start = function(
animatedValue: AnimatedValue | AnimatedValueXY,
configuration: SpringAnimationConfig,
callback?: ?EndCallback,
): void {
callback = _combineCallbacks(callback, configuration);
const singleValue: any = animatedValue;
const singleConfig: any = configuration;
singleValue.stopTracking();
if (configuration.toValue instanceof AnimatedNode) {
singleValue.track(
new AnimatedTracking(
singleValue,
configuration.toValue,
SpringAnimation,
singleConfig,
callback,
),
);
} else {
singleValue.animate(new SpringAnimation(singleConfig), callback);
}
};
return (
maybeVectorAnim(value, config, spring) || {
start: function(callback?: ?EndCallback): void {
start(value, config, callback);
},
stop: function(): void {
value.stopAnimation();
},
reset: function(): void {
value.resetAnimation();
},
_startNativeLoop: function(iterations?: number): void {
const singleConfig = {...config, iterations};
start(value, singleConfig);
},
_isUsingNativeDriver: function(): boolean {
return config.useNativeDriver || false;
},
}
);
};
const timing = function(
value: AnimatedValue | AnimatedValueXY,
config: TimingAnimationConfig,
): CompositeAnimation {
const start = function(
animatedValue: AnimatedValue | AnimatedValueXY,
configuration: TimingAnimationConfig,
callback?: ?EndCallback,
): void {
callback = _combineCallbacks(callback, configuration);
const singleValue: any = animatedValue;
const singleConfig: any = configuration;
singleValue.stopTracking();
if (configuration.toValue instanceof AnimatedNode) {
singleValue.track(
new AnimatedTracking(
singleValue,
configuration.toValue,
TimingAnimation,
singleConfig,
callback,
),
);
} else {
singleValue.animate(new TimingAnimation(singleConfig), callback);
}
};
return (
maybeVectorAnim(value, config, timing) || {
start: function(callback?: ?EndCallback): void {
start(value, config, callback);
},
stop: function(): void {
value.stopAnimation();
},
reset: function(): void {
value.resetAnimation();
},
_startNativeLoop: function(iterations?: number): void {
const singleConfig = {...config, iterations};
start(value, singleConfig);
},
_isUsingNativeDriver: function(): boolean {
return config.useNativeDriver || false;
},
}
);
};
const decay = function(
value: AnimatedValue | AnimatedValueXY,
config: DecayAnimationConfig,
): CompositeAnimation {
const start = function(
animatedValue: AnimatedValue | AnimatedValueXY,
configuration: DecayAnimationConfig,
callback?: ?EndCallback,
): void {
callback = _combineCallbacks(callback, configuration);
const singleValue: any = animatedValue;
const singleConfig: any = configuration;
singleValue.stopTracking();
singleValue.animate(new DecayAnimation(singleConfig), callback);
};
return (
maybeVectorAnim(value, config, decay) || {
start: function(callback?: ?EndCallback): void {
start(value, config, callback);
},
stop: function(): void {
value.stopAnimation();
},
reset: function(): void {
value.resetAnimation();
},
_startNativeLoop: function(iterations?: number): void {
const singleConfig = {...config, iterations};
start(value, singleConfig);
},
_isUsingNativeDriver: function(): boolean {
return config.useNativeDriver || false;
},
}
);
};
const sequence = function(
animations: Array<CompositeAnimation>,
): CompositeAnimation {
let current = 0;
return {
start: function(callback?: ?EndCallback) {
const onComplete = function(result) {
if (!result.finished) {
callback && callback(result);
return;
}
current++;
if (current === animations.length) {
callback && callback(result);
return;
}
animations[current].start(onComplete);
};
if (animations.length === 0) {
callback && callback({finished: true});
} else {
animations[current].start(onComplete);
}
},
stop: function() {
if (current < animations.length) {
animations[current].stop();
}
},
reset: function() {
animations.forEach((animation, idx) => {
if (idx <= current) {
animation.reset();
}
});
current = 0;
},
_startNativeLoop: function() {
throw new Error(
'Loops run using the native driver cannot contain Animated.sequence animations',
);
},
_isUsingNativeDriver: function(): boolean {
return false;
},
};
};
type ParallelConfig = {
stopTogether?: boolean, // If one is stopped, stop all. default: true
};
const parallel = function(
animations: Array<CompositeAnimation>,
config?: ?ParallelConfig,
): CompositeAnimation {
let doneCount = 0;
// Make sure we only call stop() at most once for each animation
const hasEnded = {};
const stopTogether = !(config && config.stopTogether === false);
const result = {
start: function(callback?: ?EndCallback) {
if (doneCount === animations.length) {
callback && callback({finished: true});
return;
}
animations.forEach((animation, idx) => {
const cb = function(endResult) {
hasEnded[idx] = true;
doneCount++;
if (doneCount === animations.length) {
doneCount = 0;
callback && callback(endResult);
return;
}
if (!endResult.finished && stopTogether) {
result.stop();
}
};
if (!animation) {
cb({finished: true});
} else {
animation.start(cb);
}
});
},
stop: function(): void {
animations.forEach((animation, idx) => {
!hasEnded[idx] && animation.stop();
hasEnded[idx] = true;
});
},
reset: function(): void {
animations.forEach((animation, idx) => {
animation.reset();
hasEnded[idx] = false;
doneCount = 0;
});
},
_startNativeLoop: function() {
throw new Error(
'Loops run using the native driver cannot contain Animated.parallel animations',
);
},
_isUsingNativeDriver: function(): boolean {
return false;
},
};
return result;
};
const delay = function(time: number): CompositeAnimation {
// Would be nice to make a specialized implementation
return timing(new AnimatedValue(0), {toValue: 0, delay: time, duration: 0});
};
const stagger = function(
time: number,
animations: Array<CompositeAnimation>,
): CompositeAnimation {
return parallel(
animations.map((animation, i) => {
return sequence([delay(time * i), animation]);
}),
);
};
type LoopAnimationConfig = {iterations: number};
const loop = function(
animation: CompositeAnimation,
{iterations = -1}: LoopAnimationConfig = {},
): CompositeAnimation {
let isFinished = false;
let iterationsSoFar = 0;
return {
start: function(callback?: ?EndCallback) {
const restart = function(result: EndResult = {finished: true}): void {
if (
isFinished ||
iterationsSoFar === iterations ||
result.finished === false
) {
callback && callback(result);
} else {
iterationsSoFar++;
animation.reset();
animation.start(restart);
}
};
if (!animation || iterations === 0) {
callback && callback({finished: true});
} else {
if (animation._isUsingNativeDriver()) {
animation._startNativeLoop(iterations);
} else {
restart(); // Start looping recursively on the js thread
}
}
},
stop: function(): void {
isFinished = true;
animation.stop();
},
reset: function(): void {
iterationsSoFar = 0;
isFinished = false;
animation.reset();
},
_startNativeLoop: function() {
throw new Error(
'Loops run using the native driver cannot contain Animated.loop animations',
);
},
_isUsingNativeDriver: function(): boolean {
return animation._isUsingNativeDriver();
},
};
};
function forkEvent(
event: ?AnimatedEvent | ?Function,
listener: Function,
): AnimatedEvent | Function {
if (!event) {
return listener;
} else if (event instanceof AnimatedEvent) {
event.__addListener(listener);
return event;
} else {
return (...args) => {
typeof event === 'function' && event(...args);
listener(...args);
};
}
}
function unforkEvent(
event: ?AnimatedEvent | ?Function,
listener: Function,
): void {
if (event && event instanceof AnimatedEvent) {
event.__removeListener(listener);
}
}
const event = function(argMapping: Array<?Mapping>, config?: EventConfig): any {
const animatedEvent = new AnimatedEvent(argMapping, config);
if (animatedEvent.__isNative) {
return animatedEvent;
} else {
return animatedEvent.__getHandler();
}
};
/**
* The `Animated` library is designed to make animations fluid, powerful, and
* easy to build and maintain. `Animated` focuses on declarative relationships
* between inputs and outputs, with configurable transforms in between, and
* simple `start`/`stop` methods to control time-based animation execution.
*
* See http://facebook.github.io/react-native/docs/animated.html
*/
module.exports = {
/**
* Standard value class for driving animations. Typically initialized with
* `new Animated.Value(0);`
*
* See http://facebook.github.io/react-native/docs/animated.html#value
*/
Value: AnimatedValue,
/**
* 2D value class for driving 2D animations, such as pan gestures.
*
* See https://facebook.github.io/react-native/releases/next/docs/animatedvaluexy.html
*/
ValueXY: AnimatedValueXY,
/**
* Exported to use the Interpolation type in flow.
*
* See http://facebook.github.io/react-native/docs/animated.html#interpolation
*/
Interpolation: AnimatedInterpolation,
/**
* Exported for ease of type checking. All animated values derive from this
* class.
*
* See http://facebook.github.io/react-native/docs/animated.html#node
*/
Node: AnimatedNode,
/**
* Animates a value from an initial velocity to zero based on a decay
* coefficient.
*
* See http://facebook.github.io/react-native/docs/animated.html#decay
*/
decay,
/**
* Animates a value along a timed easing curve. The Easing module has tons of
* predefined curves, or you can use your own function.
*
* See http://facebook.github.io/react-native/docs/animated.html#timing
*/
timing,
/**
* Animates a value according to an analytical spring model based on
* damped harmonic oscillation.
*
* See http://facebook.github.io/react-native/docs/animated.html#spring
*/
spring,
/**
* Creates a new Animated value composed from two Animated values added
* together.
*
* See http://facebook.github.io/react-native/docs/animated.html#add
*/
add,
/**
* Creates a new Animated value composed by dividing the first Animated value
* by the second Animated value.
*
* See http://facebook.github.io/react-native/docs/animated.html#divide
*/
divide,
/**
* Creates a new Animated value composed from two Animated values multiplied
* together.
*
* See http://facebook.github.io/react-native/docs/animated.html#multiply
*/
multiply,
/**
* Creates a new Animated value that is the (non-negative) modulo of the
* provided Animated value.
*
* See http://facebook.github.io/react-native/docs/animated.html#modulo
*/
modulo,
/**
* Create a new Animated value that is limited between 2 values. It uses the
* difference between the last value so even if the value is far from the
* bounds it will start changing when the value starts getting closer again.
*
* See http://facebook.github.io/react-native/docs/animated.html#diffclamp
*/
diffClamp,
/**
* Starts an animation after the given delay.
*
* See http://facebook.github.io/react-native/docs/animated.html#delay
*/
delay,
/**
* Starts an array of animations in order, waiting for each to complete
* before starting the next. If the current running animation is stopped, no
* following animations will be started.
*
* See http://facebook.github.io/react-native/docs/animated.html#sequence
*/
sequence,
/**
* Starts an array of animations all at the same time. By default, if one
* of the animations is stopped, they will all be stopped. You can override
* this with the `stopTogether` flag.
*
* See http://facebook.github.io/react-native/docs/animated.html#parallel
*/
parallel,
/**
* Array of animations may run in parallel (overlap), but are started in
* sequence with successive delays. Nice for doing trailing effects.
*
* See http://facebook.github.io/react-native/docs/animated.html#stagger
*/
stagger,
/**
* Loops a given animation continuously, so that each time it reaches the
* end, it resets and begins again from the start.
*
* See http://facebook.github.io/react-native/docs/animated.html#loop
*/
loop,
/**
* Takes an array of mappings and extracts values from each arg accordingly,
* then calls `setValue` on the mapped outputs.
*
* See http://facebook.github.io/react-native/docs/animated.html#event
*/
event,
/**
* Make any React component Animatable. Used to create `Animated.View`, etc.
*
* See http://facebook.github.io/react-native/docs/animated.html#createanimatedcomponent
*/
createAnimatedComponent,
/**
* Imperative API to attach an animated value to an event on a view. Prefer
* using `Animated.event` with `useNativeDrive: true` if possible.
*
* See http://facebook.github.io/react-native/docs/animated.html#attachnativeevent
*/
attachNativeEvent,
/**
* Advanced imperative API for snooping on animated events that are passed in
* through props. Use values directly where possible.
*
* See http://facebook.github.io/react-native/docs/animated.html#forkevent
*/
forkEvent,
unforkEvent,
__PropsOnlyForTests: AnimatedProps,
};

303
src/vendor/Animated/Easing.js vendored Normal file
View File

@@ -0,0 +1,303 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule Easing
* @flow
*/
'use strict';
let ease;
/**
* The `Easing` module implements common easing functions. This module is used
* by [Animate.timing()](docs/animate.html#timing) to convey physically
* believable motion in animations.
*
* You can find a visualization of some common easing functions at
* http://easings.net/
*
* ### Predefined animations
*
* The `Easing` module provides several predefined animations through the
* following methods:
*
* - [`back`](docs/easing.html#back) provides a simple animation where the
* object goes slightly back before moving forward
* - [`bounce`](docs/easing.html#bounce) provides a bouncing animation
* - [`ease`](docs/easing.html#ease) provides a simple inertial animation
* - [`elastic`](docs/easing.html#elastic) provides a simple spring interaction
*
* ### Standard functions
*
* Three standard easing functions are provided:
*
* - [`linear`](docs/easing.html#linear)
* - [`quad`](docs/easing.html#quad)
* - [`cubic`](docs/easing.html#cubic)
*
* The [`poly`](docs/easing.html#poly) function can be used to implement
* quartic, quintic, and other higher power functions.
*
* ### Additional functions
*
* Additional mathematical functions are provided by the following methods:
*
* - [`bezier`](docs/easing.html#bezier) provides a cubic bezier curve
* - [`circle`](docs/easing.html#circle) provides a circular function
* - [`sin`](docs/easing.html#sin) provides a sinusoidal function
* - [`exp`](docs/easing.html#exp) provides an exponential function
*
* The following helpers are used to modify other easing functions.
*
* - [`in`](docs/easing.html#in) runs an easing function forwards
* - [`inOut`](docs/easing.html#inout) makes any easing function symmetrical
* - [`out`](docs/easing.html#out) runs an easing function backwards
*/
class Easing {
/**
* A stepping function, returns 1 for any positive value of `n`.
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static step0(n) {
return n > 0 ? 1 : 0;
}
/**
* A stepping function, returns 1 if `n` is greater than or equal to 1.
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static step1(n) {
return n >= 1 ? 1 : 0;
}
/**
* A linear function, `f(t) = t`. Position correlates to elapsed time one to
* one.
*
* http://cubic-bezier.com/#0,0,1,1
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static linear(t) {
return t;
}
/**
* A simple inertial interaction, similar to an object slowly accelerating to
* speed.
*
* http://cubic-bezier.com/#.42,0,1,1
*/
static ease(t: number): number {
if (!ease) {
ease = Easing.bezier(0.42, 0, 1, 1);
}
return ease(t);
}
/**
* A quadratic function, `f(t) = t * t`. Position equals the square of elapsed
* time.
*
* http://easings.net/#easeInQuad
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static quad(t) {
return t * t;
}
/**
* A cubic function, `f(t) = t * t * t`. Position equals the cube of elapsed
* time.
*
* http://easings.net/#easeInCubic
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static cubic(t) {
return t * t * t;
}
/**
* A power function. Position is equal to the Nth power of elapsed time.
*
* n = 4: http://easings.net/#easeInQuart
* n = 5: http://easings.net/#easeInQuint
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static poly(n) {
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an
* error caught by Flow 0.59 which was not caught before. Most likely, this
* error is because an exported function parameter is missing an
* annotation. Without an annotation, these parameters are uncovered by
* Flow. */
return (t) => Math.pow(t, n);
}
/**
* A sinusoidal function.
*
* http://easings.net/#easeInSine
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static sin(t) {
return 1 - Math.cos(t * Math.PI / 2);
}
/**
* A circular function.
*
* http://easings.net/#easeInCirc
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static circle(t) {
return 1 - Math.sqrt(1 - t * t);
}
/**
* An exponential function.
*
* http://easings.net/#easeInExpo
*/
/* $FlowFixMe(>=0.59.0 site=react_native_fb) This comment suppresses an error
* caught by Flow 0.59 which was not caught before. Most likely, this error
* is because an exported function parameter is missing an annotation.
* Without an annotation, these parameters are uncovered by Flow. */
static exp(t) {
return Math.pow(2, 10 * (t - 1));
}
/**
* A simple elastic interaction, similar to a spring oscillating back and
* forth.
*
* Default bounciness is 1, which overshoots a little bit once. 0 bounciness
* doesn't overshoot at all, and bounciness of N > 1 will overshoot about N
* times.
*
* http://easings.net/#easeInElastic
*/
static elastic(bounciness: number = 1): (t: number) => number {
const p = bounciness * Math.PI;
return (t) => 1 - Math.pow(Math.cos(t * Math.PI / 2), 3) * Math.cos(t * p);
}
/**
* Use with `Animated.parallel()` to create a simple effect where the object
* animates back slightly as the animation starts.
*
* Wolfram Plot:
*
* - http://tiny.cc/back_default (s = 1.70158, default)
*/
static back(s: number): (t: number) => number {
if (s === undefined) {
s = 1.70158;
}
return (t) => t * t * ((s + 1) * t - s);
}
/**
* Provides a simple bouncing effect.
*
* http://easings.net/#easeInBounce
*/
static bounce(t: number): number {
if (t < 1 / 2.75) {
return 7.5625 * t * t;
}
if (t < 2 / 2.75) {
t -= 1.5 / 2.75;
return 7.5625 * t * t + 0.75;
}
if (t < 2.5 / 2.75) {
t -= 2.25 / 2.75;
return 7.5625 * t * t + 0.9375;
}
t -= 2.625 / 2.75;
return 7.5625 * t * t + 0.984375;
}
/**
* Provides a cubic bezier curve, equivalent to CSS Transitions'
* `transition-timing-function`.
*
* A useful tool to visualize cubic bezier curves can be found at
* http://cubic-bezier.com/
*/
static bezier(
x1: number,
y1: number,
x2: number,
y2: number
): (t: number) => number {
const _bezier = require('./bezier');
return _bezier(x1, y1, x2, y2);
}
/**
* Runs an easing function forwards.
*/
static in(
easing: (t: number) => number,
): (t: number) => number {
return easing;
}
/**
* Runs an easing function backwards.
*/
static out(
easing: (t: number) => number,
): (t: number) => number {
return (t) => 1 - easing(1 - t);
}
/**
* Makes any easing function symmetrical. The easing function will run
* forwards for half of the duration, then backwards for the rest of the
* duration.
*/
static inOut(
easing: (t: number) => number,
): (t: number) => number {
return (t) => {
if (t < 0.5) {
return easing(t * 2) / 2;
}
return 1 - easing((1 - t) * 2) / 2;
};
}
}
module.exports = Easing;

View File

@@ -0,0 +1,259 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeAnimatedHelper
* @flow
* @format
*/
'use strict';
const invariant = require('fbjs/lib/invariant');
const NativeModules = require('../../modules/NativeModules');
const NativeEventEmitter = require('../../modules/NativeEventEmitter');
import type {AnimationConfig} from './animations/Animation';
import type {EventConfig} from './AnimatedEvent';
const NativeAnimatedModule = NativeModules.NativeAnimatedModule;
let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
let __nativeAnimationIdCount = 1; /* used for started animations */
type EndResult = {finished: boolean};
type EndCallback = (result: EndResult) => void;
type EventMapping = {
nativeEventPath: Array<string>,
animatedValueTag: ?number,
};
let nativeEventEmitter;
/**
* Simple wrappers around NativeAnimatedModule to provide flow and autocmplete support for
* the native module methods
*/
const API = {
createAnimatedNode: function(tag: ?number, config: Object): void {
assertNativeAnimatedModule();
NativeAnimatedModule.createAnimatedNode(tag, config);
},
startListeningToAnimatedNodeValue: function(tag: ?number) {
assertNativeAnimatedModule();
NativeAnimatedModule.startListeningToAnimatedNodeValue(tag);
},
stopListeningToAnimatedNodeValue: function(tag: ?number) {
assertNativeAnimatedModule();
NativeAnimatedModule.stopListeningToAnimatedNodeValue(tag);
},
connectAnimatedNodes: function(parentTag: ?number, childTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag);
},
disconnectAnimatedNodes: function(
parentTag: ?number,
childTag: ?number,
): void {
assertNativeAnimatedModule();
NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag);
},
startAnimatingNode: function(
animationId: ?number,
nodeTag: ?number,
config: Object,
endCallback: EndCallback,
): void {
assertNativeAnimatedModule();
NativeAnimatedModule.startAnimatingNode(
animationId,
nodeTag,
config,
endCallback,
);
},
stopAnimation: function(animationId: ?number) {
assertNativeAnimatedModule();
NativeAnimatedModule.stopAnimation(animationId);
},
setAnimatedNodeValue: function(nodeTag: ?number, value: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.setAnimatedNodeValue(nodeTag, value);
},
setAnimatedNodeOffset: function(nodeTag: ?number, offset: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.setAnimatedNodeOffset(nodeTag, offset);
},
flattenAnimatedNodeOffset: function(nodeTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.flattenAnimatedNodeOffset(nodeTag);
},
extractAnimatedNodeOffset: function(nodeTag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.extractAnimatedNodeOffset(nodeTag);
},
connectAnimatedNodeToView: function(
nodeTag: ?number,
viewTag: ?number,
): void {
assertNativeAnimatedModule();
NativeAnimatedModule.connectAnimatedNodeToView(nodeTag, viewTag);
},
disconnectAnimatedNodeFromView: function(
nodeTag: ?number,
viewTag: ?number,
): void {
assertNativeAnimatedModule();
NativeAnimatedModule.disconnectAnimatedNodeFromView(nodeTag, viewTag);
},
dropAnimatedNode: function(tag: ?number): void {
assertNativeAnimatedModule();
NativeAnimatedModule.dropAnimatedNode(tag);
},
addAnimatedEventToView: function(
viewTag: ?number,
eventName: string,
eventMapping: EventMapping,
) {
assertNativeAnimatedModule();
NativeAnimatedModule.addAnimatedEventToView(
viewTag,
eventName,
eventMapping,
);
},
removeAnimatedEventFromView(
viewTag: ?number,
eventName: string,
animatedNodeTag: ?number,
) {
assertNativeAnimatedModule();
NativeAnimatedModule.removeAnimatedEventFromView(
viewTag,
eventName,
animatedNodeTag,
);
},
};
/**
* Styles allowed by the native animated implementation.
*
* In general native animated implementation should support any numeric property that doesn't need
* to be updated through the shadow view hierarchy (all non-layout properties).
*/
const STYLES_WHITELIST = {
opacity: true,
transform: true,
/* ios styles */
shadowOpacity: true,
shadowRadius: true,
/* legacy android transform properties */
scaleX: true,
scaleY: true,
translateX: true,
translateY: true,
};
const TRANSFORM_WHITELIST = {
translateX: true,
translateY: true,
scale: true,
scaleX: true,
scaleY: true,
rotate: true,
rotateX: true,
rotateY: true,
perspective: true,
};
function validateTransform(configs: Array<Object>): void {
configs.forEach(config => {
if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) {
throw new Error(
`Property '${config.property}' is not supported by native animated module`,
);
}
});
}
function validateStyles(styles: Object): void {
for (var key in styles) {
if (!STYLES_WHITELIST.hasOwnProperty(key)) {
throw new Error(
`Style property '${key}' is not supported by native animated module`,
);
}
}
}
function validateInterpolation(config: Object): void {
var SUPPORTED_INTERPOLATION_PARAMS = {
inputRange: true,
outputRange: true,
extrapolate: true,
extrapolateRight: true,
extrapolateLeft: true,
};
for (var key in config) {
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
throw new Error(
`Interpolation property '${key}' is not supported by native animated module`,
);
}
}
}
function generateNewNodeTag(): number {
return __nativeAnimatedNodeTagCount++;
}
function generateNewAnimationId(): number {
return __nativeAnimationIdCount++;
}
function assertNativeAnimatedModule(): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
}
let _warnedMissingNativeAnimated = false;
function shouldUseNativeDriver(config: AnimationConfig | EventConfig): boolean {
if (config.useNativeDriver && !NativeAnimatedModule) {
if (!_warnedMissingNativeAnimated) {
console.warn(
'Animated: `useNativeDriver` is not supported because the native ' +
'animated module is missing. Falling back to JS-based animation. To ' +
'resolve this, add `RCTAnimation` module to this app, or remove ' +
'`useNativeDriver`. ' +
'More info: https://github.com/facebook/react-native/issues/11094#issuecomment-263240420',
);
_warnedMissingNativeAnimated = true;
}
return false;
}
return config.useNativeDriver || false;
}
const NativeAnimatedHelper = {
API,
validateStyles,
validateTransform,
validateInterpolation,
generateNewNodeTag,
generateNewAnimationId,
assertNativeAnimatedModule,
shouldUseNativeDriver,
get nativeEventEmitter() {
if (!nativeEventEmitter) {
nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule);
}
return nativeEventEmitter;
},
};
module.exports = NativeAnimatedHelper;

1
src/vendor/Animated/SHA vendored Normal file
View File

@@ -0,0 +1 @@
facebook/react-native@71006f74cdafdae7212c8a10603fb972c6ee338c

102
src/vendor/Animated/SpringConfig.js vendored Normal file
View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule SpringConfig
* @flow
*/
'use strict';
type SpringConfigType = {
stiffness: number,
damping: number,
};
function stiffnessFromOrigamiValue(oValue) {
return (oValue - 30) * 3.62 + 194;
}
function dampingFromOrigamiValue(oValue) {
return (oValue - 8) * 3 + 25;
}
function fromOrigamiTensionAndFriction(
tension: number,
friction: number,
): SpringConfigType {
return {
stiffness: stiffnessFromOrigamiValue(tension),
damping: dampingFromOrigamiValue(friction),
};
}
function fromBouncinessAndSpeed(
bounciness: number,
speed: number,
): SpringConfigType {
function normalize(value, startValue, endValue) {
return (value - startValue) / (endValue - startValue);
}
function projectNormal(n, start, end) {
return start + (n * (end - start));
}
function linearInterpolation(t, start, end) {
return t * end + (1 - t) * start;
}
function quadraticOutInterpolation(t, start, end) {
return linearInterpolation(2 * t - t * t, start, end);
}
function b3Friction1(x) {
return (0.0007 * Math.pow(x, 3)) -
(0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28;
}
function b3Friction2(x) {
return (0.000044 * Math.pow(x, 3)) -
(0.006 * Math.pow(x, 2)) + 0.36 * x + 2;
}
function b3Friction3(x) {
return (0.00000045 * Math.pow(x, 3)) -
(0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84;
}
function b3Nobounce(tension) {
if (tension <= 18) {
return b3Friction1(tension);
} else if (tension > 18 && tension <= 44) {
return b3Friction2(tension);
} else {
return b3Friction3(tension);
}
}
var b = normalize(bounciness / 1.7, 0, 20);
b = projectNormal(b, 0, 0.8);
var s = normalize(speed / 1.7, 0, 20);
var bouncyTension = projectNormal(s, 0.5, 200);
var bouncyFriction = quadraticOutInterpolation(
b,
b3Nobounce(bouncyTension),
0.01
);
return {
stiffness: stiffnessFromOrigamiValue(bouncyTension),
damping: dampingFromOrigamiValue(bouncyFriction),
};
}
module.exports = {
fromOrigamiTensionAndFriction,
fromBouncinessAndSpeed,
};

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule Animation
* @flow
* @format
*/
'use strict';
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
import type AnimatedValue from '../nodes/AnimatedValue';
export type EndResult = {finished: boolean};
export type EndCallback = (result: EndResult) => void;
export type AnimationConfig = {
isInteraction?: boolean,
useNativeDriver?: boolean,
onComplete?: ?EndCallback,
iterations?: number,
};
// Important note: start() and stop() will only be called at most once.
// Once an animation has been stopped or finished its course, it will
// not be reused.
class Animation {
__active: boolean;
__isInteraction: boolean;
__nativeId: number;
__onEnd: ?EndCallback;
__iterations: number;
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {}
stop(): void {
if (this.__nativeId) {
NativeAnimatedHelper.API.stopAnimation(this.__nativeId);
}
}
__getNativeAnimationConfig(): any {
// Subclasses that have corresponding animation implementation done in native
// should override this method
throw new Error('This animation type cannot be offloaded to native');
}
// Helper function for subclasses to make sure onEnd is only called once.
__debouncedOnEnd(result: EndResult): void {
const onEnd = this.__onEnd;
this.__onEnd = null;
onEnd && onEnd(result);
}
__startNativeAnimation(animatedValue: AnimatedValue): void {
animatedValue.__makeNative();
this.__nativeId = NativeAnimatedHelper.generateNewAnimationId();
NativeAnimatedHelper.API.startAnimatingNode(
this.__nativeId,
animatedValue.__getNativeTag(),
this.__getNativeAnimationConfig(),
this.__debouncedOnEnd.bind(this),
);
}
}
module.exports = Animation;

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule DecayAnimation
* @flow
* @format
*/
'use strict';
const Animation = require('./Animation');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {AnimationConfig, EndCallback} from './Animation';
import type AnimatedValue from '../nodes/AnimatedValue';
export type DecayAnimationConfig = AnimationConfig & {
velocity: number | {x: number, y: number},
deceleration?: number,
};
export type DecayAnimationConfigSingle = AnimationConfig & {
velocity: number,
deceleration?: number,
};
class DecayAnimation extends Animation {
_startTime: number;
_lastValue: number;
_fromValue: number;
_deceleration: number;
_velocity: number;
_onUpdate: (value: number) => void;
_animationFrame: any;
_useNativeDriver: boolean;
constructor(config: DecayAnimationConfigSingle) {
super();
this._deceleration =
config.deceleration !== undefined ? config.deceleration : 0.998;
this._velocity = config.velocity;
this._useNativeDriver = shouldUseNativeDriver(config);
this.__isInteraction =
config.isInteraction !== undefined ? config.isInteraction : true;
this.__iterations = config.iterations !== undefined ? config.iterations : 1;
}
__getNativeAnimationConfig() {
return {
type: 'decay',
deceleration: this._deceleration,
velocity: this._velocity,
iterations: this.__iterations,
};
}
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {
this.__active = true;
this._lastValue = fromValue;
this._fromValue = fromValue;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
this._startTime = Date.now();
if (this._useNativeDriver) {
this.__startNativeAnimation(animatedValue);
} else {
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
onUpdate(): void {
const now = Date.now();
const value =
this._fromValue +
this._velocity /
(1 - this._deceleration) *
(1 - Math.exp(-(1 - this._deceleration) * (now - this._startTime)));
this._onUpdate(value);
if (Math.abs(this._lastValue - value) < 0.1) {
this.__debouncedOnEnd({finished: true});
return;
}
this._lastValue = value;
if (this.__active) {
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
stop(): void {
super.stop();
this.__active = false;
global.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
}
}
module.exports = DecayAnimation;

View File

@@ -0,0 +1,342 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule SpringAnimation
* @flow
* @format
*/
'use strict';
const AnimatedValue = require('../nodes/AnimatedValue');
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
const Animation = require('./Animation');
const SpringConfig = require('../SpringConfig');
const invariant = require('fbjs/lib/invariant');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {AnimationConfig, EndCallback} from './Animation';
export type SpringAnimationConfig = AnimationConfig & {
toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY,
overshootClamping?: boolean,
restDisplacementThreshold?: number,
restSpeedThreshold?: number,
velocity?: number | {x: number, y: number},
bounciness?: number,
speed?: number,
tension?: number,
friction?: number,
stiffness?: number,
damping?: number,
mass?: number,
delay?: number,
};
export type SpringAnimationConfigSingle = AnimationConfig & {
toValue: number | AnimatedValue,
overshootClamping?: boolean,
restDisplacementThreshold?: number,
restSpeedThreshold?: number,
velocity?: number,
bounciness?: number,
speed?: number,
tension?: number,
friction?: number,
stiffness?: number,
damping?: number,
mass?: number,
delay?: number,
};
function withDefault<T>(value: ?T, defaultValue: T): T {
if (value === undefined || value === null) {
return defaultValue;
}
return value;
}
class SpringAnimation extends Animation {
_overshootClamping: boolean;
_restDisplacementThreshold: number;
_restSpeedThreshold: number;
_lastVelocity: number;
_startPosition: number;
_lastPosition: number;
_fromValue: number;
_toValue: any;
_stiffness: number;
_damping: number;
_mass: number;
_initialVelocity: number;
_delay: number;
_timeout: any;
_startTime: number;
_lastTime: number;
_frameTime: number;
_onUpdate: (value: number) => void;
_animationFrame: any;
_useNativeDriver: boolean;
constructor(config: SpringAnimationConfigSingle) {
super();
this._overshootClamping = withDefault(config.overshootClamping, false);
this._restDisplacementThreshold = withDefault(
config.restDisplacementThreshold,
0.001,
);
this._restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001);
this._initialVelocity = withDefault(config.velocity, 0);
this._lastVelocity = withDefault(config.velocity, 0);
this._toValue = config.toValue;
this._delay = withDefault(config.delay, 0);
this._useNativeDriver = shouldUseNativeDriver(config);
this.__isInteraction =
config.isInteraction !== undefined ? config.isInteraction : true;
this.__iterations = config.iterations !== undefined ? config.iterations : 1;
if (
config.stiffness !== undefined ||
config.damping !== undefined ||
config.mass !== undefined
) {
invariant(
config.bounciness === undefined &&
config.speed === undefined &&
config.tension === undefined &&
config.friction === undefined,
'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one',
);
this._stiffness = withDefault(config.stiffness, 100);
this._damping = withDefault(config.damping, 10);
this._mass = withDefault(config.mass, 1);
} else if (config.bounciness !== undefined || config.speed !== undefined) {
// Convert the origami bounciness/speed values to stiffness/damping
// We assume mass is 1.
invariant(
config.tension === undefined &&
config.friction === undefined &&
config.stiffness === undefined &&
config.damping === undefined &&
config.mass === undefined,
'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one',
);
const springConfig = SpringConfig.fromBouncinessAndSpeed(
withDefault(config.bounciness, 8),
withDefault(config.speed, 12),
);
this._stiffness = springConfig.stiffness;
this._damping = springConfig.damping;
this._mass = 1;
} else {
// Convert the origami tension/friction values to stiffness/damping
// We assume mass is 1.
const springConfig = SpringConfig.fromOrigamiTensionAndFriction(
withDefault(config.tension, 40),
withDefault(config.friction, 7),
);
this._stiffness = springConfig.stiffness;
this._damping = springConfig.damping;
this._mass = 1;
}
invariant(this._stiffness > 0, 'Stiffness value must be greater than 0');
invariant(this._damping > 0, 'Damping value must be greater than 0');
invariant(this._mass > 0, 'Mass value must be greater than 0');
}
__getNativeAnimationConfig() {
return {
type: 'spring',
overshootClamping: this._overshootClamping,
restDisplacementThreshold: this._restDisplacementThreshold,
restSpeedThreshold: this._restSpeedThreshold,
stiffness: this._stiffness,
damping: this._damping,
mass: this._mass,
initialVelocity: withDefault(this._initialVelocity, this._lastVelocity),
toValue: this._toValue,
iterations: this.__iterations,
};
}
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {
this.__active = true;
this._startPosition = fromValue;
this._lastPosition = this._startPosition;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
this._lastTime = Date.now();
this._frameTime = 0.0;
if (previousAnimation instanceof SpringAnimation) {
const internalState = previousAnimation.getInternalState();
this._lastPosition = internalState.lastPosition;
this._lastVelocity = internalState.lastVelocity;
// Set the initial velocity to the last velocity
this._initialVelocity = this._lastVelocity;
this._lastTime = internalState.lastTime;
}
const start = () => {
if (this._useNativeDriver) {
this.__startNativeAnimation(animatedValue);
} else {
this.onUpdate();
}
};
// If this._delay is more than 0, we start after the timeout.
if (this._delay) {
this._timeout = setTimeout(start, this._delay);
} else {
start();
}
}
getInternalState(): Object {
return {
lastPosition: this._lastPosition,
lastVelocity: this._lastVelocity,
lastTime: this._lastTime,
};
}
/**
* This spring model is based off of a damped harmonic oscillator
* (https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator).
*
* We use the closed form of the second order differential equation:
*
* x'' + (2ζ⍵_0)x' + ⍵^2x = 0
*
* where
* ⍵_0 = √(k / m) (undamped angular frequency of the oscillator),
* ζ = c / 2√mk (damping ratio),
* c = damping constant
* k = stiffness
* m = mass
*
* The derivation of the closed form is described in detail here:
* http://planetmath.org/sites/default/files/texpdf/39745.pdf
*
* This algorithm happens to match the algorithm used by CASpringAnimation,
* a QuartzCore (iOS) API that creates spring animations.
*/
onUpdate(): void {
// If for some reason we lost a lot of frames (e.g. process large payload or
// stopped in the debugger), we only advance by 4 frames worth of
// computation and will continue on the next frame. It's better to have it
// running at faster speed than jumping to the end.
const MAX_STEPS = 64;
let now = Date.now();
if (now > this._lastTime + MAX_STEPS) {
now = this._lastTime + MAX_STEPS;
}
const deltaTime = (now - this._lastTime) / 1000;
this._frameTime += deltaTime;
const c: number = this._damping;
const m: number = this._mass;
const k: number = this._stiffness;
const v0: number = -this._initialVelocity;
const zeta = c / (2 * Math.sqrt(k * m)); // damping ratio
const omega0 = Math.sqrt(k / m); // undamped angular frequency of the oscillator (rad/ms)
const omega1 = omega0 * Math.sqrt(1.0 - zeta * zeta); // exponential decay
const x0 = this._toValue - this._startPosition; // calculate the oscillation from x0 = 1 to x = 0
let position = 0.0;
let velocity = 0.0;
const t = this._frameTime;
if (zeta < 1) {
// Under damped
const envelope = Math.exp(-zeta * omega0 * t);
position =
this._toValue -
envelope *
((v0 + zeta * omega0 * x0) / omega1 * Math.sin(omega1 * t) +
x0 * Math.cos(omega1 * t));
// This looks crazy -- it's actually just the derivative of the
// oscillation function
velocity =
zeta *
omega0 *
envelope *
(Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0) / omega1 +
x0 * Math.cos(omega1 * t)) -
envelope *
(Math.cos(omega1 * t) * (v0 + zeta * omega0 * x0) -
omega1 * x0 * Math.sin(omega1 * t));
} else {
// Critically damped
const envelope = Math.exp(-omega0 * t);
position = this._toValue - envelope * (x0 + (v0 + omega0 * x0) * t);
velocity =
envelope * (v0 * (t * omega0 - 1) + t * x0 * (omega0 * omega0));
}
this._lastTime = now;
this._lastPosition = position;
this._lastVelocity = velocity;
this._onUpdate(position);
if (!this.__active) {
// a listener might have stopped us in _onUpdate
return;
}
// Conditions for stopping the spring animation
let isOvershooting = false;
if (this._overshootClamping && this._stiffness !== 0) {
if (this._startPosition < this._toValue) {
isOvershooting = position > this._toValue;
} else {
isOvershooting = position < this._toValue;
}
}
const isVelocity = Math.abs(velocity) <= this._restSpeedThreshold;
let isDisplacement = true;
if (this._stiffness !== 0) {
isDisplacement =
Math.abs(this._toValue - position) <= this._restDisplacementThreshold;
}
if (isOvershooting || (isVelocity && isDisplacement)) {
if (this._stiffness !== 0) {
// Ensure that we end up with a round value
this._lastPosition = this._toValue;
this._lastVelocity = 0;
this._onUpdate(this._toValue);
}
this.__debouncedOnEnd({finished: true});
return;
}
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
stop(): void {
super.stop();
this.__active = false;
clearTimeout(this._timeout);
global.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
}
}
module.exports = SpringAnimation;

View File

@@ -0,0 +1,155 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule TimingAnimation
* @flow
* @format
*/
'use strict';
const AnimatedValue = require('../nodes/AnimatedValue');
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
const Animation = require('./Animation');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {AnimationConfig, EndCallback} from './Animation';
export type TimingAnimationConfig = AnimationConfig & {
toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY,
easing?: (value: number) => number,
duration?: number,
delay?: number,
};
export type TimingAnimationConfigSingle = AnimationConfig & {
toValue: number | AnimatedValue,
easing?: (value: number) => number,
duration?: number,
delay?: number,
};
let _easeInOut;
function easeInOut() {
if (!_easeInOut) {
const Easing = require('../Easing');
_easeInOut = Easing.inOut(Easing.ease);
}
return _easeInOut;
}
class TimingAnimation extends Animation {
_startTime: number;
_fromValue: number;
_toValue: any;
_duration: number;
_delay: number;
_easing: (value: number) => number;
_onUpdate: (value: number) => void;
_animationFrame: any;
_timeout: any;
_useNativeDriver: boolean;
constructor(config: TimingAnimationConfigSingle) {
super();
this._toValue = config.toValue;
this._easing = config.easing !== undefined ? config.easing : easeInOut();
this._duration = config.duration !== undefined ? config.duration : 500;
this._delay = config.delay !== undefined ? config.delay : 0;
this.__iterations = config.iterations !== undefined ? config.iterations : 1;
this.__isInteraction =
config.isInteraction !== undefined ? config.isInteraction : true;
this._useNativeDriver = shouldUseNativeDriver(config);
}
__getNativeAnimationConfig(): any {
const frameDuration = 1000.0 / 60.0;
const frames = [];
for (let dt = 0.0; dt < this._duration; dt += frameDuration) {
frames.push(this._easing(dt / this._duration));
}
frames.push(this._easing(1));
return {
type: 'frames',
frames,
toValue: this._toValue,
iterations: this.__iterations,
};
}
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {
this.__active = true;
this._fromValue = fromValue;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
const start = () => {
// Animations that sometimes have 0 duration and sometimes do not
// still need to use the native driver when duration is 0 so as to
// not cause intermixed JS and native animations.
if (this._duration === 0 && !this._useNativeDriver) {
this._onUpdate(this._toValue);
this.__debouncedOnEnd({finished: true});
} else {
this._startTime = Date.now();
if (this._useNativeDriver) {
this.__startNativeAnimation(animatedValue);
} else {
this._animationFrame = requestAnimationFrame(
this.onUpdate.bind(this),
);
}
}
};
if (this._delay) {
this._timeout = setTimeout(start, this._delay);
} else {
start();
}
}
onUpdate(): void {
const now = Date.now();
if (now >= this._startTime + this._duration) {
if (this._duration === 0) {
this._onUpdate(this._toValue);
} else {
this._onUpdate(
this._fromValue + this._easing(1) * (this._toValue - this._fromValue),
);
}
this.__debouncedOnEnd({finished: true});
return;
}
this._onUpdate(
this._fromValue +
this._easing((now - this._startTime) / this._duration) *
(this._toValue - this._fromValue),
);
if (this.__active) {
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
stop(): void {
super.stop();
this.__active = false;
clearTimeout(this._timeout);
global.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
}
}
module.exports = TimingAnimation;

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