mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-30 23:23:35 +08:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cc1c983e8 | ||
|
|
37413fd55f | ||
|
|
07ff0ea104 | ||
|
|
1a87657500 | ||
|
|
5e4c8e520a | ||
|
|
236121e32c | ||
|
|
39c76ca50c | ||
|
|
e65f91d849 | ||
|
|
a535c558d8 | ||
|
|
a74be91b7c | ||
|
|
8eaaf28a32 | ||
|
|
16d448dc5b | ||
|
|
ea75cced13 | ||
|
|
cfc56a1354 | ||
|
|
b1cd92a65d | ||
|
|
d87f71ebc1 | ||
|
|
a2cafe56fc | ||
|
|
351c0ac3d4 | ||
|
|
877c0d2818 | ||
|
|
3afc5d5de6 | ||
|
|
edf3b9b7ff | ||
|
|
518a85bf1b | ||
|
|
ba75acb66a | ||
|
|
bc68b0b6f4 | ||
|
|
44ecbc072e | ||
|
|
4cf4905fc2 | ||
|
|
509920be4b | ||
|
|
04e3c23e67 | ||
|
|
32f454de66 | ||
|
|
1273bfc7cf | ||
|
|
dc7f526f6b | ||
|
|
7cda89c5ce | ||
|
|
695eba45af | ||
|
|
92a2cb274a | ||
|
|
b1ca04d11e | ||
|
|
22ab70ea6f | ||
|
|
49f36d8eb1 |
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -13,5 +13,6 @@ https://github.com/necolas/react-native-web/CONTRIBUTING.md
|
||||
|
||||
- [ ] includes documentation
|
||||
- [ ] includes tests
|
||||
- [ ] includes benchmark reports
|
||||
- [ ] includes an interactive example
|
||||
- [ ] includes screenshots/videos
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/dist
|
||||
/dist-examples
|
||||
/dist-performance
|
||||
/node_modules
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Contributing
|
||||
|
||||
We are open to, and grateful for, any contributions made by the community.
|
||||
|
||||
## Reporting Issues and Asking Questions
|
||||
|
||||
Before opening an issue, please search the [issue
|
||||
@@ -31,6 +29,12 @@ Run the examples:
|
||||
npm run examples
|
||||
```
|
||||
|
||||
Run the benchmarks in a browser by opening `./performance/index.html` after running:
|
||||
|
||||
```
|
||||
npm run build:performance
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```
|
||||
|
||||
@@ -27,7 +27,7 @@ online with [React Native for Web: Playground](http://codepen.io/necolas/pen/PZz
|
||||
To install in your app:
|
||||
|
||||
```
|
||||
npm install --save react@15.3 react-native-web
|
||||
npm install --save react@15.4 react-native-web
|
||||
```
|
||||
|
||||
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
|
||||
@@ -70,6 +70,7 @@ Exported modules:
|
||||
* [`AppRegistry`](docs/apis/AppRegistry.md)
|
||||
* [`AppState`](docs/apis/AppState.md)
|
||||
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
|
||||
* [`Clipboard`](docs/apis/Clipboard.md)
|
||||
* [`Dimensions`](docs/apis/Dimensions.md)
|
||||
* [`I18nManager`](docs/apis/I18nManager.md)
|
||||
* [`NativeMethods`](docs/apis/NativeMethods.md)
|
||||
@@ -81,6 +82,7 @@ Exported modules:
|
||||
* [`Vibration`](docs/apis/Vibration.md)
|
||||
|
||||
<span id="#why"></span>
|
||||
|
||||
## Why?
|
||||
|
||||
There are many different teams at Twitter building web applications with React.
|
||||
|
||||
16
docs/apis/Clipboard.md
Normal file
16
docs/apis/Clipboard.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Clipboard
|
||||
|
||||
Clipboard gives you an interface for setting to the clipboard. (Getting
|
||||
clipboard content is not supported on web.)
|
||||
|
||||
## Methods
|
||||
|
||||
static **getString**()
|
||||
|
||||
Returns a `Promise` of an empty string.
|
||||
|
||||
static **setString**(content: string): boolean
|
||||
|
||||
Copies a string to the clipboard. On web, some browsers may not support copying
|
||||
to the clipboard, therefore, this function returns a boolean to indicate if the
|
||||
copy was successful.
|
||||
@@ -15,9 +15,9 @@ Each key of the object passed to `create` must define a style object.
|
||||
|
||||
Flattens an array of styles into a single style object.
|
||||
|
||||
**render**: function
|
||||
**renderToString**: function
|
||||
|
||||
Returns a React `<style>` element for use in server-side rendering.
|
||||
Returns a string of the stylesheet for use in server-side rendering.
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -75,6 +75,23 @@ Example usage:
|
||||
<Image resizeMode={Image.resizeMode.contain} />
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
static **getSize**(uri: string, success: (width, height) => {}, failure: function)
|
||||
|
||||
Retrieve the width and height (in pixels) of an image prior to displaying it.
|
||||
This method can fail if the image cannot be found, or fails to download.
|
||||
|
||||
(In order to retrieve the image dimensions, the image may first need to be
|
||||
loaded or downloaded, after which it will be cached. This means that in
|
||||
principle you could use this method to preload images, however it is not
|
||||
optimized for that purpose, and may in future be implemented in a way that does
|
||||
not fully load/download the image data.)
|
||||
|
||||
static **prefetch**(url: string): Promise
|
||||
|
||||
Prefetches a remote image for later use by downloading it.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
|
||||
@@ -27,14 +27,48 @@ the `url-loader` to the webpack config:
|
||||
module.exports = {
|
||||
// ...
|
||||
module: {
|
||||
loaders: {
|
||||
test: /\.(gif|jpe?g|png|svg)$/,
|
||||
loader: 'url-loader',
|
||||
query: { name: '[name].[hash:16].[ext]' }
|
||||
}
|
||||
loaders: [
|
||||
{
|
||||
test: /\.(gif|jpe?g|png|svg)$/,
|
||||
loader: 'url-loader',
|
||||
query: { name: '[name].[hash:16].[ext]' }
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
Many OSS React Native packages are not compiled to ES5 before being published.
|
||||
This can result in webpack build errors. To avoid this issue you should
|
||||
configure webpack (or your bundler of choice) to run
|
||||
`babel-preset-react-native` over the necessary `node_module`. For example:
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
// transpile to ES5
|
||||
test: /\.jsx?$/,
|
||||
include: [
|
||||
path.resolve(__dirname, 'src'),
|
||||
path.resolve(__dirname, 'node_modules/react-native-something')
|
||||
],
|
||||
loader: 'babel-loader',
|
||||
query: { cacheDirectory: true }
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Please refer to the webpack documentation for more information.
|
||||
|
||||
## Web-specific code
|
||||
|
||||
Minor platform differences can use the `Platform` module.
|
||||
|
||||
@@ -16,8 +16,9 @@ module.exports = {
|
||||
}
|
||||
```
|
||||
|
||||
The `react-native-web` package also includes a `core` module that exports only
|
||||
`ReactNative`, `Image`, `StyleSheet`, `Text`, `TextInput`, and `View`.
|
||||
The `react-native-web` package also includes a `core` module that exports a
|
||||
subset of modules: `ReactNative`, `I18nManager`, `Platform`, `StyleSheet`,
|
||||
`Image`, `Text`, `TextInput`, `Touchable`, and `View`.
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
@@ -64,6 +65,10 @@ AppRegistry.runApplication('App', {
|
||||
})
|
||||
```
|
||||
|
||||
Setting `process.env.__REACT_NATIVE_DEBUG_ENABLED__` to `true` will expose some
|
||||
debugging logs. This can help track down when you're rendering without the
|
||||
performance benefit of cached styles.
|
||||
|
||||
## Server-side rendering
|
||||
|
||||
Rendering using the `AppRegistry`:
|
||||
|
||||
@@ -31,10 +31,9 @@ const styles = StyleSheet.create({
|
||||
})
|
||||
```
|
||||
|
||||
Using `StyleSheet.create` is optional but provides some key advantages: styles
|
||||
are immutable in development, certain declarations are automatically converted
|
||||
to CSS rather than applied as inline styles, and styles are only created once
|
||||
for the application and not on every render.
|
||||
Using `StyleSheet.create` is optional but provides the best performance
|
||||
(`style` is resolved to CSS stylesheets). Avoid creating unregistered style
|
||||
objects.
|
||||
|
||||
The attribute names and values are a subset of CSS. See the `style`
|
||||
documentation of individual components.
|
||||
@@ -56,12 +55,6 @@ A common pattern is to conditionally add style based on a condition:
|
||||
styles.base,
|
||||
this.state.active && styles.active
|
||||
]} />
|
||||
|
||||
// or
|
||||
<View style={{
|
||||
...styles.base,
|
||||
...(this.state.active && styles.active)
|
||||
}} />
|
||||
```
|
||||
|
||||
## Composing styles
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const DEV = process.env.NODE_ENV !== 'production';
|
||||
|
||||
module.exports = {
|
||||
module: {
|
||||
loaders: [
|
||||
@@ -19,13 +21,9 @@ module.exports = {
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
|
||||
'process.env.__REACT_NATIVE_DEBUG_ENABLED__': DEV
|
||||
}),
|
||||
// https://github.com/animatedjs/animated/issues/40
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/es6-set/,
|
||||
path.join(__dirname, '../../src/modules/polyfills/Set.js')
|
||||
),
|
||||
new webpack.optimize.OccurenceOrderPlugin()
|
||||
],
|
||||
resolve: {
|
||||
|
||||
33
examples/apis/Clipboard/ClipboardExample.js
Normal file
33
examples/apis/Clipboard/ClipboardExample.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Clipboard, Text, TextInput, View } from 'react-native'
|
||||
import React, { Component } from 'react';
|
||||
import { action, storiesOf } from '@kadira/storybook';
|
||||
|
||||
class ClipboardExample extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={{ minWidth: 300 }}>
|
||||
<Text onPress={this._handleSet}>Copy to clipboard</Text>
|
||||
<TextInput
|
||||
multiline={true}
|
||||
placeholder={'Try pasting here afterwards'}
|
||||
style={{ borderWidth: 1, height: 200, marginVertical: 20 }}
|
||||
/>
|
||||
<Text onPress={this._handleGet}>(Clipboard.getString returns a Promise that always resolves to an empty string on web)</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
_handleGet() {
|
||||
Clipboard.getString().then((value) => { console.log(`Clipboard value: ${value}`) });
|
||||
}
|
||||
|
||||
_handleSet() {
|
||||
const success = Clipboard.setString('This text was copied to the clipboard by React Native');
|
||||
console.log(`Clipboard.setString success? ${success}`);
|
||||
}
|
||||
}
|
||||
|
||||
storiesOf('api: Clipboard', module)
|
||||
.add('setString', () => (
|
||||
<ClipboardExample />
|
||||
));
|
||||
@@ -1,8 +1,8 @@
|
||||
import { storiesOf } from '@kadira/storybook';
|
||||
import { I18nManager, StyleSheet, TouchableHighlight, Text, View } from 'react-native'
|
||||
import React, { Component } from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
|
||||
class RTLExample extends Component {
|
||||
class I18nManagerExample extends Component {
|
||||
componentWillUnmount() {
|
||||
I18nManager.setPreferredLanguageRTL(false)
|
||||
}
|
||||
@@ -75,5 +75,5 @@ const styles = StyleSheet.create({
|
||||
|
||||
storiesOf('api: I18nManager', module)
|
||||
.add('RTL layout', () => (
|
||||
<RTLExample />
|
||||
<I18nManagerExample />
|
||||
))
|
||||
@@ -50,7 +50,8 @@ const ToggleAnimatingActivityIndicator = React.createClass({
|
||||
return (
|
||||
<ActivityIndicator
|
||||
animating={this.state.animating}
|
||||
style={[styles.centering, {height: 80}]}
|
||||
style={styles.centering}
|
||||
hidesWhenStopped={this.props.hidesWhenStopped}
|
||||
size="large"
|
||||
/>
|
||||
);
|
||||
@@ -121,7 +122,12 @@ const examples = [
|
||||
{
|
||||
title: 'Start/stop',
|
||||
render() {
|
||||
return <ToggleAnimatingActivityIndicator />;
|
||||
return (
|
||||
<View style={[styles.horizontal, styles.centering]}>
|
||||
<ToggleAnimatingActivityIndicator />
|
||||
<ToggleAnimatingActivityIndicator hidesWhenStopped={false} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -129,7 +135,7 @@ const examples = [
|
||||
render() {
|
||||
return (
|
||||
<View style={[styles.horizontal, styles.centering]}>
|
||||
<ActivityIndicator size="40" />
|
||||
<ActivityIndicator size={40} />
|
||||
<ActivityIndicator
|
||||
style={{ marginLeft: 20, transform: [ {scale: 1.5} ] }}
|
||||
size="large"
|
||||
|
||||
@@ -28,10 +28,9 @@ import { ActivityIndicator, Image, Platform, StyleSheet, Text, View } from 'reac
|
||||
var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg==';
|
||||
|
||||
//var ImageCapInsetsExample = require('./ImageCapInsetsExample');
|
||||
//const IMAGE_PREFETCH_URL = 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1&t=' + Date.now();
|
||||
//var prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL);
|
||||
const IMAGE_PREFETCH_URL = 'http://origami.design/public/images/bird-logo.png?r=1&t=' + Date.now();
|
||||
var prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL);
|
||||
|
||||
/*
|
||||
var NetworkImageCallbackExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
@@ -88,7 +87,6 @@ var NetworkImageCallbackExample = React.createClass({
|
||||
});
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
var NetworkImageExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
@@ -118,7 +116,6 @@ var NetworkImageExample = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
var ImageSizeExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
@@ -133,24 +130,25 @@ var ImageSizeExample = React.createClass({
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Image
|
||||
style={{
|
||||
width: 60,
|
||||
height: 60,
|
||||
backgroundColor: 'transparent',
|
||||
marginRight: 10,
|
||||
}}
|
||||
source={this.props.source} />
|
||||
<View>
|
||||
<Text>
|
||||
Actual dimensions:{'\n'}
|
||||
Width: {this.state.width}, Height: {this.state.height}
|
||||
width: {this.state.width}, height: {this.state.height}
|
||||
</Text>
|
||||
<Image
|
||||
source={this.props.source}
|
||||
style={{
|
||||
backgroundColor: '#eee',
|
||||
height: 227,
|
||||
marginTop: 10,
|
||||
width: 323
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
*/
|
||||
|
||||
/*
|
||||
var MultipleSourcesExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
@@ -239,17 +237,17 @@ const examples = [
|
||||
);
|
||||
},
|
||||
},
|
||||
/*
|
||||
{
|
||||
title: 'Image Loading Events',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageCallbackExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1&t=' + Date.now()}}
|
||||
prefetchedSource={{uri: IMAGE_PREFETCH_URL}}/>
|
||||
<NetworkImageCallbackExample
|
||||
source={{uri: 'http://origami.design/public/images/bird-logo.png?r=1&t=' + Date.now()}}
|
||||
prefetchedSource={{uri: IMAGE_PREFETCH_URL}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
*/
|
||||
{
|
||||
title: 'Error Handler',
|
||||
render: function() {
|
||||
@@ -263,7 +261,7 @@ const examples = [
|
||||
title: 'Image Download Progress',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1'}}/>
|
||||
<NetworkImageExample source={{uri: 'http://origami.design/public/images/bird-logo.png?r=1'}}/>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
@@ -567,14 +565,12 @@ const examples = [
|
||||
platform: 'ios',
|
||||
},
|
||||
*/
|
||||
/*
|
||||
{
|
||||
title: 'Image Size',
|
||||
render: function() {
|
||||
return <ImageSizeExample source={fullImage} />;
|
||||
return <ImageSizeExample source={{ uri: 'https://upload.wikimedia.org/wikipedia/commons/d/d7/Chestnut-mandibled_Toucan.jpg' }} />;
|
||||
},
|
||||
},
|
||||
*/
|
||||
/*
|
||||
{
|
||||
title: 'MultipleSourcesExample',
|
||||
|
||||
@@ -1,4 +1,80 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { ListView } from 'react-native'
|
||||
import { storiesOf } from '@kadira/storybook';
|
||||
import { ListView, StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
const generateData = (length) => Array.from({ length }).map((item, i) => i);
|
||||
const dataSource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
|
||||
|
||||
storiesOf('component: ListView', module)
|
||||
.add('vertical', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ListView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
dataSource={dataSource.cloneWithRows(generateData(100))}
|
||||
initialListSize={100}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
renderRow={(row) => (
|
||||
<View><Text>{row}</Text></View>
|
||||
)}
|
||||
scrollEventThrottle={1000} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
/>
|
||||
</View>
|
||||
))
|
||||
.add('incremental rendering - large pageSize', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ListView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
dataSource={dataSource.cloneWithRows(generateData(5000))}
|
||||
initialListSize={100}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
|
||||
pageSize={50}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
renderRow={(row) => (
|
||||
<View><Text>{row}</Text></View>
|
||||
)}
|
||||
scrollEventThrottle={1000} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
/>
|
||||
</View>
|
||||
))
|
||||
.add('incremental rendering - small pageSize', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ListView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
dataSource={dataSource.cloneWithRows(generateData(5000))}
|
||||
initialListSize={5}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
|
||||
pageSize={1}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
renderRow={(row) => (
|
||||
<View><Text>{row}</Text></View>
|
||||
)}
|
||||
scrollEventThrottle={1000} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
/>
|
||||
</View>
|
||||
));
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
box: {
|
||||
flexGrow: 1,
|
||||
justifyContent: 'center',
|
||||
borderWidth: 1
|
||||
},
|
||||
scrollViewContainer: {
|
||||
height: '200px',
|
||||
width: 300
|
||||
},
|
||||
scrollViewStyle: {
|
||||
borderWidth: '1px'
|
||||
},
|
||||
scrollViewContentContainerStyle: {
|
||||
backgroundColor: '#eee',
|
||||
padding: '10px'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ class TextEventsExample extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
placeholder="Enter text to see events"
|
||||
@@ -83,7 +83,7 @@ class TextEventsExample extends React.Component {
|
||||
onKeyPress={(event) => {
|
||||
this.updateText('onKeyPress key: ' + event.nativeEvent.key);
|
||||
}}
|
||||
style={styles.default}
|
||||
style={[ styles.default, { maxWidth: 200 } ]}
|
||||
/>
|
||||
<Text style={styles.eventLabel}>
|
||||
{this.state.curText}{'\n'}
|
||||
|
||||
@@ -307,11 +307,11 @@ var TouchableDisabled = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity disabled={true} style={[styles.row, styles.block]}>
|
||||
<TouchableOpacity disabled={true} style={[styles.row, styles.block]} onPress={action('TouchableOpacity')}>
|
||||
<Text style={styles.disabledButton}>Disabled TouchableOpacity</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity disabled={false} style={[styles.row, styles.block]}>
|
||||
<TouchableOpacity disabled={false} style={[styles.row, styles.block]} onPress={action('TouchableOpacity')}>
|
||||
<Text style={styles.button}>Enabled TouchableOpacity</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -321,7 +321,7 @@ var TouchableDisabled = React.createClass({
|
||||
animationVelocity={0}
|
||||
underlayColor="rgb(210, 230, 255)"
|
||||
style={[styles.row, styles.block]}
|
||||
onPress={() => console.log('custom THW text - highlight')}>
|
||||
onPress={action('TouchableHighlight')}>
|
||||
<Text style={styles.disabledButton}>
|
||||
Disabled TouchableHighlight
|
||||
</Text>
|
||||
@@ -332,7 +332,7 @@ var TouchableDisabled = React.createClass({
|
||||
animationVelocity={0}
|
||||
underlayColor="rgb(210, 230, 255)"
|
||||
style={[styles.row, styles.block]}
|
||||
onPress={() => console.log('custom THW text - highlight')}>
|
||||
onPress={action('TouchableHighlight')}>
|
||||
<Text style={styles.button}>
|
||||
Enabled TouchableHighlight
|
||||
</Text>
|
||||
|
||||
@@ -31,6 +31,17 @@ var styles = StyleSheet.create({
|
||||
borderColor: '#000033',
|
||||
borderWidth: 1,
|
||||
},
|
||||
shadowBox: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderWidth: 2,
|
||||
},
|
||||
shadow: {
|
||||
shadowOpacity: 0.5,
|
||||
shadowColor: 'red',
|
||||
shadowRadius: 3,
|
||||
shadowOffset: { width: 3, height: 3 },
|
||||
},
|
||||
zIndex: {
|
||||
justifyContent: 'space-around',
|
||||
width: 100,
|
||||
@@ -242,6 +253,19 @@ const examples = [
|
||||
return <ZIndexExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Basic shadow',
|
||||
render() {
|
||||
return <View style={[ styles.shadowBox, styles.shadow ]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Shaped shadow',
|
||||
description: 'borderRadius: 50',
|
||||
render() {
|
||||
return <View style={[ styles.shadowBox, styles.shadow, {borderRadius: 50} ]} />;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
|
||||
@@ -113,7 +113,7 @@ var Cell = React.createClass({
|
||||
case 2:
|
||||
return styles.cellTextO;
|
||||
default:
|
||||
return {};
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
35
package.json
35
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.0.57",
|
||||
"version": "0.0.63",
|
||||
"description": "React Native for Web",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
@@ -11,36 +11,38 @@
|
||||
"scripts": {
|
||||
"build": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
|
||||
"build:examples": "build-storybook -o dist-examples -c ./examples/.storybook",
|
||||
"build:performance": "cd performance && webpack",
|
||||
"build:umd": "webpack --config webpack.config.js --sort-assets-by --progress",
|
||||
"deploy:examples": "git checkout gh-pages && rm -rf ./storybook && mv dist-examples storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
|
||||
"examples": "start-storybook -p 9001 -c ./examples/.storybook --dont-track",
|
||||
"lint": "eslint src",
|
||||
"lint": "eslint performance src",
|
||||
"prepublish": "npm run build && npm run build:umd",
|
||||
"test": "npm run lint && npm run test:jest",
|
||||
"test:jest": "jest",
|
||||
"test:watch": "npm run test:jest -- --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"animated": "^0.1.3",
|
||||
"animated": "^0.1.5",
|
||||
"array-find-index": "^1.0.2",
|
||||
"babel-runtime": "^6.11.6",
|
||||
"asap": "^2.0.5",
|
||||
"babel-runtime": "^6.20.0",
|
||||
"debounce": "^1.0.0",
|
||||
"deep-assign": "^2.0.0",
|
||||
"fbjs": "^0.8.4",
|
||||
"inline-style-prefixer": "^2.0.1",
|
||||
"react-dom": "~15.3.2",
|
||||
"fbjs": "^0.8.8",
|
||||
"inline-style-prefixer": "^2.0.5",
|
||||
"react-dom": "~15.4.1",
|
||||
"react-textarea-autosize": "^4.0.4",
|
||||
"react-timer-mixin": "^0.13.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kadira/storybook": "^2.5.1",
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-eslint": "^6.1.2",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-core": "^6.21.0",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "^6.2.10",
|
||||
"babel-plugin-transform-react-remove-prop-types": "^0.2.11",
|
||||
"babel-preset-react-native": "^1.9.0",
|
||||
"del-cli": "^0.2.0",
|
||||
"babel-preset-react-native": "^1.9.1",
|
||||
"del-cli": "^0.2.1",
|
||||
"enzyme": "^2.4.1",
|
||||
"eslint": "^3.4.0",
|
||||
"eslint-plugin-jsx-a11y": "^2.2.0",
|
||||
@@ -48,16 +50,17 @@
|
||||
"eslint-plugin-react": "^6.1.2",
|
||||
"file-loader": "^0.9.0",
|
||||
"jest": "^16.0.2",
|
||||
"marky": "^1.1.1",
|
||||
"node-libs-browser": "^0.5.3",
|
||||
"react": "~15.3.2",
|
||||
"react-addons-test-utils": "~15.3.2",
|
||||
"react-test-renderer": "~15.3.2",
|
||||
"react": "~15.4.1",
|
||||
"react-addons-test-utils": "~15.4.1",
|
||||
"react-test-renderer": "~15.4.1",
|
||||
"url-loader": "^0.5.7",
|
||||
"webpack": "^1.13.2",
|
||||
"webpack-bundle-analyzer": "^1.5.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "~15.3.2"
|
||||
"react": "~15.4.1"
|
||||
},
|
||||
"author": "Nicolas Gallagher",
|
||||
"license": "BSD-3-Clause",
|
||||
|
||||
65
performance/benchmark.js
Normal file
65
performance/benchmark.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import * as marky from 'marky';
|
||||
|
||||
const fmt = (time) => `${Math.round(time * 100) / 100}ms`;
|
||||
|
||||
const measure = (name, fn) => {
|
||||
marky.mark(name);
|
||||
fn();
|
||||
const performanceMeasure = marky.stop(name);
|
||||
return performanceMeasure;
|
||||
};
|
||||
|
||||
const benchmark = ({ name, description, setup, teardown, task, runs }) => {
|
||||
return new Promise((resolve) => {
|
||||
const performanceMeasures = [];
|
||||
let i = 0;
|
||||
|
||||
setup();
|
||||
const first = measure('first', task);
|
||||
teardown();
|
||||
|
||||
const done = () => {
|
||||
const mean = performanceMeasures.reduce((sum, performanceMeasure) => {
|
||||
return sum + performanceMeasure.duration;
|
||||
}, 0) / runs;
|
||||
|
||||
const firstDuration = fmt(first.duration);
|
||||
const meanDuration = fmt(mean);
|
||||
|
||||
console.log(`First: ${firstDuration}`);
|
||||
console.log(`Mean: ${meanDuration}`);
|
||||
console.groupEnd();
|
||||
resolve(mean);
|
||||
};
|
||||
|
||||
const a = () => {
|
||||
setup();
|
||||
window.requestAnimationFrame(b);
|
||||
};
|
||||
|
||||
const b = () => {
|
||||
performanceMeasures.push(measure('mean', task));
|
||||
window.requestAnimationFrame(c);
|
||||
};
|
||||
|
||||
const c = () => {
|
||||
teardown();
|
||||
window.requestAnimationFrame(d);
|
||||
};
|
||||
|
||||
const d = () => {
|
||||
i += 1;
|
||||
if (i < runs) {
|
||||
window.requestAnimationFrame(a);
|
||||
} else {
|
||||
window.requestAnimationFrame(done);
|
||||
}
|
||||
};
|
||||
|
||||
console.group(`[benchmark] ${name}`);
|
||||
console.log(description);
|
||||
window.requestAnimationFrame(a);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = benchmark;
|
||||
88
performance/benchmarks/deepTree/createDeepTree.js
Normal file
88
performance/benchmarks/deepTree/createDeepTree.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const createDeepTree = ({ StyleSheet, View }, options = {}) => {
|
||||
class DeepTree extends Component {
|
||||
static propTypes = {
|
||||
breadth: PropTypes.number.isRequired,
|
||||
depth: PropTypes.number.isRequired,
|
||||
id: PropTypes.number.isRequired,
|
||||
wrap: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const { breadth, depth, id, wrap } = this.props;
|
||||
|
||||
let result = (
|
||||
<View
|
||||
style={[
|
||||
styles.outer,
|
||||
depth % 2 === 0 ? styles.even : styles.odd,
|
||||
styles[`custom${id % 3}`]
|
||||
]}
|
||||
>
|
||||
{depth === 0 && (
|
||||
<View
|
||||
style={[
|
||||
styles.terminal,
|
||||
styles[`terminal${id % 3}`]
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{depth !== 0 && Array.from({ length: breadth }).map((el, i) => (
|
||||
<DeepTree
|
||||
breadth={breadth}
|
||||
depth={depth - 1}
|
||||
id={i}
|
||||
key={i}
|
||||
wrap={wrap}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
for (let i = 0; i < wrap; i++) {
|
||||
result = <View>{result}</View>;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const stylesObject = {
|
||||
outer: {
|
||||
padding: 4
|
||||
},
|
||||
odd: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
even: {
|
||||
flexDirection: 'column'
|
||||
},
|
||||
custom0: {
|
||||
backgroundColor: '#222'
|
||||
},
|
||||
custom1: {
|
||||
backgroundColor: '#666'
|
||||
},
|
||||
custom2: {
|
||||
backgroundColor: '#999'
|
||||
},
|
||||
terminal: {
|
||||
width: 20,
|
||||
height: 20
|
||||
},
|
||||
terminal0: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
terminal1: {
|
||||
backgroundColor: 'orange'
|
||||
},
|
||||
terminal2: {
|
||||
backgroundColor: 'red'
|
||||
}
|
||||
};
|
||||
|
||||
const styles = options.registerStyles ? StyleSheet.create(stylesObject) : stylesObject;
|
||||
|
||||
return DeepTree;
|
||||
};
|
||||
|
||||
module.exports = createDeepTree;
|
||||
24
performance/benchmarks/deepTree/index.js
Normal file
24
performance/benchmarks/deepTree/index.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import benchmark from '../../benchmark';
|
||||
import createDeepTree from './createDeepTree';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactNative from 'react-native';
|
||||
|
||||
const deepTreeBenchmark = ({ breadth, depth, registerStyles = true, runs, wrap }, node) => () => {
|
||||
// React Native for Web implementation of the tree
|
||||
const DeepTree = createDeepTree(ReactNative, { registerStyles });
|
||||
|
||||
const setup = () => {};
|
||||
const teardown = () => ReactDOM.unmountComponentAtNode(node);
|
||||
|
||||
return benchmark({
|
||||
name: `DeepTree (${registerStyles ? 'registered' : 'unregistered'}) styles)`,
|
||||
description: `depth=${depth}, breadth=${breadth}, wrap=${wrap}`,
|
||||
runs,
|
||||
setup,
|
||||
teardown,
|
||||
task: () => ReactDOM.render(<DeepTree breadth={breadth} depth={depth} id={0} wrap={wrap} />, node)
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = deepTreeBenchmark;
|
||||
11
performance/index.html
Normal file
11
performance/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="root"></div>
|
||||
<script src="../dist-performance/performance.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
16
performance/index.js
Normal file
16
performance/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import createDeepTree from './benchmarks/deepTree/createDeepTree';
|
||||
import deepTree from './benchmarks/deepTree';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactNative from 'react-native';
|
||||
|
||||
const node = document.querySelector('.root');
|
||||
const DeepTree = createDeepTree(ReactNative);
|
||||
|
||||
Promise.resolve()
|
||||
.then(deepTree({ wrap: 4, depth: 3, breadth: 10, runs: 10, registerStyles: false }, node))
|
||||
.then(deepTree({ wrap: 1, depth: 5, breadth: 3, runs: 20, registerStyles: false }, node))
|
||||
.then(deepTree({ wrap: 4, depth: 3, breadth: 10, runs: 10 }, node))
|
||||
.then(deepTree({ wrap: 1, depth: 5, breadth: 3, runs: 20 }, node))
|
||||
.then(() => ReactDOM.render(<DeepTree breadth={3} depth={5} id={0} wrap={1} />, node));
|
||||
|
||||
45
performance/webpack.config.js
Normal file
45
performance/webpack.config.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
performance: './index'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../dist-performance'),
|
||||
filename: 'performance.bundle.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
query: { cacheDirectory: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }),
|
||||
new webpack.optimize.DedupePlugin(),
|
||||
// https://github.com/animatedjs/animated/issues/40
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/es6-set/,
|
||||
path.join(__dirname, '../src/modules/polyfills/Set.js')
|
||||
),
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
dead_code: true,
|
||||
screw_ie8: true,
|
||||
warnings: true
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': path.join(__dirname, '../src')
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
|
||||
"<style id=\"react-native-stylesheet\">
|
||||
/* React Native StyleSheet*/
|
||||
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}
|
||||
body{margin:0}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
|
||||
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none}
|
||||
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform: rotate(0deg); transform: rotate(0deg);}100%{-webkit-transform: rotate(360deg); transform: rotate(360deg);}}
|
||||
@keyframes rn-ProgressBar-animation{0%{-webkit-transform: translateX(-100%); transform: translateX(-100%);}100%{-webkit-transform: translateX(400%); transform: translateX(400%);}}
|
||||
.rn-pointerEvents\\:auto,.rn_pointerEvents\\:box-only,.rn-pointerEvents\\:box-none *{pointer-events:auto}.rn-pointerEvents\\:none,.rn_pointerEvents\\:box-only *,.rn-pointerEvents\\:box-none{pointer-events:none}
|
||||
.rn-bottom\\:0px{bottom:0px}
|
||||
.rn-left\\:0px{left:0px}
|
||||
.rn-position\\:absolute{position:absolute}
|
||||
.rn-right\\:0px{right:0px}
|
||||
.rn-top\\:0px{top:0px}
|
||||
.rn-alignItems\\:stretch{-ms-flex-align:stretch;-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
|
||||
.rn-backgroundColor\\:transparent{background-color:transparent}
|
||||
.rn-borderTopStyle\\:solid{border-top-style:solid}
|
||||
.rn-borderRightStyle\\:solid{border-right-style:solid}
|
||||
.rn-borderBottomStyle\\:solid{border-bottom-style:solid}
|
||||
.rn-borderLeftStyle\\:solid{border-left-style:solid}
|
||||
.rn-borderTopWidth\\:0px{border-top-width:0px}
|
||||
.rn-borderRightWidth\\:0px{border-right-width:0px}
|
||||
.rn-borderBottomWidth\\:0px{border-bottom-width:0px}
|
||||
.rn-borderLeftWidth\\:0px{border-left-width:0px}
|
||||
.rn-boxSizing\\:border-box{-moz-box-sizing:border-box;box-sizing:border-box}
|
||||
.rn-color\\:inherit{color:inherit}
|
||||
.rn-display\\:flex{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
|
||||
.rn-flexBasis\\:auto{-ms-preferred-size:auto;-webkit-flex-basis:auto;flex-basis:auto}
|
||||
.rn-flexDirection\\:column{-ms-flex-direction:column;-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;flex-direction:column}
|
||||
.rn-font\\:inherit{font:inherit}
|
||||
.rn-listStyle\\:none{list-style:none}
|
||||
.rn-marginTop\\:0px{margin-top:0px}
|
||||
.rn-marginRight\\:0px{margin-right:0px}
|
||||
.rn-marginBottom\\:0px{margin-bottom:0px}
|
||||
.rn-marginLeft\\:0px{margin-left:0px}
|
||||
.rn-minHeight\\:0px{min-height:0px}
|
||||
.rn-minWidth\\:0px{min-width:0px}
|
||||
.rn-paddingTop\\:0px{padding-top:0px}
|
||||
.rn-paddingRight\\:0px{padding-right:0px}
|
||||
.rn-paddingBottom\\:0px{padding-bottom:0px}
|
||||
.rn-paddingLeft\\:0px{padding-left:0px}
|
||||
.rn-position\\:relative{position:relative}
|
||||
.rn-textAlign\\:inherit{text-align:inherit}
|
||||
.rn-textDecoration\\:none{text-decoration:none}
|
||||
.rn-flexShrink\\:0{-ms-flex-negative:0px;-webkit-flex-shrink:0px;flex-shrink:0}
|
||||
</style>"
|
||||
`;
|
||||
@@ -10,6 +10,6 @@ describe('apis/AppRegistry/renderApplication', () => {
|
||||
const { element, stylesheet } = getApplication(component, {});
|
||||
|
||||
expect(element).toBeTruthy();
|
||||
expect(stylesheet.type).toEqual('style');
|
||||
expect(stylesheet).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
|
||||
import { Component } from 'react';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import { unmountComponentAtNode } from 'react/lib/ReactMount';
|
||||
import { unmountComponentAtNode } from 'react-dom/lib/ReactMount';
|
||||
import renderApplication, { getApplication } from './renderApplication';
|
||||
|
||||
const emptyObject = {};
|
||||
const runnables = {};
|
||||
|
||||
type ComponentProvider = () => Component<any, any, any>
|
||||
@@ -41,8 +42,8 @@ class AppRegistry {
|
||||
|
||||
static registerComponent(appKey: string, getComponentFunc: ComponentProvider): string {
|
||||
runnables[appKey] = {
|
||||
getApplication: ({ initialProps } = {}) => getApplication(getComponentFunc(), initialProps),
|
||||
run: ({ initialProps, rootTag }) => renderApplication(getComponentFunc(), initialProps, rootTag)
|
||||
getApplication: ({ initialProps } = emptyObject) => getApplication(getComponentFunc(), initialProps),
|
||||
run: ({ initialProps = emptyObject, rootTag }) => renderApplication(getComponentFunc(), initialProps, rootTag)
|
||||
};
|
||||
return appKey;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import { render } from 'react/lib/ReactMount';
|
||||
import { render } from 'react-dom/lib/ReactMount';
|
||||
import ReactNativeApp from './ReactNativeApp';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import React, { Component } from 'react';
|
||||
@@ -32,6 +32,6 @@ export function getApplication(RootComponent: Component, initialProps: Object):
|
||||
rootComponent={RootComponent}
|
||||
/>
|
||||
);
|
||||
const stylesheet = StyleSheet.render();
|
||||
const stylesheet = StyleSheet.renderToString();
|
||||
return { element, stylesheet };
|
||||
}
|
||||
|
||||
28
src/apis/BackAndroid/index.js
Normal file
28
src/apis/BackAndroid/index.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* web stub for BackAndroid.android.js
|
||||
*
|
||||
* @providesModule BackAndroid
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function emptyFunction() {}
|
||||
|
||||
const BackAndroid = {
|
||||
exitApp: emptyFunction,
|
||||
addEventListener() {
|
||||
return {
|
||||
remove: emptyFunction
|
||||
};
|
||||
},
|
||||
removeEventListener: emptyFunction
|
||||
};
|
||||
|
||||
module.exports = BackAndroid;
|
||||
21
src/apis/Clipboard/index.js
Normal file
21
src/apis/Clipboard/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
class Clipboard {
|
||||
static getString() {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
static setString(text) {
|
||||
let success = false;
|
||||
const textField = document.createElement('textarea');
|
||||
textField.innerText = text;
|
||||
document.body.appendChild(textField);
|
||||
textField.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
success = true;
|
||||
} catch (e) {}
|
||||
document.body.removeChild(textField);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Clipboard;
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
var TouchHistoryMath = require('react/lib/TouchHistoryMath');
|
||||
var TouchHistoryMath = require('react-dom/lib/TouchHistoryMath');
|
||||
|
||||
var currentCentroidXOfTouchesChangedAfter =
|
||||
TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
import { PropTypes } from 'react'
|
||||
import ImageStylePropTypes from '../../components/Image/ImageStylePropTypes'
|
||||
import ReactPropTypeLocations from 'react/lib/ReactPropTypeLocations'
|
||||
import ReactPropTypesSecret from 'react/lib/ReactPropTypesSecret'
|
||||
import ReactPropTypeLocations from 'react-dom/lib/ReactPropTypeLocations'
|
||||
import ReactPropTypesSecret from 'react-dom/lib/ReactPropTypesSecret'
|
||||
import TextStylePropTypes from '../../components/Text/TextStylePropTypes'
|
||||
import ViewStylePropTypes from '../../components/View/ViewStylePropTypes'
|
||||
import warning from 'fbjs/lib/warning'
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
exports[`apis/StyleSheet/createReactDOMStyle converts ReactNative style to ReactDOM style 1`] = `
|
||||
Object {
|
||||
"borderBottomWidth": "1px",
|
||||
"borderLeftWidth": "1px",
|
||||
"borderRightWidth": "1px",
|
||||
"borderTopWidth": "1px",
|
||||
"borderWidthLeft": "2px",
|
||||
"borderWidthRight": "3px",
|
||||
"boxShadow": "1px 1px 1px 1px #000, 1px 2px 0px rgba(255,0,0,1)",
|
||||
"display": "flex",
|
||||
"marginBottom": "0px",
|
||||
"marginTop": "0px",
|
||||
"opacity": 0,
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,26 @@
|
||||
exports[`apis/StyleSheet/flattenStyle should merge style objects 1`] = `
|
||||
Object {
|
||||
"opacity": 1,
|
||||
"order": 2,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/flattenStyle should override style properties 1`] = `
|
||||
Object {
|
||||
"backgroundColor": "#023c69",
|
||||
"order": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/flattenStyle should overwrite properties with \`undefined\` 1`] = `
|
||||
Object {
|
||||
"backgroundColor": undefined,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/flattenStyle should recursively flatten arrays 1`] = `
|
||||
Object {
|
||||
"opacity": 1,
|
||||
"order": 3,
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1 @@
|
||||
exports[`apis/StyleSheet/generateCss generates correct css 1`] = `"-webkit-transition-duration:0.1s;border-width-left:2px;border-width-right:3px;box-shadow:1px 1px 1px 1px #000;position:absolute;transition-duration:0.1s"`;
|
||||
@@ -1,10 +1,14 @@
|
||||
exports[`apis/StyleSheet resolve 1`] = `
|
||||
Object {
|
||||
"className": "test __style_df __style_pebn",
|
||||
"style": Object {
|
||||
"display": null,
|
||||
"opacity": 1,
|
||||
"pointerEvents": null,
|
||||
},
|
||||
}
|
||||
exports[`apis/StyleSheet renderToString 1`] = `
|
||||
"<style id=\"react-native-stylesheet\">
|
||||
.rn-borderTopColor\\:red{border-top-color:red}
|
||||
.rn-borderRightColor\\:red{border-right-color:red}
|
||||
.rn-borderBottomColor\\:red{border-bottom-color:red}
|
||||
.rn-borderLeftColor\\:red{border-left-color:red}
|
||||
.rn-borderTopWidth\\:0px{border-top-width:0px}
|
||||
.rn-borderRightWidth\\:0px{border-right-width:0px}
|
||||
.rn-borderBottomWidth\\:0px{border-bottom-width:0px}
|
||||
.rn-borderLeftWidth\\:0px{border-left-width:0px}
|
||||
.rn-left\\:50px{left:50px}
|
||||
.rn-position\\:absolute{position:absolute}
|
||||
</style>"
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
exports[`apis/StyleSheet/registry resolve with register, resolves to className 1`] = `
|
||||
Object {
|
||||
"className": "
|
||||
rn-borderTopColor:red
|
||||
rn-borderRightColor:red
|
||||
rn-borderBottomColor:red
|
||||
rn-borderLeftColor:red
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-left:50px
|
||||
rn-pointerEvents:box-only
|
||||
rn-position:absolute
|
||||
rn-width:100px",
|
||||
"style": Object {},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/registry resolve with register, resolves to className 2`] = `
|
||||
Object {
|
||||
"className": "
|
||||
rn-borderTopColor:red
|
||||
rn-borderRightColor:red
|
||||
rn-borderBottomColor:red
|
||||
rn-borderLeftColor:red
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-left:50px
|
||||
rn-pointerEvents:box-only
|
||||
rn-position:absolute
|
||||
rn-width:200px",
|
||||
"style": Object {},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/registry resolve with register, resolves to className 3`] = `
|
||||
Object {
|
||||
"className": "
|
||||
rn-borderTopColor:red
|
||||
rn-borderRightColor:red
|
||||
rn-borderBottomColor:red
|
||||
rn-borderLeftColor:red
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-left:50px
|
||||
rn-pointerEvents:box-only
|
||||
rn-position:absolute
|
||||
rn-width:100px",
|
||||
"style": Object {},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/registry resolve with register, resolves to mixed 1`] = `
|
||||
Object {
|
||||
"className": "
|
||||
rn-left:50px
|
||||
rn-pointerEvents:box-only
|
||||
rn-position:absolute",
|
||||
"style": Object {
|
||||
"borderBottomColor": "red",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightColor": "red",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopColor": "red",
|
||||
"borderTopWidth": "0px",
|
||||
"width": "100px",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/registry resolve with register, resolves to mixed 2`] = `
|
||||
Object {
|
||||
"className": "
|
||||
rn-left:50px
|
||||
rn-pointerEvents:box-only
|
||||
rn-position:absolute
|
||||
rn-width:200px",
|
||||
"style": Object {
|
||||
"borderBottomColor": "red",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightColor": "red",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopColor": "red",
|
||||
"borderTopWidth": "0px",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/registry resolve with register, resolves to mixed 3`] = `
|
||||
Object {
|
||||
"className": "
|
||||
rn-left:50px
|
||||
rn-pointerEvents:box-only
|
||||
rn-position:absolute",
|
||||
"style": Object {
|
||||
"borderBottomColor": "red",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightColor": "red",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopColor": "red",
|
||||
"borderTopWidth": "0px",
|
||||
"width": "100px",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/registry resolve without register, resolves to inline styles 1`] = `
|
||||
Object {
|
||||
"className": "
|
||||
",
|
||||
"style": Object {
|
||||
"borderBottomColor": "red",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightColor": "red",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopColor": "red",
|
||||
"borderTopWidth": "0px",
|
||||
"left": "50px",
|
||||
"pointerEvents": "box-only",
|
||||
"position": "absolute",
|
||||
"width": "100px",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/registry resolve without register, resolves to inline styles 2`] = `
|
||||
Object {
|
||||
"className": "
|
||||
",
|
||||
"style": Object {
|
||||
"borderBottomColor": "red",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightColor": "red",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopColor": "red",
|
||||
"borderTopWidth": "0px",
|
||||
"left": "50px",
|
||||
"pointerEvents": "box-only",
|
||||
"position": "absolute",
|
||||
"width": "200px",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/registry resolve without register, resolves to inline styles 3`] = `
|
||||
Object {
|
||||
"className": "
|
||||
",
|
||||
"style": Object {
|
||||
"borderBottomColor": "red",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightColor": "red",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopColor": "red",
|
||||
"borderTopWidth": "0px",
|
||||
"left": "50px",
|
||||
"pointerEvents": "box-only",
|
||||
"position": "absolute",
|
||||
"width": "100px",
|
||||
},
|
||||
}
|
||||
`;
|
||||
28
src/apis/StyleSheet/__tests__/createReactDOMStyle-test.js
Normal file
28
src/apis/StyleSheet/__tests__/createReactDOMStyle-test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import createReactDOMStyle from '../createReactDOMStyle';
|
||||
|
||||
const reactNativeStyle = {
|
||||
boxShadow: '1px 1px 1px 1px #000',
|
||||
borderWidthLeft: 2,
|
||||
borderWidth: 1,
|
||||
borderWidthRight: 3,
|
||||
display: 'flex',
|
||||
marginVertical: 0,
|
||||
opacity: 0,
|
||||
shadowColor: 'red',
|
||||
shadowOffset: { width: 1, height: 2 },
|
||||
resizeMode: 'contain'
|
||||
};
|
||||
|
||||
describe('apis/StyleSheet/createReactDOMStyle', () => {
|
||||
test('converts ReactNative style to ReactDOM style', () => {
|
||||
expect(createReactDOMStyle(reactNativeStyle)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('noop on DOM styles', () => {
|
||||
const firstStyle = createReactDOMStyle(reactNativeStyle);
|
||||
const secondStyle = createReactDOMStyle(firstStyle);
|
||||
expect(firstStyle).toEqual(secondStyle);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import createReactStyleObject from '../createReactStyleObject';
|
||||
|
||||
describe('apis/StyleSheet/createReactStyleObject', () => {
|
||||
test('converts ReactNative style to ReactDOM style', () => {
|
||||
const reactNativeStyle = { display: 'flex', marginVertical: 0, opacity: 0 };
|
||||
const expectedStyle = { display: 'flex', marginTop: '0px', marginBottom: '0px', opacity: 0 };
|
||||
|
||||
expect(createReactStyleObject(reactNativeStyle)).toEqual(expectedStyle);
|
||||
});
|
||||
});
|
||||
@@ -9,9 +9,9 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
import flattenStyle from '..';
|
||||
import flattenStyle from '../flattenStyle';
|
||||
|
||||
describe('modules/flattenStyle', () => {
|
||||
describe('apis/StyleSheet/flattenStyle', () => {
|
||||
test('should merge style objects', () => {
|
||||
const style = flattenStyle([
|
||||
{ opacity: 1 },
|
||||
16
src/apis/StyleSheet/__tests__/generateCss-test.js
Normal file
16
src/apis/StyleSheet/__tests__/generateCss-test.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import generateCss from '../generateCss';
|
||||
|
||||
describe('apis/StyleSheet/generateCss', () => {
|
||||
test('generates correct css', () => {
|
||||
const style = {
|
||||
boxShadow: '1px 1px 1px 1px #000',
|
||||
borderWidthLeft: 2,
|
||||
borderWidthRight: 3,
|
||||
position: 'absolute',
|
||||
transitionDuration: '0.1s'
|
||||
};
|
||||
expect(generateCss(style)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import { getDefaultStyleSheet } from '../css';
|
||||
import StyleSheet from '..';
|
||||
import StyleRegistry from '../registry';
|
||||
|
||||
const isPlainObject = (x) => {
|
||||
const toString = Object.prototype.toString;
|
||||
@@ -16,7 +16,7 @@ const isPlainObject = (x) => {
|
||||
|
||||
describe('apis/StyleSheet', () => {
|
||||
beforeEach(() => {
|
||||
StyleSheet._reset();
|
||||
StyleRegistry.reset();
|
||||
});
|
||||
|
||||
test('absoluteFill', () => {
|
||||
@@ -32,11 +32,6 @@ describe('apis/StyleSheet', () => {
|
||||
const style = StyleSheet.create({ root: { opacity: 1 } });
|
||||
expect(Number.isInteger(style.root) === true).toBeTruthy();
|
||||
});
|
||||
|
||||
test('renders a style sheet in the browser', () => {
|
||||
StyleSheet.create({ root: { color: 'red' } });
|
||||
expect(document.getElementById('react-native-style__').textContent).toEqual(getDefaultStyleSheet());
|
||||
});
|
||||
});
|
||||
|
||||
test('flatten', () => {
|
||||
@@ -47,18 +42,17 @@ describe('apis/StyleSheet', () => {
|
||||
expect(Number.isInteger(StyleSheet.hairlineWidth) === true).toBeTruthy();
|
||||
});
|
||||
|
||||
test('render', () => {
|
||||
expect(StyleSheet.render().props.dangerouslySetInnerHTML.__html).toEqual(getDefaultStyleSheet());
|
||||
});
|
||||
|
||||
test('resolve', () => {
|
||||
expect(StyleSheet.resolve({
|
||||
className: 'test',
|
||||
style: {
|
||||
display: 'flex',
|
||||
opacity: 1,
|
||||
pointerEvents: 'box-none'
|
||||
test('renderToString', () => {
|
||||
StyleSheet.create({
|
||||
a: {
|
||||
borderWidth: 0,
|
||||
borderColor: 'red'
|
||||
},
|
||||
b: {
|
||||
position: 'absolute',
|
||||
left: 50
|
||||
}
|
||||
})).toMatchSnapshot();
|
||||
});
|
||||
expect(StyleSheet.renderToString()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
22
src/apis/StyleSheet/__tests__/injector.js
Normal file
22
src/apis/StyleSheet/__tests__/injector.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import injector from '../injector';
|
||||
|
||||
describe('apis/StyleSheet/injector', () => {
|
||||
beforeEach(() => {
|
||||
document.head.insertAdjacentHTML('afterbegin', `
|
||||
<style id="react-native-stylesheet">
|
||||
.rn-alignItems\\:stretch{align-items:stretch;}
|
||||
.rn-position\\:top{position:top;}
|
||||
</style>
|
||||
`);
|
||||
});
|
||||
|
||||
test('hydrates from SSR', () => {
|
||||
const classList = injector.getClassNames();
|
||||
expect(classList).toEqual({
|
||||
'rn-alignItems\\:stretch': true,
|
||||
'rn-position\\:top': true
|
||||
});
|
||||
});
|
||||
});
|
||||
13
src/apis/StyleSheet/__tests__/prefixInlineStyles-test.js
Normal file
13
src/apis/StyleSheet/__tests__/prefixInlineStyles-test.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import prefixInlineStyles from '../prefixInlineStyles';
|
||||
|
||||
describe('apis/StyleSheet/prefixInlineStyles', () => {
|
||||
test('handles array values', () => {
|
||||
const style = {
|
||||
display: [ '-webkit-flex', 'flex' ]
|
||||
};
|
||||
|
||||
expect(prefixInlineStyles(style)).toEqual({ display: 'flex' });
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import processTextShadow from '../processTextShadow';
|
||||
|
||||
describe('apis/StyleSheet/processTextShadow', () => {
|
||||
test('textShadowOffset', () => {
|
||||
const style = {
|
||||
textShadowColor: 'red',
|
||||
textShadowOffset: { width: 2, height: 2 },
|
||||
textShadowRadius: 5
|
||||
};
|
||||
|
||||
expect(processTextShadow(style)).toEqual({
|
||||
textShadow: '2px 2px 5px red',
|
||||
textShadowColor: null,
|
||||
textShadowOffset: null,
|
||||
textShadowRadius: null
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import processTransform from '../processTransform';
|
||||
|
||||
describe('apis/StyleSheet/processTransform', () => {
|
||||
test('transform', () => {
|
||||
const style = {
|
||||
transform: [
|
||||
{ scaleX: 20 },
|
||||
{ translateX: 20 },
|
||||
{ rotate: '20deg' }
|
||||
]
|
||||
};
|
||||
|
||||
expect(processTransform(style)).toEqual({ transform: 'scaleX(20) translateX(20px) rotate(20deg)' });
|
||||
});
|
||||
|
||||
test('transformMatrix', () => {
|
||||
const style = {
|
||||
transformMatrix: [ 1, 2, 3, 4, 5, 6 ]
|
||||
};
|
||||
|
||||
expect(processTransform(style)).toEqual({
|
||||
transform: 'matrix3d(1,2,3,4,5,6)',
|
||||
transformMatrix: null
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import processVendorPrefixes from '../processVendorPrefixes';
|
||||
|
||||
describe('apis/StyleSheet/processVendorPrefixes', () => {
|
||||
test('handles array values', () => {
|
||||
const style = {
|
||||
display: [ '-webkit-flex', 'flex' ]
|
||||
};
|
||||
|
||||
expect(processVendorPrefixes(style)).toEqual({ display: 'flex' });
|
||||
});
|
||||
});
|
||||
54
src/apis/StyleSheet/__tests__/registry-test.js
Normal file
54
src/apis/StyleSheet/__tests__/registry-test.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import StyleRegistry from '../registry';
|
||||
|
||||
describe('apis/StyleSheet/registry', () => {
|
||||
beforeEach(() => {
|
||||
StyleRegistry.reset();
|
||||
});
|
||||
|
||||
test('register', () => {
|
||||
const style = { opacity: 0 };
|
||||
const id = StyleRegistry.register(style);
|
||||
expect(typeof id === 'number').toBe(true);
|
||||
});
|
||||
|
||||
describe('resolve', () => {
|
||||
const styleA = { borderWidth: 0, borderColor: 'red', width: 100 };
|
||||
const styleB = { position: 'absolute', left: 50, pointerEvents: 'box-only' };
|
||||
const styleC = { width: 200 };
|
||||
|
||||
const testResolve = (a, b, c) => {
|
||||
// no common properties, different resolving order, same result
|
||||
const resolve1 = StyleRegistry.resolve([ a, b ]);
|
||||
expect(resolve1).toMatchSnapshot();
|
||||
const resolve2 = StyleRegistry.resolve([ b, a ]);
|
||||
expect(resolve1).toEqual(resolve2);
|
||||
|
||||
// common properties, different resolving order, different result
|
||||
const resolve3 = StyleRegistry.resolve([ a, b, c ]);
|
||||
expect(resolve3).toMatchSnapshot();
|
||||
const resolve4 = StyleRegistry.resolve([ c, a, b ]);
|
||||
expect(resolve4).toMatchSnapshot();
|
||||
expect(resolve3).not.toEqual(resolve4);
|
||||
};
|
||||
|
||||
test('with register, resolves to className', () => {
|
||||
const a = StyleRegistry.register(styleA);
|
||||
const b = StyleRegistry.register(styleB);
|
||||
const c = StyleRegistry.register(styleC);
|
||||
testResolve(a, b, c);
|
||||
});
|
||||
|
||||
test('with register, resolves to mixed', () => {
|
||||
const a = styleA;
|
||||
const b = StyleRegistry.register(styleB);
|
||||
const c = StyleRegistry.register(styleC);
|
||||
testResolve(a, b, c);
|
||||
});
|
||||
|
||||
test('without register, resolves to inline styles', () => {
|
||||
testResolve(styleA, styleB, styleC);
|
||||
});
|
||||
});
|
||||
});
|
||||
60
src/apis/StyleSheet/__tests__/resolveBoxShadow-test.js
Normal file
60
src/apis/StyleSheet/__tests__/resolveBoxShadow-test.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import resolveBoxShadow from '../resolveBoxShadow';
|
||||
|
||||
describe('apis/StyleSheet/resolveBoxShadow', () => {
|
||||
test('shadowColor only', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { shadowColor: 'red' };
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
boxShadow: '0px 0px 0px rgba(255,0,0,1)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowColor and shadowOpacity only', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { shadowColor: 'red', shadowOpacity: 0.5 };
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
boxShadow: '0px 0px 0px rgba(255,0,0,0.5)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowOffset only', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { shadowOffset: { width: 1, height: 2 } };
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
boxShadow: '1px 2px 0px rgba(0,0,0,0)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowRadius only', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { shadowRadius: 5 };
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
boxShadow: '0px 0px 5px rgba(0,0,0,0)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowOffset, shadowRadius, shadowColor', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = {
|
||||
shadowColor: 'rgba(50,60,70,0.5)',
|
||||
shadowOffset: { width: 1, height: 2 },
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 3
|
||||
};
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
boxShadow: '1px 2px 3px rgba(50,60,70,0.25)'
|
||||
});
|
||||
});
|
||||
});
|
||||
19
src/apis/StyleSheet/__tests__/resolveTextShadow-test.js
Normal file
19
src/apis/StyleSheet/__tests__/resolveTextShadow-test.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import resolveTextShadow from '../resolveTextShadow';
|
||||
|
||||
describe('apis/StyleSheet/resolveTextShadow', () => {
|
||||
test('textShadowOffset', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = {
|
||||
textShadowColor: 'red',
|
||||
textShadowOffset: { width: 1, height: 2 },
|
||||
textShadowRadius: 5
|
||||
};
|
||||
resolveTextShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
textShadow: '1px 2px 5px red'
|
||||
});
|
||||
});
|
||||
});
|
||||
31
src/apis/StyleSheet/__tests__/resolveTransform-test.js
Normal file
31
src/apis/StyleSheet/__tests__/resolveTransform-test.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import resolveTransform from '../resolveTransform';
|
||||
|
||||
describe('apis/StyleSheet/resolveTransform', () => {
|
||||
test('transform', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = {
|
||||
transform: [
|
||||
{ scaleX: 20 },
|
||||
{ translateX: 20 },
|
||||
{ rotate: '20deg' }
|
||||
]
|
||||
};
|
||||
resolveTransform(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
transform: 'scaleX(20) translateX(20px) rotate(20deg)'
|
||||
});
|
||||
});
|
||||
|
||||
test('transformMatrix', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { transformMatrix: [ 1, 2, 3, 4, 5, 6 ] };
|
||||
resolveTransform(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
transform: 'matrix3d(1,2,3,4,5,6)'
|
||||
});
|
||||
});
|
||||
});
|
||||
6
src/apis/StyleSheet/createReactDOMStyle.js
Normal file
6
src/apis/StyleSheet/createReactDOMStyle.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import expandStyle from './expandStyle';
|
||||
import i18nStyle from './i18nStyle';
|
||||
|
||||
const createReactDOMStyle = (flattenedReactNativeStyle) => expandStyle(i18nStyle(flattenedReactNativeStyle));
|
||||
|
||||
module.exports = createReactDOMStyle;
|
||||
@@ -1,20 +0,0 @@
|
||||
import expandStyle from './expandStyle';
|
||||
import flattenStyle from '../../modules/flattenStyle';
|
||||
import i18nStyle from './i18nStyle';
|
||||
import processTextShadow from './processTextShadow';
|
||||
import processTransform from './processTransform';
|
||||
import processVendorPrefixes from './processVendorPrefixes';
|
||||
|
||||
const processors = [
|
||||
processTextShadow,
|
||||
processTransform,
|
||||
processVendorPrefixes
|
||||
];
|
||||
|
||||
const applyProcessors = (style) => processors.reduce((style, processor) => processor(style), style);
|
||||
|
||||
const createReactDOMStyleObject = (reactNativeStyle) => applyProcessors(
|
||||
expandStyle(i18nStyle(flattenStyle(reactNativeStyle)))
|
||||
);
|
||||
|
||||
module.exports = createReactDOMStyleObject;
|
||||
@@ -1,42 +0,0 @@
|
||||
const DISPLAY_FLEX_CLASSNAME = '__style_df';
|
||||
const POINTER_EVENTS_AUTO_CLASSNAME = '__style_pea';
|
||||
const POINTER_EVENTS_BOX_NONE_CLASSNAME = '__style_pebn';
|
||||
const POINTER_EVENTS_BOX_ONLY_CLASSNAME = '__style_pebo';
|
||||
const POINTER_EVENTS_NONE_CLASSNAME = '__style_pen';
|
||||
|
||||
/* eslint-disable max-len */
|
||||
const CSS_RESET =
|
||||
// reset unwanted styles
|
||||
'/* React Native */\n' +
|
||||
'html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}\n' +
|
||||
'body{margin:0}\n' +
|
||||
'button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}\n' +
|
||||
'input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,' +
|
||||
'input::-webkit-search-cancel-button,input::-webkit-search-decoration,' +
|
||||
'input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none}';
|
||||
|
||||
const CSS_HELPERS =
|
||||
// vendor prefix 'display:flex' until React supports fallback values for inline styles
|
||||
`.${DISPLAY_FLEX_CLASSNAME} {display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}\n` +
|
||||
// implement React Native's pointer event values
|
||||
`.${POINTER_EVENTS_AUTO_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME}, .${POINTER_EVENTS_BOX_NONE_CLASSNAME} * {pointer-events:auto}\n` +
|
||||
`.${POINTER_EVENTS_NONE_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME} *, .${POINTER_EVENTS_NONE_CLASSNAME} {pointer-events:none}`;
|
||||
/* eslint-enable max-len */
|
||||
|
||||
const styleAsClassName = {
|
||||
display: {
|
||||
'flex': DISPLAY_FLEX_CLASSNAME
|
||||
},
|
||||
pointerEvents: {
|
||||
'auto': POINTER_EVENTS_AUTO_CLASSNAME,
|
||||
'box-none': POINTER_EVENTS_BOX_NONE_CLASSNAME,
|
||||
'box-only': POINTER_EVENTS_BOX_ONLY_CLASSNAME,
|
||||
'none': POINTER_EVENTS_NONE_CLASSNAME
|
||||
}
|
||||
};
|
||||
|
||||
export const getDefaultStyleSheet = () => `${CSS_RESET}\n${CSS_HELPERS}`;
|
||||
|
||||
export const getStyleAsHelperClassName = (prop, value) => {
|
||||
return styleAsClassName[prop] && styleAsClassName[prop][value];
|
||||
};
|
||||
@@ -10,6 +10,9 @@
|
||||
*/
|
||||
|
||||
import normalizeValue from './normalizeValue';
|
||||
import resolveBoxShadow from './resolveBoxShadow';
|
||||
import resolveTextShadow from './resolveTextShadow';
|
||||
import resolveTransform from './resolveTransform';
|
||||
|
||||
const emptyObject = {};
|
||||
const styleShortFormProperties = {
|
||||
@@ -28,46 +31,83 @@ const styleShortFormProperties = {
|
||||
writingDirection: [ 'direction' ]
|
||||
};
|
||||
|
||||
const alphaSort = (arr) => arr.sort((a, b) => {
|
||||
const alphaSortProps = (propsArray) => propsArray.sort((a, b) => {
|
||||
if (a < b) { return -1; }
|
||||
if (a > b) { return 1; }
|
||||
return 0;
|
||||
});
|
||||
|
||||
const createStyleReducer = (originalStyle) => {
|
||||
const originalStyleProps = Object.keys(originalStyle);
|
||||
const expandStyle = (style) => {
|
||||
if (!style) { return emptyObject; }
|
||||
const styleProps = Object.keys(style);
|
||||
const sortedStyleProps = alphaSortProps(styleProps);
|
||||
let hasResolvedBoxShadow = false;
|
||||
let hasResolvedTextShadow = false;
|
||||
|
||||
return (style, prop) => {
|
||||
const value = normalizeValue(prop, originalStyle[prop]);
|
||||
const longFormProperties = styleShortFormProperties[prop];
|
||||
const reducer = (resolvedStyle, prop) => {
|
||||
const value = normalizeValue(prop, style[prop]);
|
||||
if (value == null) { return resolvedStyle; }
|
||||
|
||||
// React Native treats `flex:1` like `flex:1 1 auto`
|
||||
if (prop === 'flex') {
|
||||
style.flexGrow = value;
|
||||
style.flexShrink = 1;
|
||||
style.flexBasis = 'auto';
|
||||
// React Native accepts 'center' as a value
|
||||
} else if (prop === 'textAlignVertical') {
|
||||
style.verticalAlign = (value === 'center' ? 'middle' : value);
|
||||
} else if (longFormProperties) {
|
||||
longFormProperties.forEach((longForm, i) => {
|
||||
// the value of any longform property in the original styles takes
|
||||
// precedence over the shortform's value
|
||||
if (originalStyleProps.indexOf(longForm) === -1) {
|
||||
style[longForm] = value;
|
||||
switch (prop) {
|
||||
// ignore React Native styles
|
||||
case 'elevation':
|
||||
case 'resizeMode': {
|
||||
break;
|
||||
}
|
||||
case 'flex': {
|
||||
resolvedStyle.flexGrow = value;
|
||||
resolvedStyle.flexShrink = 1;
|
||||
resolvedStyle.flexBasis = 'auto';
|
||||
break;
|
||||
}
|
||||
case 'shadowColor':
|
||||
case 'shadowOffset':
|
||||
case 'shadowOpacity':
|
||||
case 'shadowRadius': {
|
||||
if (!hasResolvedBoxShadow) {
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
style[prop] = value;
|
||||
hasResolvedBoxShadow = true;
|
||||
break;
|
||||
}
|
||||
case 'textAlignVertical': {
|
||||
resolvedStyle.verticalAlign = (value === 'center' ? 'middle' : value);
|
||||
break;
|
||||
}
|
||||
case 'textShadowColor':
|
||||
case 'textShadowOffset':
|
||||
case 'textShadowRadius': {
|
||||
if (!hasResolvedTextShadow) {
|
||||
resolveTextShadow(resolvedStyle, style);
|
||||
}
|
||||
hasResolvedTextShadow = true;
|
||||
break;
|
||||
}
|
||||
case 'transform': {
|
||||
resolveTransform(resolvedStyle, style);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const longFormProperties = styleShortFormProperties[prop];
|
||||
if (longFormProperties) {
|
||||
longFormProperties.forEach((longForm, i) => {
|
||||
// the value of any longform property in the original styles takes
|
||||
// precedence over the shortform's value
|
||||
if (styleProps.indexOf(longForm) === -1) {
|
||||
resolvedStyle[longForm] = value;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolvedStyle[prop] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return style;
|
||||
};
|
||||
};
|
||||
|
||||
const expandStyle = (style = emptyObject) => {
|
||||
const sortedStyleProps = alphaSort(Object.keys(style));
|
||||
const styleReducer = createStyleReducer(style);
|
||||
return sortedStyleProps.reduce(styleReducer, {});
|
||||
return resolvedStyle;
|
||||
};
|
||||
|
||||
const resolvedStyle = sortedStyleProps.reduce(reducer, {});
|
||||
return resolvedStyle;
|
||||
};
|
||||
|
||||
module.exports = expandStyle;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
@@ -10,10 +9,8 @@
|
||||
* @providesModule flattenStyle
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactNativePropRegistry = require('../ReactNativePropRegistry');
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
function getStyle(style) {
|
||||
if (typeof style === 'number') {
|
||||
@@ -22,22 +19,26 @@ function getStyle(style) {
|
||||
return style;
|
||||
}
|
||||
|
||||
function flattenStyle(style: ?StyleObj): ?Object {
|
||||
function flattenStyle(style) {
|
||||
if (!style) {
|
||||
return undefined;
|
||||
}
|
||||
invariant(style !== true, 'style may be false but not true');
|
||||
|
||||
if (process.env.NODE !== 'production') {
|
||||
invariant(style !== true, 'style may be false but not true');
|
||||
}
|
||||
|
||||
if (!Array.isArray(style)) {
|
||||
return getStyle(style);
|
||||
}
|
||||
|
||||
var result = {};
|
||||
for (var i = 0, styleLength = style.length; i < styleLength; ++i) {
|
||||
var computedStyle = flattenStyle(style[i]);
|
||||
const result = {};
|
||||
for (let i = 0, styleLength = style.length; i < styleLength; ++i) {
|
||||
const computedStyle = flattenStyle(style[i]);
|
||||
if (computedStyle) {
|
||||
for (var key in computedStyle) {
|
||||
result[key] = computedStyle[key];
|
||||
for (const key in computedStyle) {
|
||||
const value = computedStyle[key];
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/apis/StyleSheet/generateCss.js
Normal file
23
src/apis/StyleSheet/generateCss.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import hyphenate from './hyphenate';
|
||||
import mapKeyValue from '../../modules/mapKeyValue';
|
||||
import normalizeValue from './normalizeValue';
|
||||
import prefixAll from 'inline-style-prefixer/static';
|
||||
|
||||
const createDeclarationString = (prop, val) => {
|
||||
const name = hyphenate(prop);
|
||||
const value = normalizeValue(prop, val);
|
||||
if (Array.isArray(val)) {
|
||||
return val.map((v) => `${name}:${v}`).join(';');
|
||||
}
|
||||
return `${name}:${value}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates valid CSS rule body from a JS object
|
||||
*
|
||||
* generateCss({ width: 20, color: 'blue' });
|
||||
* // => 'color:blue;width:20px'
|
||||
*/
|
||||
const generateCss = (style) => mapKeyValue(prefixAll(style), createDeclarationString).sort().join(';');
|
||||
|
||||
module.exports = generateCss;
|
||||
3
src/apis/StyleSheet/hyphenate.js
Normal file
3
src/apis/StyleSheet/hyphenate.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const RE_1 = /([A-Z])/g;
|
||||
const RE_2 = /^ms-/;
|
||||
module.exports = (s) => s.replace(RE_1, '-$1').toLowerCase().replace(RE_2, '-ms-');
|
||||
@@ -1,6 +1,8 @@
|
||||
import I18nManager from '../I18nManager';
|
||||
import multiplyStyleLengthValue from '../../modules/multiplyStyleLengthValue';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
/**
|
||||
* Map of property names to their BiDi equivalent.
|
||||
*/
|
||||
@@ -64,38 +66,40 @@ const swapLtrRtl = (value:String): String => {
|
||||
return value === 'ltr' ? 'rtl' : value === 'rtl' ? 'ltr' : value;
|
||||
};
|
||||
|
||||
const i18nStyle = (style = {}) => {
|
||||
const i18nStyle = (style = emptyObject) => {
|
||||
const newStyle = {};
|
||||
for (const prop in style) {
|
||||
if (style.hasOwnProperty(prop)) {
|
||||
const indexOfNoFlip = prop.indexOf('$noI18n');
|
||||
if (!Object.prototype.hasOwnProperty.call(style, prop)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (I18nManager.isRTL) {
|
||||
if (PROPERTIES_TO_SWAP[prop]) {
|
||||
const newProp = flipProperty(prop);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else if (PROPERTIES_SWAP_LEFT_RIGHT[prop]) {
|
||||
newStyle[prop] = swapLeftRight(style[prop]);
|
||||
} else if (PROPERTIES_SWAP_LTR_RTL[prop]) {
|
||||
newStyle[prop] = swapLtrRtl(style[prop]);
|
||||
} else if (prop === 'textShadowOffset') {
|
||||
newStyle[prop] = style[prop];
|
||||
newStyle[prop].width = additiveInverse(style[prop].width);
|
||||
} else if (prop === 'transform') {
|
||||
newStyle[prop] = style[prop].map(flipTransform);
|
||||
} else if (indexOfNoFlip > -1) {
|
||||
const newProp = prop.substring(0, indexOfNoFlip);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else {
|
||||
newStyle[prop] = style[prop];
|
||||
}
|
||||
const indexOfNoFlip = prop.indexOf('$noI18n');
|
||||
|
||||
if (I18nManager.isRTL) {
|
||||
if (PROPERTIES_TO_SWAP[prop]) {
|
||||
const newProp = flipProperty(prop);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else if (PROPERTIES_SWAP_LEFT_RIGHT[prop]) {
|
||||
newStyle[prop] = swapLeftRight(style[prop]);
|
||||
} else if (PROPERTIES_SWAP_LTR_RTL[prop]) {
|
||||
newStyle[prop] = swapLtrRtl(style[prop]);
|
||||
} else if (prop === 'textShadowOffset') {
|
||||
newStyle[prop] = style[prop];
|
||||
newStyle[prop].width = additiveInverse(style[prop].width);
|
||||
} else if (prop === 'transform') {
|
||||
newStyle[prop] = style[prop].map(flipTransform);
|
||||
} else if (indexOfNoFlip > -1) {
|
||||
const newProp = prop.substring(0, indexOfNoFlip);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else {
|
||||
if (indexOfNoFlip > -1) {
|
||||
const newProp = prop.substring(0, indexOfNoFlip);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else {
|
||||
newStyle[prop] = style[prop];
|
||||
}
|
||||
newStyle[prop] = style[prop];
|
||||
}
|
||||
} else {
|
||||
if (indexOfNoFlip > -1) {
|
||||
const newProp = prop.substring(0, indexOfNoFlip);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else {
|
||||
newStyle[prop] = style[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,26 @@
|
||||
import * as css from './css';
|
||||
import createReactStyleObject from './createReactStyleObject';
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import flattenStyle from '../../modules/flattenStyle';
|
||||
import React from 'react';
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||
import flattenStyle from './flattenStyle';
|
||||
import initialize from './initialize';
|
||||
import injector from './injector';
|
||||
import StyleRegistry from './registry';
|
||||
|
||||
let styleElement;
|
||||
let shouldInsertStyleSheet = ExecutionEnvironment.canUseDOM;
|
||||
|
||||
const STYLE_SHEET_ID = 'react-native-style__';
|
||||
initialize();
|
||||
|
||||
const absoluteFillObject = { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 };
|
||||
|
||||
const defaultStyleSheet = css.getDefaultStyleSheet();
|
||||
|
||||
const insertStyleSheet = () => {
|
||||
// check if the server rendered the style sheet
|
||||
styleElement = document.getElementById(STYLE_SHEET_ID);
|
||||
// if not, inject the style sheet
|
||||
if (!styleElement) {
|
||||
document.head.insertAdjacentHTML(
|
||||
'afterbegin',
|
||||
`<style id="${STYLE_SHEET_ID}">${defaultStyleSheet}</style>`
|
||||
);
|
||||
shouldInsertStyleSheet = false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* For testing
|
||||
* @private
|
||||
*/
|
||||
_reset() {
|
||||
if (styleElement) {
|
||||
document.head.removeChild(styleElement);
|
||||
styleElement = null;
|
||||
shouldInsertStyleSheet = true;
|
||||
}
|
||||
},
|
||||
|
||||
absoluteFill: ReactNativePropRegistry.register(absoluteFillObject),
|
||||
|
||||
absoluteFill: StyleRegistry.register(absoluteFillObject),
|
||||
absoluteFillObject,
|
||||
|
||||
create(styles) {
|
||||
if (shouldInsertStyleSheet) {
|
||||
insertStyleSheet();
|
||||
}
|
||||
|
||||
const result = {};
|
||||
for (const key in styles) {
|
||||
Object.keys(styles).forEach((key) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
require('./StyleSheetValidation').validateStyle(key, styles);
|
||||
}
|
||||
result[key] = ReactNativePropRegistry.register(styles[key]);
|
||||
}
|
||||
result[key] = StyleRegistry.register(styles[key]);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
hairlineWidth: 1,
|
||||
|
||||
flatten: flattenStyle,
|
||||
|
||||
/* @platform web */
|
||||
render() {
|
||||
return <style dangerouslySetInnerHTML={{ __html: defaultStyleSheet }} id={STYLE_SHEET_ID} />;
|
||||
},
|
||||
|
||||
/**
|
||||
* Accepts React props and converts style declarations to classNames when necessary
|
||||
* @platform web
|
||||
*/
|
||||
resolve(props) {
|
||||
let className = props.className || '';
|
||||
const style = createReactStyleObject(props.style);
|
||||
for (const prop in style) {
|
||||
const value = style[prop];
|
||||
const replacementClassName = css.getStyleAsHelperClassName(prop, value);
|
||||
if (replacementClassName) {
|
||||
className += ` ${replacementClassName}`;
|
||||
style[prop] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return { className, style };
|
||||
}
|
||||
renderToString: injector.getStyleSheetHtml
|
||||
};
|
||||
|
||||
39
src/apis/StyleSheet/initialize.js
Normal file
39
src/apis/StyleSheet/initialize.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import injector from './injector';
|
||||
import StyleRegistry from './registry';
|
||||
|
||||
const initialize = () => {
|
||||
injector.addRule(
|
||||
'reset',
|
||||
'/* React Native StyleSheet*/\n' +
|
||||
'html{' +
|
||||
'font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;' +
|
||||
'-webkit-tap-highlight-color:rgba(0,0,0,0)' +
|
||||
'}\n' +
|
||||
'body{margin:0}\n' +
|
||||
'button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}\n' +
|
||||
'input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,' +
|
||||
'input::-webkit-search-cancel-button,input::-webkit-search-decoration,' +
|
||||
'input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none}'
|
||||
);
|
||||
injector.addRule(
|
||||
'keyframes',
|
||||
'@keyframes rn-ActivityIndicator-animation{' +
|
||||
'0%{-webkit-transform: rotate(0deg); transform: rotate(0deg);}' +
|
||||
'100%{-webkit-transform: rotate(360deg); transform: rotate(360deg);}' +
|
||||
'}\n' +
|
||||
'@keyframes rn-ProgressBar-animation{' +
|
||||
'0%{-webkit-transform: translateX(-100%); transform: translateX(-100%);}' +
|
||||
'100%{-webkit-transform: translateX(400%); transform: translateX(400%);}' +
|
||||
'}'
|
||||
);
|
||||
injector.addRule(
|
||||
'pointer-events',
|
||||
'.rn-pointerEvents\\:auto,.rn_pointerEvents\\:box-only,.rn-pointerEvents\\:box-none *{pointer-events:auto}' +
|
||||
'.rn-pointerEvents\\:none,.rn_pointerEvents\\:box-only *,.rn-pointerEvents\\:box-none{pointer-events:none}'
|
||||
);
|
||||
|
||||
const classNames = injector.getClassNames();
|
||||
StyleRegistry.initialize(classNames);
|
||||
};
|
||||
|
||||
export default initialize;
|
||||
106
src/apis/StyleSheet/injector.js
Normal file
106
src/apis/StyleSheet/injector.js
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Based on
|
||||
* https://github.com/lelandrichardson/react-primitives/blob/master/src/StyleSheet/injector.js
|
||||
*/
|
||||
|
||||
import asap from 'asap';
|
||||
|
||||
const emptyObject = {};
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
const CLASSNAME_REXEP = /\.rn-([^{;\s]+)/g;
|
||||
const STYLE_ELEMENT_ID = 'react-native-stylesheet';
|
||||
|
||||
let registry = {};
|
||||
let isDirty = false;
|
||||
|
||||
/**
|
||||
* Registers a rule and requests an update to the style sheet
|
||||
*/
|
||||
const addRule = (key, rule) => {
|
||||
if (!registry[key]) {
|
||||
registry[key] = rule;
|
||||
isDirty = true;
|
||||
if (global.document) {
|
||||
asap(frame);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a string of the registered rules
|
||||
*/
|
||||
const getStyleText = () => {
|
||||
/* eslint prefer-template:0 */
|
||||
let result = '\n';
|
||||
for (const key in registry) {
|
||||
if (hasOwnProperty.call(registry, key)) {
|
||||
result += registry[key] + '\n';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an HTML string for server rendering
|
||||
*/
|
||||
const getStyleSheetHtml = () => `<style id="${STYLE_ELEMENT_ID}">${getStyleText()}</style>`;
|
||||
|
||||
const reset = () => { registry = {}; };
|
||||
|
||||
/**
|
||||
* Finds or injects the style sheet when in a browser environment
|
||||
*/
|
||||
let styleNode = null;
|
||||
const getStyleNode = () => {
|
||||
if (global.document) {
|
||||
if (!styleNode) {
|
||||
// look for existing style sheet (could also be server-rendered)
|
||||
styleNode = document.getElementById(STYLE_ELEMENT_ID);
|
||||
if (!styleNode) {
|
||||
// if there is no existing stylesheet, inject it style sheet
|
||||
document.head.insertAdjacentHTML('afterbegin', getStyleSheetHtml());
|
||||
styleNode = document.getElementById(STYLE_ELEMENT_ID);
|
||||
}
|
||||
}
|
||||
return styleNode;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines which classes are available in the existing document. Doesn't
|
||||
* rely on the registry so it can be used to read class names from a SSR style
|
||||
* sheet.
|
||||
*/
|
||||
const getClassNames = () => {
|
||||
const styleNode = getStyleNode();
|
||||
if (styleNode) {
|
||||
const text = styleNode.textContent;
|
||||
const matches = text.match(CLASSNAME_REXEP);
|
||||
if (matches) {
|
||||
return matches.map((name) => name.slice(1)).reduce((classMap, className) => {
|
||||
classMap[className] = true;
|
||||
return classMap;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
return emptyObject;
|
||||
};
|
||||
|
||||
const frame = () => {
|
||||
if (!isDirty || !global.document) { return; }
|
||||
isDirty = false;
|
||||
|
||||
const styleNode = getStyleNode();
|
||||
if (styleNode) {
|
||||
const css = getStyleText();
|
||||
styleNode.textContent = css;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
addRule,
|
||||
getClassNames,
|
||||
getStyleSheetHtml,
|
||||
reset
|
||||
};
|
||||
@@ -24,7 +24,9 @@ const unitlessNumbers = {
|
||||
scale: true,
|
||||
scaleX: true,
|
||||
scaleY: true,
|
||||
scaleZ: true
|
||||
scaleZ: true,
|
||||
// RN properties
|
||||
shadowOpacity: true
|
||||
};
|
||||
|
||||
const normalizeValue = (property, value) => {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import prefixAll from 'inline-style-prefixer/static';
|
||||
|
||||
const processVendorPrefixes = (style) => {
|
||||
const prefixInlineStyles = (style) => {
|
||||
const prefixedStyles = prefixAll(style);
|
||||
|
||||
// React@15 removed undocumented support for fallback values in
|
||||
// inline-styles. Revert array values to the standard CSS value
|
||||
for (const prop in prefixedStyles) {
|
||||
Object.keys(prefixedStyles).forEach((prop) => {
|
||||
const value = prefixedStyles[prop];
|
||||
if (Array.isArray(value)) {
|
||||
prefixedStyles[prop] = value[value.length - 1];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return prefixedStyles;
|
||||
};
|
||||
|
||||
module.exports = processVendorPrefixes;
|
||||
module.exports = prefixInlineStyles;
|
||||
@@ -1,19 +0,0 @@
|
||||
import normalizeValue from './normalizeValue';
|
||||
|
||||
const processTextShadow = (style) => {
|
||||
if (style && style.textShadowOffset) {
|
||||
const { height, width } = style.textShadowOffset;
|
||||
const offsetX = normalizeValue(null, height || 0);
|
||||
const offsetY = normalizeValue(null, width || 0);
|
||||
const blurRadius = normalizeValue(null, style.textShadowRadius || 0);
|
||||
const color = style.textShadowColor || 'currentcolor';
|
||||
|
||||
style.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
style.textShadowColor = null;
|
||||
style.textShadowOffset = null;
|
||||
style.textShadowRadius = null;
|
||||
}
|
||||
return style;
|
||||
};
|
||||
|
||||
module.exports = processTextShadow;
|
||||
212
src/apis/StyleSheet/registry.js
Normal file
212
src/apis/StyleSheet/registry.js
Normal file
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* WARNING: changes to this file in particular can cause significant changes to
|
||||
* the results of render performance benchmarks.
|
||||
*/
|
||||
|
||||
import createReactDOMStyle from './createReactDOMStyle';
|
||||
import flattenArray from '../../modules/flattenArray';
|
||||
import flattenStyle from './flattenStyle';
|
||||
import generateCss from './generateCss';
|
||||
import injector from './injector';
|
||||
import mapKeyValue from '../../modules/mapKeyValue';
|
||||
import prefixInlineStyles from './prefixInlineStyles';
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||
|
||||
const prefix = 'r';
|
||||
const SPACE_REGEXP = /\s/g;
|
||||
const ESCAPE_SELECTOR_CHARS_REGEXP = /[(),":?.%\\$#]/g;
|
||||
|
||||
/**
|
||||
* Creates an HTML class name for use on elements
|
||||
*/
|
||||
const createClassName = (prop, value) => {
|
||||
const val = `${value}`.replace(SPACE_REGEXP, '-');
|
||||
return `rn-${prop}:${val}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject a CSS rule for a given declaration and record the availability of the
|
||||
* resulting class name.
|
||||
*/
|
||||
let injectedClassNames = {};
|
||||
const injectClassNameIfNeeded = (prop, value) => {
|
||||
const className = createClassName(prop, value);
|
||||
if (!injectedClassNames[className]) {
|
||||
// create a valid CSS selector for a given HTML class name
|
||||
const selector = className.replace(ESCAPE_SELECTOR_CHARS_REGEXP, '\\$&');
|
||||
const body = generateCss({ [prop]: value });
|
||||
const css = `.${selector}{${body}}`;
|
||||
// this adds the rule to the buffer to be injected into the document
|
||||
injector.addRule(className, css);
|
||||
injectedClassNames[className] = true;
|
||||
}
|
||||
|
||||
return className;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a React Native style object to HTML class names
|
||||
*/
|
||||
let resolvedPropsCache = {};
|
||||
const registerStyle = (id, flatStyle) => {
|
||||
const style = createReactDOMStyle(flatStyle);
|
||||
const className = mapKeyValue(style, (prop, value) => {
|
||||
if (value != null) {
|
||||
return injectClassNameIfNeeded(prop, value);
|
||||
}
|
||||
}).join(' ').trim();
|
||||
|
||||
const key = `${prefix}-${id}`;
|
||||
resolvedPropsCache[key] = { className };
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves a React Native style object to DOM props
|
||||
*/
|
||||
const resolveProps = (reactNativeStyle) => {
|
||||
const flatStyle = flattenStyle(reactNativeStyle);
|
||||
const domStyle = createReactDOMStyle(flatStyle);
|
||||
const style = {};
|
||||
|
||||
const _className = mapKeyValue(domStyle, (prop, value) => {
|
||||
if (value != null) {
|
||||
const singleClassName = createClassName(prop, value);
|
||||
if (injectedClassNames[singleClassName]) {
|
||||
return singleClassName;
|
||||
} else {
|
||||
// 4x slower render
|
||||
style[prop] = value;
|
||||
}
|
||||
}
|
||||
})
|
||||
// improves debugging in devtools and snapshots
|
||||
.join('\n')
|
||||
.trim();
|
||||
|
||||
const className = `\n${_className}`;
|
||||
|
||||
const props = {
|
||||
className,
|
||||
style: prefixInlineStyles(style)
|
||||
};
|
||||
|
||||
if (process.env.__REACT_NATIVE_DEBUG_ENABLED__) {
|
||||
console.groupCollapsed('[StyleSheet] resolving uncached styles');
|
||||
console.log(
|
||||
'Slow operation. Resolving style objects (uncached result). ' +
|
||||
'Occurs on first render and when using styles not registered with "StyleSheet.create"'
|
||||
);
|
||||
console.log('source => \n', reactNativeStyle);
|
||||
console.log('flatten => \n', flatStyle);
|
||||
console.log('resolve => \n', props);
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
return props;
|
||||
};
|
||||
|
||||
/**
|
||||
* Caching layer over 'resolveProps'
|
||||
*/
|
||||
const resolvePropsIfNeeded = (key, style) => {
|
||||
if (key) {
|
||||
if (!resolvedPropsCache[key]) {
|
||||
// slow: convert style object to props and cache
|
||||
resolvedPropsCache[key] = resolveProps(style);
|
||||
}
|
||||
return resolvedPropsCache[key];
|
||||
}
|
||||
return resolveProps(style);
|
||||
};
|
||||
|
||||
/**
|
||||
* Web style registry
|
||||
*/
|
||||
const StyleRegistry = {
|
||||
initialize(classNames) {
|
||||
injectedClassNames = classNames;
|
||||
|
||||
if (process.env.__REACT_NATIVE_DEBUG_ENABLED__) {
|
||||
if (global.__REACT_NATIVE_DEBUG_ENABLED__styleRegistryTimer) {
|
||||
clearInterval(global.__REACT_NATIVE_DEBUG_ENABLED__styleRegistryTimer);
|
||||
}
|
||||
global.__REACT_NATIVE_DEBUG_ENABLED__styleRegistryTimer = setInterval(() => {
|
||||
const entryCount = Object.keys(resolvedPropsCache).length;
|
||||
console.groupCollapsed('[StyleSheet] resolved props cache snapshot:', entryCount, 'entries');
|
||||
console.log(resolvedPropsCache);
|
||||
console.groupEnd();
|
||||
}, 30000);
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
injectedClassNames = {};
|
||||
resolvedPropsCache = {};
|
||||
injector.reset();
|
||||
},
|
||||
|
||||
register(style) {
|
||||
const id = ReactNativePropRegistry.register(style);
|
||||
return registerStyle(id, style);
|
||||
},
|
||||
|
||||
resolve(reactNativeStyle) {
|
||||
if (!reactNativeStyle) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// fast and cachable
|
||||
if (typeof reactNativeStyle === 'number') {
|
||||
const key = `${prefix}${reactNativeStyle}`;
|
||||
return resolvePropsIfNeeded(key, reactNativeStyle);
|
||||
}
|
||||
|
||||
// convert a RN style object to DOM props
|
||||
if (!Array.isArray(reactNativeStyle)) {
|
||||
return resolveProps(reactNativeStyle);
|
||||
}
|
||||
|
||||
// flatten the array
|
||||
// [ 1, [ 2, 3 ], { prop: value }, 4, 5 ] => [ 1, 2, 3, { prop: value }, 4, 5 ];
|
||||
const flatArray = flattenArray(reactNativeStyle);
|
||||
|
||||
let isArrayOfNumbers = true;
|
||||
for (let i = 0; i < flatArray.length; i++) {
|
||||
if (typeof flatArray[i] !== 'number') {
|
||||
isArrayOfNumbers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: determine when/if to cache unregistered styles. This produces 2x
|
||||
// faster benchmark results for unregistered styles. However, the cache
|
||||
// could be filled with props that are never used again.
|
||||
//
|
||||
// let hasValidKey = true;
|
||||
// let key = flatArray.reduce((keyParts, element) => {
|
||||
// if (typeof element === 'number') {
|
||||
// keyParts.push(element);
|
||||
// } else {
|
||||
// if (element.transform) {
|
||||
// hasValidKey = false;
|
||||
// } else {
|
||||
// const objectAsKey = Object.keys(element).map((prop) => `${prop}:${element[prop]}`).join(';');
|
||||
// if (objectAsKey !== '') {
|
||||
// keyParts.push(objectAsKey);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return keyParts;
|
||||
// }, [ prefix ]).join('-');
|
||||
// if (!hasValidKey) { key = null; }
|
||||
|
||||
// cache resolved props when all styles are registered
|
||||
const key = isArrayOfNumbers ? `${prefix}-${flatArray.join('-')}` : null;
|
||||
|
||||
return resolvePropsIfNeeded(key, flatArray);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = StyleRegistry;
|
||||
28
src/apis/StyleSheet/resolveBoxShadow.js
Normal file
28
src/apis/StyleSheet/resolveBoxShadow.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import normalizeColor from '../../modules/normalizeColor';
|
||||
import normalizeValue from './normalizeValue';
|
||||
|
||||
const defaultOffset = { height: 0, width: 0 };
|
||||
|
||||
const applyOpacity = (color, opacity = 1) => {
|
||||
const nullableColor = normalizeColor(color);
|
||||
const colorInt = nullableColor === null ? 0x00000000 : nullableColor;
|
||||
const r = Math.round(((colorInt & 0xff000000) >>> 24));
|
||||
const g = Math.round(((colorInt & 0x00ff0000) >>> 16));
|
||||
const b = Math.round(((colorInt & 0x0000ff00) >>> 8));
|
||||
const a = (((colorInt & 0x000000ff) >>> 0) / 255).toFixed(2);
|
||||
return `rgba(${r},${g},${b},${a * opacity})`;
|
||||
};
|
||||
|
||||
// TODO: add inset and spread support
|
||||
const resolveBoxShadow = (resolvedStyle, style) => {
|
||||
const { height, width } = style.shadowOffset || defaultOffset;
|
||||
const offsetX = normalizeValue(null, width);
|
||||
const offsetY = normalizeValue(null, height);
|
||||
const blurRadius = normalizeValue(null, style.shadowRadius || 0);
|
||||
const color = applyOpacity(style.shadowColor, style.shadowOpacity);
|
||||
|
||||
const boxShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
resolvedStyle.boxShadow = style.boxShadow ? `${style.boxShadow}, ${boxShadow}` : boxShadow;
|
||||
};
|
||||
|
||||
module.exports = resolveBoxShadow;
|
||||
15
src/apis/StyleSheet/resolveTextShadow.js
Normal file
15
src/apis/StyleSheet/resolveTextShadow.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import normalizeValue from './normalizeValue';
|
||||
|
||||
const defaultOffset = { height: 0, width: 0 };
|
||||
|
||||
const resolveTextShadow = (resolvedStyle, style) => {
|
||||
const { height, width } = style.textShadowOffset || defaultOffset;
|
||||
const offsetX = normalizeValue(null, width);
|
||||
const offsetY = normalizeValue(null, height);
|
||||
const blurRadius = normalizeValue(null, style.textShadowRadius || 0);
|
||||
const color = style.textShadowColor || 'currentcolor';
|
||||
|
||||
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
};
|
||||
|
||||
module.exports = resolveTextShadow;
|
||||
@@ -14,16 +14,14 @@ const convertTransformMatrix = (transformMatrix) => {
|
||||
return `matrix3d(${matrix})`;
|
||||
};
|
||||
|
||||
const processTransform = (style) => {
|
||||
if (style) {
|
||||
if (style.transform && Array.isArray(style.transform)) {
|
||||
style.transform = style.transform.map(mapTransform).join(' ');
|
||||
} else if (style.transformMatrix) {
|
||||
style.transform = convertTransformMatrix(style.transformMatrix);
|
||||
style.transformMatrix = null;
|
||||
}
|
||||
const resolveTransform = (resolvedStyle, style) => {
|
||||
if (Array.isArray(style.transform)) {
|
||||
const transform = style.transform.map(mapTransform).join(' ');
|
||||
resolvedStyle.transform = transform;
|
||||
} else if (style.transformMatrix) {
|
||||
const transform = convertTransformMatrix(style.transformMatrix);
|
||||
resolvedStyle.transform = transform;
|
||||
}
|
||||
return style;
|
||||
};
|
||||
|
||||
module.exports = processTransform;
|
||||
module.exports = resolveTransform;
|
||||
@@ -1,5 +1,7 @@
|
||||
import createReactStyleObject from '../StyleSheet/createReactStyleObject';
|
||||
import CSSPropertyOperations from 'react/lib/CSSPropertyOperations';
|
||||
import createReactDOMStyle from '../StyleSheet/createReactDOMStyle';
|
||||
import flattenStyle from '../StyleSheet/flattenStyle';
|
||||
import CSSPropertyOperations from 'react-dom/lib/CSSPropertyOperations';
|
||||
import prefixInlineStyles from '../StyleSheet/prefixInlineStyles';
|
||||
|
||||
const _measureLayout = (node, relativeToNativeNode, callback) => {
|
||||
const relativeNode = relativeToNativeNode || node.parentNode;
|
||||
@@ -35,17 +37,17 @@ const UIManager = {
|
||||
|
||||
updateView(node, props, component /* only needed to surpress React errors in development */) {
|
||||
for (const prop in props) {
|
||||
const value = props[prop];
|
||||
if (!Object.prototype.hasOwnProperty.call(props, prop)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = props[prop];
|
||||
switch (prop) {
|
||||
case 'style':
|
||||
// convert styles to DOM-styles
|
||||
CSSPropertyOperations.setValueForStyles(
|
||||
node,
|
||||
createReactStyleObject(value),
|
||||
component._reactInternalInstance
|
||||
);
|
||||
case 'style': {
|
||||
const style = prefixInlineStyles(createReactDOMStyle(flattenStyle(value)));
|
||||
CSSPropertyOperations.setValueForStyles(node, style, component._reactInternalInstance);
|
||||
break;
|
||||
}
|
||||
case 'class':
|
||||
case 'className': {
|
||||
const nativeProp = 'class';
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
exports[`components/ActivityIndicator default render 1`] = `
|
||||
<div
|
||||
aria-valuemax="1"
|
||||
aria-valuemin="0"
|
||||
className="
|
||||
rn-alignItems:center
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-justifyContent:center
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
role="progressbar"
|
||||
style={Object {}}>
|
||||
<div
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-animationDuration:0.75s
|
||||
rn-animationIterationCount:infinite
|
||||
rn-animationName:rn-ActivityIndicator-animation
|
||||
rn-animationTimingFunction:linear
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-height:20px
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none
|
||||
rn-width:20px"
|
||||
style={Object {}}>
|
||||
<svg
|
||||
height="100%"
|
||||
viewBox="0 0 32 32"
|
||||
width="100%">
|
||||
<circle
|
||||
cx="16"
|
||||
cy="16"
|
||||
fill="none"
|
||||
r="14"
|
||||
strokeWidth="4"
|
||||
style={
|
||||
Object {
|
||||
"opacity": 0.2,
|
||||
"stroke": "#1976D2",
|
||||
}
|
||||
} />
|
||||
<circle
|
||||
cx="16"
|
||||
cy="16"
|
||||
fill="none"
|
||||
r="14"
|
||||
strokeWidth="4"
|
||||
style={
|
||||
Object {
|
||||
"stroke": "#1976D2",
|
||||
"strokeDasharray": 80,
|
||||
"strokeDashoffset": 60,
|
||||
}
|
||||
} />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/ActivityIndicator other render 1`] = `
|
||||
<div
|
||||
aria-valuemax="1"
|
||||
aria-valuemin="0"
|
||||
className="
|
||||
rn-alignItems:center
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-justifyContent:center
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
role="progressbar"
|
||||
style={Object {}}>
|
||||
<div
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-animationDuration:0.75s
|
||||
rn-animationIterationCount:infinite
|
||||
rn-animationName:rn-ActivityIndicator-animation
|
||||
rn-animationPlayState:paused
|
||||
rn-animationTimingFunction:linear
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-height:36px
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none
|
||||
rn-width:36px"
|
||||
style={Object {}}>
|
||||
<svg
|
||||
height="100%"
|
||||
viewBox="0 0 32 32"
|
||||
width="100%">
|
||||
<circle
|
||||
cx="16"
|
||||
cy="16"
|
||||
fill="none"
|
||||
r="14"
|
||||
strokeWidth="4"
|
||||
style={
|
||||
Object {
|
||||
"opacity": 0.2,
|
||||
"stroke": "#1976D2",
|
||||
}
|
||||
} />
|
||||
<circle
|
||||
cx="16"
|
||||
cy="16"
|
||||
fill="none"
|
||||
r="14"
|
||||
strokeWidth="4"
|
||||
style={
|
||||
Object {
|
||||
"stroke": "#1976D2",
|
||||
"strokeDasharray": 80,
|
||||
"strokeDashoffset": 60,
|
||||
}
|
||||
} />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,5 +1,17 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import ActivityIndicator from '..';
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
describe('components/ActivityIndicator', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
test('default render', () => {
|
||||
const component = renderer.create(<ActivityIndicator />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('other render', () => {
|
||||
const component = renderer.create(<ActivityIndicator animating={false} hidesWhenStopped={false} size='large' />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import Animated from '../../apis/Animated';
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import Easing from 'animated/lib/Easing';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const rotationInterpolation = { inputRange: [ 0, 1 ], outputRange: [ '0deg', '360deg' ] };
|
||||
|
||||
class ActivityIndicator extends Component {
|
||||
static displayName = 'ActivityIndicator';
|
||||
|
||||
@@ -25,21 +21,6 @@ class ActivityIndicator extends Component {
|
||||
size: 'small'
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
animation: new Animated.Value(0)
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._manageAnimation();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._manageAnimation();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
animating,
|
||||
@@ -50,8 +31,6 @@ class ActivityIndicator extends Component {
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
const { animation } = this.state;
|
||||
|
||||
const svg = (
|
||||
<svg height='100%' viewBox='0 0 32 32' width='100%'>
|
||||
<circle
|
||||
@@ -88,47 +67,21 @@ class ActivityIndicator extends Component {
|
||||
style={[
|
||||
styles.container,
|
||||
style,
|
||||
size && { height: size, width: size }
|
||||
typeof size === 'number' && { height: size, width: size }
|
||||
]}
|
||||
>
|
||||
<Animated.View
|
||||
<View
|
||||
children={svg}
|
||||
style={[
|
||||
indicatorStyles[size],
|
||||
hidesWhenStopped && !animating && styles.hidesWhenStopped,
|
||||
{
|
||||
transform: [
|
||||
{ rotate: animation.interpolate(rotationInterpolation) }
|
||||
]
|
||||
}
|
||||
indicatorSizes[size],
|
||||
styles.animation,
|
||||
!animating && styles.animationPause,
|
||||
!animating && hidesWhenStopped && styles.hidesWhenStopped
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_manageAnimation() {
|
||||
const { animation } = this.state;
|
||||
|
||||
const cycleAnimation = () => {
|
||||
animation.setValue(0);
|
||||
Animated.timing(animation, {
|
||||
duration: 750,
|
||||
easing: Easing.inOut(Easing.linear),
|
||||
toValue: 1
|
||||
}).start((event) => {
|
||||
if (event.finished) {
|
||||
cycleAnimation();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (this.props.animating) {
|
||||
cycleAnimation();
|
||||
} else {
|
||||
animation.stopAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -138,10 +91,19 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
hidesWhenStopped: {
|
||||
visibility: 'hidden'
|
||||
},
|
||||
animation: {
|
||||
animationDuration: '0.75s',
|
||||
animationName: 'rn-ActivityIndicator-animation',
|
||||
animationTimingFunction: 'linear',
|
||||
animationIterationCount: 'infinite'
|
||||
},
|
||||
animationPause: {
|
||||
animationPlayState: 'paused'
|
||||
}
|
||||
});
|
||||
|
||||
const indicatorStyles = StyleSheet.create({
|
||||
const indicatorSizes = StyleSheet.create({
|
||||
small: {
|
||||
width: 20,
|
||||
height: 20
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
/* global window */
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import ImageResizeMode from './ImageResizeMode';
|
||||
import ImageLoader from '../../modules/ImageLoader';
|
||||
import ImageStylePropTypes from './ImageStylePropTypes';
|
||||
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
@@ -8,6 +9,8 @@ import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
const STATUS_ERRORED = 'ERRORED';
|
||||
const STATUS_LOADED = 'LOADED';
|
||||
const STATUS_LOADING = 'LOADING';
|
||||
@@ -52,14 +55,22 @@ class Image extends Component {
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
style: {}
|
||||
style: emptyObject
|
||||
};
|
||||
|
||||
static getSize(uri, success, failure) {
|
||||
ImageLoader.getSize(uri, success, failure);
|
||||
}
|
||||
|
||||
static prefetch(uri) {
|
||||
return ImageLoader.prefetch(uri);
|
||||
}
|
||||
|
||||
static resizeMode = ImageResizeMode;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = { isLoaded: false };
|
||||
this.state = { shouldDisplaySource: false };
|
||||
const uri = resolveAssetSource(props.source);
|
||||
this._imageState = uri ? STATUS_PENDING : STATUS_IDLE;
|
||||
this._isMounted = false;
|
||||
@@ -73,7 +84,7 @@ class Image extends Component {
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this._imageState === STATUS_PENDING && !this.image) {
|
||||
if (this._imageState === STATUS_PENDING) {
|
||||
this._createImageLoader();
|
||||
}
|
||||
}
|
||||
@@ -91,7 +102,7 @@ class Image extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isLoaded } = this.state;
|
||||
const { shouldDisplaySource } = this.state;
|
||||
const {
|
||||
accessibilityLabel,
|
||||
accessible,
|
||||
@@ -101,13 +112,17 @@ class Image extends Component {
|
||||
source,
|
||||
testID,
|
||||
/* eslint-disable */
|
||||
onError,
|
||||
onLoad,
|
||||
onLoadEnd,
|
||||
onLoadStart,
|
||||
resizeMode,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source);
|
||||
const imageSizeStyle = resolveAssetDimensions(!isLoaded ? defaultSource : source);
|
||||
const displayImage = resolveAssetSource(shouldDisplaySource ? source : defaultSource);
|
||||
const imageSizeStyle = resolveAssetDimensions(shouldDisplaySource ? source : defaultSource);
|
||||
const backgroundImage = displayImage ? `url("${displayImage}")` : null;
|
||||
const originalStyle = StyleSheet.flatten(this.props.style);
|
||||
const finalResizeMode = resizeMode || originalStyle.resizeMode || ImageResizeMode.cover;
|
||||
@@ -116,8 +131,8 @@ class Image extends Component {
|
||||
styles.initial,
|
||||
imageSizeStyle,
|
||||
originalStyle,
|
||||
backgroundImage && { backgroundImage },
|
||||
resizeModeStyles[finalResizeMode]
|
||||
resizeModeStyles[finalResizeMode],
|
||||
backgroundImage && { backgroundImage }
|
||||
]);
|
||||
// View doesn't support 'resizeMode' as a style
|
||||
delete style.resizeMode;
|
||||
@@ -137,28 +152,21 @@ class Image extends Component {
|
||||
}
|
||||
|
||||
_createImageLoader() {
|
||||
const uri = resolveAssetSource(this.props.source);
|
||||
|
||||
this._destroyImageLoader();
|
||||
this.image = new window.Image();
|
||||
this.image.onerror = this._onError;
|
||||
this.image.onload = this._onLoad;
|
||||
this.image.src = uri;
|
||||
const uri = resolveAssetSource(this.props.source);
|
||||
this._imageRequestId = ImageLoader.load(uri, this._onLoad, this._onError);
|
||||
this._onLoadStart();
|
||||
}
|
||||
|
||||
_destroyImageLoader() {
|
||||
if (this.image) {
|
||||
this.image.onerror = null;
|
||||
this.image.onload = null;
|
||||
this.image = null;
|
||||
if (this._imageRequestId) {
|
||||
ImageLoader.abort(this._imageRequestId);
|
||||
this._imageRequestId = null;
|
||||
}
|
||||
}
|
||||
|
||||
_onError = () => {
|
||||
const { onError, source } = this.props;
|
||||
this._destroyImageLoader();
|
||||
this._onLoadEnd();
|
||||
this._updateImageState(STATUS_ERRORED);
|
||||
if (onError) {
|
||||
onError({
|
||||
@@ -167,13 +175,13 @@ class Image extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
this._onLoadEnd();
|
||||
}
|
||||
|
||||
_onLoad = (e) => {
|
||||
const { onLoad } = this.props;
|
||||
const event = { nativeEvent: e };
|
||||
|
||||
this._destroyImageLoader();
|
||||
this._updateImageState(STATUS_LOADED);
|
||||
if (onLoad) { onLoad(event); }
|
||||
this._onLoadEnd();
|
||||
@@ -192,11 +200,12 @@ class Image extends Component {
|
||||
|
||||
_updateImageState(status) {
|
||||
this._imageState = status;
|
||||
const isLoaded = this._imageState === STATUS_LOADED;
|
||||
if (isLoaded !== this.state.isLoaded) {
|
||||
const shouldDisplaySource = this._imageState === STATUS_LOADED || this._imageState === STATUS_LOADING;
|
||||
// only triggers a re-render when the image is loading (to support PJEG), loaded, or failed
|
||||
if (shouldDisplaySource !== this.state.shouldDisplaySource) {
|
||||
requestAnimationFrame(() => {
|
||||
if (this._isMounted) {
|
||||
this.setState({ isLoaded });
|
||||
this.setState({ shouldDisplaySource });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,18 +2,27 @@ import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import ListViewDataSource from './ListViewDataSource';
|
||||
import ListViewPropTypes from './ListViewPropTypes';
|
||||
import ScrollView from '../ScrollView';
|
||||
import View from '../View';
|
||||
import React, { Component } from 'react';
|
||||
import StaticRenderer from '../StaticRenderer';
|
||||
import React, { Component, isEmpty, merge } from 'react';
|
||||
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 1;
|
||||
const DEFAULT_INITIAL_ROWS = 10;
|
||||
const DEFAULT_SCROLL_RENDER_AHEAD = 1000;
|
||||
const DEFAULT_END_REACHED_THRESHOLD = 1000;
|
||||
const DEFAULT_SCROLL_CALLBACK_THROTTLE = 50;
|
||||
|
||||
class ListView extends Component {
|
||||
static propTypes = ListViewPropTypes;
|
||||
|
||||
static defaultProps = {
|
||||
initialListSize: 10,
|
||||
pageSize: 1,
|
||||
initialListSize: DEFAULT_INITIAL_ROWS,
|
||||
pageSize: DEFAULT_PAGE_SIZE,
|
||||
renderScrollComponent: (props) => <ScrollView {...props} />,
|
||||
scrollRenderAheadDistance: 1000,
|
||||
onEndReachedThreshold: 1000,
|
||||
scrollRenderAheadDistance: DEFAULT_SCROLL_RENDER_AHEAD,
|
||||
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD,
|
||||
scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE,
|
||||
removeClippedSubviews: true,
|
||||
stickyHeaderIndices: []
|
||||
};
|
||||
|
||||
@@ -26,6 +35,48 @@ class ListView extends Component {
|
||||
highlightedRow: {}
|
||||
};
|
||||
this.onRowHighlighted = (sectionId, rowId) => this._onRowHighlighted(sectionId, rowId);
|
||||
this.scrollProperties = {};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
// this data should never trigger a render pass, so don't put in state
|
||||
this.scrollProperties = {
|
||||
visibleLength: null,
|
||||
contentLength: null,
|
||||
offset: 0
|
||||
};
|
||||
this._childFrames = [];
|
||||
this._visibleRows = {};
|
||||
this._prevRenderedRowsCount = 0;
|
||||
this._sentEndForContentLength = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// do this in animation frame until componentDidMount actually runs after
|
||||
// the component is laid out
|
||||
requestAnimationFrame(() => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Object) {
|
||||
if (this.props.dataSource !== nextProps.dataSource || this.props.initialListSize !== nextProps.initialListSize) {
|
||||
this.setState((state, props) => {
|
||||
this._prevRenderedRowsCount = 0;
|
||||
return {
|
||||
curRenderedRowsCount: Math.min(
|
||||
Math.max(state.curRenderedRowsCount, props.initialListSize),
|
||||
props.enableEmptySections ? props.dataSource.getRowAndSectionCount() : props.dataSource.getRowCount()
|
||||
)
|
||||
};
|
||||
}, () => this._renderMoreRowsIfNeeded());
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
requestAnimationFrame(() => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
});
|
||||
}
|
||||
|
||||
getScrollResponder() {
|
||||
@@ -40,58 +91,313 @@ class ListView extends Component {
|
||||
return this._scrollViewRef && this._scrollViewRef.setNativeProps(props);
|
||||
}
|
||||
|
||||
_onRowHighlighted(sectionId, rowId) {
|
||||
_onRowHighlighted = (sectionId, rowId) => {
|
||||
this.setState({ highlightedRow: { sectionId, rowId } });
|
||||
}
|
||||
|
||||
renderSectionHeaderFn = (data, sectionID) => {
|
||||
return () => this.props.renderSectionHeader(data, sectionID);
|
||||
}
|
||||
|
||||
renderRowFn = (data, sectionID, rowID) => {
|
||||
return () => this.props.renderRow(data, sectionID, rowID, this._onRowHighlighted);
|
||||
}
|
||||
|
||||
render() {
|
||||
const dataSource = this.props.dataSource;
|
||||
const header = this.props.renderHeader ? this.props.renderHeader() : undefined;
|
||||
const footer = this.props.renderFooter ? this.props.renderFooter() : undefined;
|
||||
|
||||
// render sections and rows
|
||||
const children = [];
|
||||
const sections = dataSource.rowIdentities;
|
||||
const renderRow = this.props.renderRow;
|
||||
const renderSectionHeader = this.props.renderSectionHeader;
|
||||
const renderSeparator = this.props.renderSeparator;
|
||||
for (let sectionIdx = 0, sectionCnt = sections.length; sectionIdx < sectionCnt; sectionIdx++) {
|
||||
const rows = sections[sectionIdx];
|
||||
const sectionId = dataSource.sectionIdentities[sectionIdx];
|
||||
|
||||
// render optional section header
|
||||
if (renderSectionHeader) {
|
||||
const section = dataSource.getSectionHeaderData(sectionIdx);
|
||||
const key = `s_${sectionId}`;
|
||||
const child = <View key={key}>{renderSectionHeader(section, sectionId)}</View>;
|
||||
children.push(child);
|
||||
const dataSource = this.props.dataSource;
|
||||
const allRowIDs = dataSource.rowIdentities;
|
||||
let rowCount = 0;
|
||||
const sectionHeaderIndices = [];
|
||||
|
||||
const header = this.props.renderHeader && this.props.renderHeader();
|
||||
const footer = this.props.renderFooter && this.props.renderFooter();
|
||||
let totalIndex = header ? 1 : 0;
|
||||
|
||||
for (let sectionIdx = 0; sectionIdx < allRowIDs.length; sectionIdx++) {
|
||||
const sectionID = dataSource.sectionIdentities[sectionIdx];
|
||||
const rowIDs = allRowIDs[sectionIdx];
|
||||
if (rowIDs.length === 0) {
|
||||
if (this.props.enableEmptySections === undefined) {
|
||||
const warning = require('fbjs/lib/warning');
|
||||
warning(false, 'In next release empty section headers will be rendered.' +
|
||||
' In this release you can use \'enableEmptySections\' flag to render empty section headers.');
|
||||
continue;
|
||||
} else {
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
invariant(
|
||||
this.props.enableEmptySections,
|
||||
'In next release \'enableEmptySections\' flag will be deprecated,' +
|
||||
' empty section headers will always be rendered. If empty section headers' +
|
||||
' are not desirable their indices should be excluded from sectionIDs object.' +
|
||||
' In this release \'enableEmptySections\' may only have value \'true\'' +
|
||||
' to allow empty section headers rendering.');
|
||||
}
|
||||
}
|
||||
|
||||
// render rows
|
||||
for (let rowIdx = 0, rowCnt = rows.length; rowIdx < rowCnt; rowIdx++) {
|
||||
const rowId = rows[rowIdx];
|
||||
const row = dataSource.getRowData(sectionIdx, rowIdx);
|
||||
const key = `r_${sectionId}_${rowId}`;
|
||||
const child = <View key={key}>{renderRow(row, sectionId, rowId, this.onRowHighlighted)}</View>;
|
||||
children.push(child);
|
||||
if (this.props.renderSectionHeader) {
|
||||
const shouldUpdateHeader = rowCount >= this._prevRenderedRowsCount &&
|
||||
dataSource.sectionHeaderShouldUpdate(sectionIdx);
|
||||
children.push(
|
||||
<StaticRenderer
|
||||
key={`s_${sectionID}`}
|
||||
render={this.renderSectionHeaderFn(
|
||||
dataSource.getSectionHeaderData(sectionIdx),
|
||||
sectionID
|
||||
)}
|
||||
shouldUpdate={!!shouldUpdateHeader}
|
||||
/>
|
||||
);
|
||||
sectionHeaderIndices.push(totalIndex++);
|
||||
}
|
||||
|
||||
// render optional separator
|
||||
if (renderSeparator && ((rowIdx !== rows.length - 1) || (sectionIdx === sections.length - 1))) {
|
||||
for (let rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {
|
||||
const rowID = rowIDs[rowIdx];
|
||||
const comboID = `${sectionID}_${rowID}`;
|
||||
const shouldUpdateRow = rowCount >= this._prevRenderedRowsCount &&
|
||||
dataSource.rowShouldUpdate(sectionIdx, rowIdx);
|
||||
const row =
|
||||
<StaticRenderer
|
||||
key={`r_${comboID}`}
|
||||
render={this.renderRowFn(
|
||||
dataSource.getRowData(sectionIdx, rowIdx),
|
||||
sectionID,
|
||||
rowID
|
||||
)}
|
||||
shouldUpdate={!!shouldUpdateRow}
|
||||
/>;
|
||||
children.push(row);
|
||||
totalIndex++;
|
||||
|
||||
if (this.props.renderSeparator &&
|
||||
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)) {
|
||||
const adjacentRowHighlighted =
|
||||
this.state.highlightedRow.sectionID === sectionId && (
|
||||
this.state.highlightedRow.rowID === rowId ||
|
||||
this.state.highlightedRow.rowID === rows[rowIdx + 1]);
|
||||
const separator = renderSeparator(sectionId, rowId, adjacentRowHighlighted);
|
||||
children.push(separator);
|
||||
this.state.highlightedRow.sectionID === sectionID && (
|
||||
this.state.highlightedRow.rowID === rowID ||
|
||||
this.state.highlightedRow.rowID === rowIDs[rowIdx + 1]
|
||||
);
|
||||
const separator = this.props.renderSeparator(
|
||||
sectionID,
|
||||
rowID,
|
||||
adjacentRowHighlighted
|
||||
);
|
||||
if (separator) {
|
||||
children.push(separator);
|
||||
totalIndex++;
|
||||
}
|
||||
}
|
||||
if (++rowCount === this.state.curRenderedRowsCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rowCount >= this.state.curRenderedRowsCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return React.cloneElement(this.props.renderScrollComponent(this.props), {
|
||||
ref: this._setScrollViewRef
|
||||
const {
|
||||
renderScrollComponent,
|
||||
...props
|
||||
} = this.props;
|
||||
Object.assign(props, {
|
||||
onScroll: this._onScroll,
|
||||
stickyHeaderIndices: this.props.stickyHeaderIndices.concat(sectionHeaderIndices),
|
||||
|
||||
// Do not pass these events downstream to ScrollView since they will be
|
||||
// registered in ListView's own ScrollResponder.Mixin
|
||||
onKeyboardWillShow: undefined,
|
||||
onKeyboardWillHide: undefined,
|
||||
onKeyboardDidShow: undefined,
|
||||
onKeyboardDidHide: undefined
|
||||
});
|
||||
|
||||
return React.cloneElement(renderScrollComponent(props), {
|
||||
ref: this._setScrollViewRef,
|
||||
onContentSizeChange: this._onContentSizeChange,
|
||||
onLayout: this._onLayout
|
||||
}, header, children, footer);
|
||||
}
|
||||
|
||||
_measureAndUpdateScrollProps() {
|
||||
const scrollComponent = this.getScrollResponder();
|
||||
if (!scrollComponent || !scrollComponent.getInnerViewNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateVisibleRows();
|
||||
}
|
||||
|
||||
_onLayout = (event: Object) => {
|
||||
const { width, height } = event.nativeEvent.layout;
|
||||
const visibleLength = !this.props.horizontal ? height : width;
|
||||
if (visibleLength !== this.scrollProperties.visibleLength) {
|
||||
this.scrollProperties.visibleLength = visibleLength;
|
||||
this._updateVisibleRows();
|
||||
this._renderMoreRowsIfNeeded();
|
||||
}
|
||||
this.props.onLayout && this.props.onLayout(event);
|
||||
}
|
||||
|
||||
_updateVisibleRows(updatedFrames?: Array<Object>) {
|
||||
if (!this.props.onChangeVisibleRows) {
|
||||
return; // No need to compute visible rows if there is no callback
|
||||
}
|
||||
if (updatedFrames) {
|
||||
updatedFrames.forEach((newFrame) => {
|
||||
this._childFrames[newFrame.index] = merge(newFrame);
|
||||
});
|
||||
}
|
||||
const isVertical = !this.props.horizontal;
|
||||
const dataSource = this.props.dataSource;
|
||||
const visibleMin = this.scrollProperties.offset;
|
||||
const visibleMax = visibleMin + this.scrollProperties.visibleLength;
|
||||
const allRowIDs = dataSource.rowIdentities;
|
||||
|
||||
const header = this.props.renderHeader && this.props.renderHeader();
|
||||
let totalIndex = header ? 1 : 0;
|
||||
let visibilityChanged = false;
|
||||
const changedRows = {};
|
||||
for (let sectionIdx = 0; sectionIdx < allRowIDs.length; sectionIdx++) {
|
||||
const rowIDs = allRowIDs[sectionIdx];
|
||||
if (rowIDs.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const sectionID = dataSource.sectionIdentities[sectionIdx];
|
||||
if (this.props.renderSectionHeader) {
|
||||
totalIndex++;
|
||||
}
|
||||
let visibleSection = this._visibleRows[sectionID];
|
||||
if (!visibleSection) {
|
||||
visibleSection = {};
|
||||
}
|
||||
for (let rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {
|
||||
const rowID = rowIDs[rowIdx];
|
||||
const frame = this._childFrames[totalIndex];
|
||||
totalIndex++;
|
||||
if (this.props.renderSeparator &&
|
||||
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)) {
|
||||
totalIndex++;
|
||||
}
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
const rowVisible = visibleSection[rowID];
|
||||
const min = isVertical ? frame.y : frame.x;
|
||||
const max = min + (isVertical ? frame.height : frame.width);
|
||||
if ((!min && !max) || (min === max)) {
|
||||
break;
|
||||
}
|
||||
if (min > visibleMax || max < visibleMin) {
|
||||
if (rowVisible) {
|
||||
visibilityChanged = true;
|
||||
delete visibleSection[rowID];
|
||||
if (!changedRows[sectionID]) {
|
||||
changedRows[sectionID] = {};
|
||||
}
|
||||
changedRows[sectionID][rowID] = false;
|
||||
}
|
||||
} else if (!rowVisible) {
|
||||
visibilityChanged = true;
|
||||
visibleSection[rowID] = true;
|
||||
if (!changedRows[sectionID]) {
|
||||
changedRows[sectionID] = {};
|
||||
}
|
||||
changedRows[sectionID][rowID] = true;
|
||||
}
|
||||
}
|
||||
if (!isEmpty(visibleSection)) {
|
||||
this._visibleRows[sectionID] = visibleSection;
|
||||
} else if (this._visibleRows[sectionID]) {
|
||||
delete this._visibleRows[sectionID];
|
||||
}
|
||||
}
|
||||
visibilityChanged && this.props.onChangeVisibleRows(this._visibleRows, changedRows);
|
||||
}
|
||||
|
||||
_onContentSizeChange = (width: number, height: number) => {
|
||||
const contentLength = !this.props.horizontal ? height : width;
|
||||
if (contentLength !== this.scrollProperties.contentLength) {
|
||||
this.scrollProperties.contentLength = contentLength;
|
||||
this._updateVisibleRows();
|
||||
this._renderMoreRowsIfNeeded();
|
||||
}
|
||||
this.props.onContentSizeChange && this.props.onContentSizeChange(width, height);
|
||||
}
|
||||
|
||||
_getDistanceFromEnd(scrollProperties: Object) {
|
||||
return scrollProperties.contentLength - scrollProperties.visibleLength - scrollProperties.offset;
|
||||
}
|
||||
|
||||
_maybeCallOnEndReached(event?: Object) {
|
||||
if (this.props.onEndReached &&
|
||||
this.scrollProperties.contentLength !== this._sentEndForContentLength &&
|
||||
this._getDistanceFromEnd(this.scrollProperties) < this.props.onEndReachedThreshold &&
|
||||
this.state.curRenderedRowsCount === (this.props.enableEmptySections ?
|
||||
this.props.dataSource.getRowAndSectionCount() : this.props.dataSource.getRowCount())) {
|
||||
this._sentEndForContentLength = this.scrollProperties.contentLength;
|
||||
this.props.onEndReached(event);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_renderMoreRowsIfNeeded() {
|
||||
if (this.scrollProperties.contentLength === null ||
|
||||
this.scrollProperties.visibleLength === null ||
|
||||
this.state.curRenderedRowsCount === (this.props.enableEmptySections ?
|
||||
this.props.dataSource.getRowAndSectionCount() : this.props.dataSource.getRowCount())) {
|
||||
this._maybeCallOnEndReached();
|
||||
return;
|
||||
}
|
||||
|
||||
const distanceFromEnd = this._getDistanceFromEnd(this.scrollProperties);
|
||||
if (distanceFromEnd < this.props.scrollRenderAheadDistance) {
|
||||
this._pageInNewRows();
|
||||
}
|
||||
}
|
||||
|
||||
_pageInNewRows() {
|
||||
this.setState((state, props) => {
|
||||
const rowsToRender = Math.min(
|
||||
state.curRenderedRowsCount + props.pageSize,
|
||||
(props.enableEmptySections ? props.dataSource.getRowAndSectionCount() : props.dataSource.getRowCount())
|
||||
);
|
||||
this._prevRenderedRowsCount = state.curRenderedRowsCount;
|
||||
return {
|
||||
curRenderedRowsCount: rowsToRender
|
||||
};
|
||||
}, () => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
this._prevRenderedRowsCount = this.state.curRenderedRowsCount;
|
||||
});
|
||||
}
|
||||
|
||||
_onScroll = (e: Object) => {
|
||||
const isVertical = !this.props.horizontal;
|
||||
this.scrollProperties.visibleLength = e.nativeEvent.layoutMeasurement[
|
||||
isVertical ? 'height' : 'width'
|
||||
];
|
||||
this.scrollProperties.contentLength = e.nativeEvent.contentSize[
|
||||
isVertical ? 'height' : 'width'
|
||||
];
|
||||
this.scrollProperties.offset = e.nativeEvent.contentOffset[
|
||||
isVertical ? 'y' : 'x'
|
||||
];
|
||||
this._updateVisibleRows(e.nativeEvent.updatedChildFrames);
|
||||
if (!this._maybeCallOnEndReached(e)) {
|
||||
this._renderMoreRowsIfNeeded();
|
||||
}
|
||||
|
||||
if (this.props.onEndReached &&
|
||||
this._getDistanceFromEnd(this.scrollProperties) > this.props.onEndReachedThreshold) {
|
||||
// Scrolled out of the end zone, so it should be able to trigger again.
|
||||
this._sentEndForContentLength = null;
|
||||
}
|
||||
|
||||
this.props.onScroll && this.props.onScroll(e);
|
||||
};
|
||||
|
||||
_setScrollViewRef = (component) => {
|
||||
this._scrollViewRef = component;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import Animated from '../../apis/Animated';
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import ColorPropType from '../../propTypes/ColorPropType';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const indeterminateWidth = '25%';
|
||||
const translateInterpolation = { inputRange: [ 0, 1 ], outputRange: [ '-100%', '400%' ] };
|
||||
|
||||
class ProgressBar extends Component {
|
||||
static displayName = 'ProgressBar';
|
||||
|
||||
@@ -26,19 +22,12 @@ class ProgressBar extends Component {
|
||||
trackColor: 'transparent'
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
animationTranslate: new Animated.Value(0)
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._manageAnimation();
|
||||
this._updateProgressWidth();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._manageAnimation();
|
||||
this._updateProgressWidth();
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -51,9 +40,7 @@ class ProgressBar extends Component {
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
const { animationTranslate } = this.state;
|
||||
|
||||
const percentageProgress = indeterminate ? 50 : progress * 100;
|
||||
const percentageProgress = progress * 100;
|
||||
|
||||
return (
|
||||
<View {...other}
|
||||
@@ -67,42 +54,29 @@ class ProgressBar extends Component {
|
||||
{ backgroundColor: trackColor }
|
||||
]}
|
||||
>
|
||||
<Animated.View style={[
|
||||
styles.progress,
|
||||
{ backgroundColor: color },
|
||||
indeterminate ? {
|
||||
transform: [
|
||||
{ translateX: animationTranslate.interpolate(translateInterpolation) }
|
||||
],
|
||||
width: indeterminateWidth
|
||||
} : {
|
||||
width: `${percentageProgress}%`
|
||||
}
|
||||
]} />
|
||||
<View
|
||||
ref={this._setProgressRef}
|
||||
style={[
|
||||
styles.progress,
|
||||
indeterminate && styles.animation,
|
||||
{ backgroundColor: color }
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_manageAnimation() {
|
||||
const { animationTranslate } = this.state;
|
||||
_setProgressRef = (component) => {
|
||||
this._progressRef = component;
|
||||
}
|
||||
|
||||
const cycleAnimation = (animation) => {
|
||||
animation.setValue(0);
|
||||
Animated.timing(animation, {
|
||||
duration: 1000,
|
||||
toValue: 1
|
||||
}).start((event) => {
|
||||
if (event.finished) {
|
||||
cycleAnimation(animation);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (this.props.indeterminate) {
|
||||
cycleAnimation(animationTranslate);
|
||||
} else {
|
||||
animationTranslate.stopAnimation();
|
||||
}
|
||||
_updateProgressWidth = () => {
|
||||
const { indeterminate, progress } = this.props;
|
||||
const percentageProgress = indeterminate ? 50 : progress * 100;
|
||||
const width = indeterminate ? '25%' : `${percentageProgress}%`;
|
||||
this._progressRef.setNativeProps({
|
||||
style: { width }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +88,12 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
progress: {
|
||||
height: '100%'
|
||||
},
|
||||
animation: {
|
||||
animationDuration: '1s',
|
||||
animationName: 'rn-ProgressBar-animation',
|
||||
animationTimingFunction: 'linear',
|
||||
animationIterationCount: 'infinite'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import View from '../View';
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
/* eslint-disable react/prefer-es6-class */
|
||||
const ScrollView = React.createClass({
|
||||
propTypes: {
|
||||
@@ -79,7 +81,7 @@ const ScrollView = React.createClass({
|
||||
if (typeof y === 'number') {
|
||||
console.warn('`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, animated: true})` instead.');
|
||||
} else {
|
||||
({ x, y, animated } = y || {});
|
||||
({ x, y, animated } = y || emptyObject);
|
||||
}
|
||||
|
||||
this.getScrollResponder().scrollResponderScrollTo({ x: x || 0, y: y || 0, animated: animated !== false });
|
||||
@@ -236,7 +238,6 @@ const styles = StyleSheet.create({
|
||||
overflowY: 'hidden'
|
||||
},
|
||||
contentContainer: {
|
||||
flexGrow: 1,
|
||||
transform: [ { translateZ: 0 } ]
|
||||
},
|
||||
contentContainerHorizontal: {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import UIManager from '../../apis/UIManager';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
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)`;
|
||||
|
||||
@@ -28,7 +29,7 @@ class Switch extends Component {
|
||||
activeThumbColor: '#009688',
|
||||
activeTrackColor: '#A3D3CF',
|
||||
disabled: false,
|
||||
style: {},
|
||||
style: emptyObject,
|
||||
thumbColor: '#FAFAFA',
|
||||
trackColor: '#939393',
|
||||
value: false
|
||||
@@ -90,7 +91,6 @@ class Switch extends Component {
|
||||
{
|
||||
backgroundColor: thumbCurrentColor,
|
||||
height: thumbHeight,
|
||||
transform: [ { translateX: value ? '100%' : '0%' } ],
|
||||
width: thumbWidth
|
||||
},
|
||||
disabled && styles.disabledThumb
|
||||
@@ -110,7 +110,16 @@ class Switch extends Component {
|
||||
return (
|
||||
<View {...other} style={rootStyle}>
|
||||
<View style={trackStyle} />
|
||||
<View ref={this._setThumbRef} style={thumbStyle} />
|
||||
<View
|
||||
ref={this._setThumbRef}
|
||||
style={[
|
||||
thumbStyle,
|
||||
value && styles.thumbOn,
|
||||
{
|
||||
marginLeft: value ? multiplyStyleLengthValue(thumbWidth, -1) : 0
|
||||
}
|
||||
]}
|
||||
/>
|
||||
{nativeControl}
|
||||
</View>
|
||||
);
|
||||
@@ -151,7 +160,7 @@ const styles = StyleSheet.create({
|
||||
...StyleSheet.absoluteFillObject,
|
||||
height: '70%',
|
||||
margin: 'auto',
|
||||
transition: '0.1s',
|
||||
transitionDuration: '0.1s',
|
||||
width: '90%'
|
||||
},
|
||||
disabledTrack: {
|
||||
@@ -161,7 +170,14 @@ const styles = StyleSheet.create({
|
||||
alignSelf: 'flex-start',
|
||||
borderRadius: '100%',
|
||||
boxShadow: thumbDefaultBoxShadow,
|
||||
transition: '0.1s'
|
||||
left: '0%',
|
||||
transform: [
|
||||
{ translateZ: 0 }
|
||||
],
|
||||
transitionDuration: '0.1s'
|
||||
},
|
||||
thumbOn: {
|
||||
left: '100%'
|
||||
},
|
||||
disabledThumb: {
|
||||
backgroundColor: '#BDBDBD'
|
||||
|
||||
@@ -1,26 +1,14 @@
|
||||
exports[`components/Text prop "children" 1`] = `
|
||||
<span
|
||||
className=""
|
||||
className="rn-borderTopWidth:0px rn-borderRightWidth:0px rn-borderBottomWidth:0px rn-borderLeftWidth:0px rn-color:inherit rn-display:inline rn-font:inherit rn-marginTop:0px rn-marginRight:0px rn-marginBottom:0px rn-marginLeft:0px rn-paddingTop:0px rn-paddingRight:0px rn-paddingBottom:0px rn-paddingLeft:0px rn-textDecoration:none rn-whiteSpace:pre-wrap rn-wordWrap:break-word"
|
||||
style={
|
||||
Object {
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopWidth": "0px",
|
||||
"color": "inherit",
|
||||
"display": "inline",
|
||||
"font": "inherit",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"textDecoration": "none",
|
||||
"wordWrap": "break-word",
|
||||
}
|
||||
Array [
|
||||
2,
|
||||
undefined,
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
]
|
||||
}>
|
||||
children
|
||||
</span>
|
||||
@@ -28,86 +16,67 @@ exports[`components/Text prop "children" 1`] = `
|
||||
|
||||
exports[`components/Text prop "onPress" 1`] = `
|
||||
<span
|
||||
className=""
|
||||
className="
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-color:inherit
|
||||
rn-cursor:pointer
|
||||
rn-display:inline
|
||||
rn-font:inherit
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-textDecoration:none
|
||||
rn-whiteSpace:pre-wrap
|
||||
rn-wordWrap:break-word"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopWidth": "0px",
|
||||
"color": "inherit",
|
||||
"cursor": "pointer",
|
||||
"display": "inline",
|
||||
"font": "inherit",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"textDecoration": "none",
|
||||
"wordWrap": "break-word",
|
||||
}
|
||||
}
|
||||
style={Object {}}
|
||||
tabIndex={0} />
|
||||
`;
|
||||
|
||||
exports[`components/Text prop "selectable" 1`] = `
|
||||
<span
|
||||
className=""
|
||||
className="rn-borderTopWidth:0px rn-borderRightWidth:0px rn-borderBottomWidth:0px rn-borderLeftWidth:0px rn-color:inherit rn-display:inline rn-font:inherit rn-marginTop:0px rn-marginRight:0px rn-marginBottom:0px rn-marginLeft:0px rn-paddingTop:0px rn-paddingRight:0px rn-paddingBottom:0px rn-paddingLeft:0px rn-textDecoration:none rn-whiteSpace:pre-wrap rn-wordWrap:break-word"
|
||||
style={
|
||||
Object {
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopWidth": "0px",
|
||||
"color": "inherit",
|
||||
"display": "inline",
|
||||
"font": "inherit",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"textDecoration": "none",
|
||||
"wordWrap": "break-word",
|
||||
}
|
||||
Array [
|
||||
2,
|
||||
undefined,
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
]
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Text prop "selectable" 2`] = `
|
||||
<span
|
||||
className=""
|
||||
style={
|
||||
Object {
|
||||
"MozUserSelect": "none",
|
||||
"WebkitUserSelect": "none",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopWidth": "0px",
|
||||
"color": "inherit",
|
||||
"display": "inline",
|
||||
"font": "inherit",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"msUserSelect": "none",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"textDecoration": "none",
|
||||
"userSelect": "none",
|
||||
"wordWrap": "break-word",
|
||||
}
|
||||
} />
|
||||
className="
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-color:inherit
|
||||
rn-display:inline
|
||||
rn-font:inherit
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-textDecoration:none
|
||||
rn-userSelect:none
|
||||
rn-whiteSpace:pre-wrap
|
||||
rn-wordWrap:break-word"
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
@@ -40,25 +40,24 @@ class Text extends Component {
|
||||
onLayout,
|
||||
suppressHighlighting,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
if (onPress) {
|
||||
other.onClick = onPress;
|
||||
other.onKeyDown = this._createEnterHandler(onPress);
|
||||
other.tabIndex = 0;
|
||||
otherProps.onClick = onPress;
|
||||
otherProps.onKeyDown = this._createEnterHandler(onPress);
|
||||
otherProps.tabIndex = 0;
|
||||
}
|
||||
|
||||
return createDOMElement('span', {
|
||||
...other,
|
||||
style: [
|
||||
styles.initial,
|
||||
style,
|
||||
!selectable && styles.notSelectable,
|
||||
numberOfLines === 1 && styles.singleLineStyle,
|
||||
onPress && styles.pressable
|
||||
]
|
||||
});
|
||||
otherProps.style = [
|
||||
styles.initial,
|
||||
style,
|
||||
!selectable && styles.notSelectable,
|
||||
numberOfLines === 1 && styles.singleLineStyle,
|
||||
onPress && styles.pressable
|
||||
];
|
||||
|
||||
return createDOMElement('span', otherProps);
|
||||
}
|
||||
|
||||
_createEnterHandler(fn) {
|
||||
@@ -79,6 +78,7 @@ const styles = StyleSheet.create({
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
textDecorationLine: 'none',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordWrap: 'break-word'
|
||||
},
|
||||
notSelectable: {
|
||||
|
||||
@@ -169,8 +169,6 @@ describe('components/TextInput', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('prop "onLayout"');
|
||||
|
||||
test('prop "onSelectionChange"', (done) => {
|
||||
const input = findNativeInput(mount(<TextInput defaultValue='12345' onSelectionChange={onSelectionChange} />));
|
||||
input.simulate('select', { target: { selectionStart: 0, selectionEnd: 3 } });
|
||||
@@ -181,6 +179,16 @@ describe('components/TextInput', () => {
|
||||
}
|
||||
});
|
||||
|
||||
describe('prop "onSubmitEditing"', () => {
|
||||
test('single-line input', (done) => {
|
||||
const input = findNativeInput(mount(<TextInput defaultValue='12345' onSubmitEditing={onSubmitEditing} />));
|
||||
input.simulate('keyPress', { which: 13 });
|
||||
function onSubmitEditing(e) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "secureTextEntry"', () => {
|
||||
let input = findNativeInput(shallow(<TextInput secureTextEntry />));
|
||||
expect(input.prop('type')).toEqual('password');
|
||||
|
||||
@@ -10,6 +10,8 @@ import UIManager from '../../apis/UIManager';
|
||||
import View from '../View';
|
||||
import { Component, PropTypes } from 'react';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
/**
|
||||
* React Native events differ from W3C events.
|
||||
*/
|
||||
@@ -72,6 +74,7 @@ class TextInput extends Component {
|
||||
onFocus: PropTypes.func,
|
||||
onKeyPress: PropTypes.func,
|
||||
onSelectionChange: PropTypes.func,
|
||||
onSubmitEditing: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
placeholderTextColor: PropTypes.string,
|
||||
secureTextEntry: PropTypes.bool,
|
||||
@@ -93,7 +96,7 @@ class TextInput extends Component {
|
||||
multiline: false,
|
||||
numberOfLines: 2,
|
||||
secureTextEntry: false,
|
||||
style: {}
|
||||
style: emptyObject
|
||||
};
|
||||
|
||||
blur() {
|
||||
@@ -152,7 +155,7 @@ class TextInput extends Component {
|
||||
selectionColor,
|
||||
selectTextOnFocus,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
let type;
|
||||
@@ -185,9 +188,9 @@ class TextInput extends Component {
|
||||
|
||||
const component = multiline ? TextareaAutosize : 'input';
|
||||
|
||||
const props = {
|
||||
...other,
|
||||
Object.assign(otherProps, {
|
||||
autoCorrect: autoCorrect ? 'on' : 'off',
|
||||
dir: 'auto',
|
||||
onBlur: normalizeEventHandler(this._handleBlur),
|
||||
onChange: normalizeEventHandler(this._handleChange),
|
||||
onFocus: normalizeEventHandler(this._handleFocus),
|
||||
@@ -199,16 +202,16 @@ class TextInput extends Component {
|
||||
styles.initial,
|
||||
style
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
if (multiline) {
|
||||
props.maxRows = maxNumberOfLines || numberOfLines;
|
||||
props.minRows = numberOfLines;
|
||||
otherProps.maxRows = maxNumberOfLines || numberOfLines;
|
||||
otherProps.minRows = numberOfLines;
|
||||
} else {
|
||||
props.type = type;
|
||||
otherProps.type = type;
|
||||
}
|
||||
|
||||
return createDOMElement(component, props);
|
||||
return createDOMElement(component, otherProps);
|
||||
}
|
||||
|
||||
_handleBlur = (e) => {
|
||||
@@ -243,7 +246,7 @@ class TextInput extends Component {
|
||||
}
|
||||
|
||||
_handleSelectionChange = (e) => {
|
||||
const { onSelectionChange, selection = {} } = this.props;
|
||||
const { onSelectionChange, selection = emptyObject } = this.props;
|
||||
if (onSelectionChange) {
|
||||
try {
|
||||
const node = e.target;
|
||||
@@ -270,7 +273,6 @@ const styles = StyleSheet.create({
|
||||
borderWidth: 0,
|
||||
boxSizing: 'border-box',
|
||||
color: 'inherit',
|
||||
flex: 1,
|
||||
font: 'inherit',
|
||||
padding: 0
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var PooledClass = require('react/lib/PooledClass');
|
||||
var PooledClass = require('react-dom/lib/PooledClass');
|
||||
|
||||
var twoArgumentPooler = PooledClass.twoArgumentPooler;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var PooledClass = require('react/lib/PooledClass');
|
||||
var PooledClass = require('react-dom/lib/PooledClass');
|
||||
|
||||
var twoArgumentPooler = PooledClass.twoArgumentPooler;
|
||||
|
||||
|
||||
@@ -404,6 +404,13 @@ var TouchableMixin = {
|
||||
*/
|
||||
touchableHandleResponderRelease: function(e) {
|
||||
this._receiveSignal(Signals.RESPONDER_RELEASE, e);
|
||||
// Browsers fire mouse events after touch events. This causes the
|
||||
// 'onResponderRelease' handler to be called twice for Touchables.
|
||||
// Auto-fix this issue by calling 'preventDefault' to cancel the mouse
|
||||
// events.
|
||||
if (e.cancelable && !e.isDefaultPrevented()) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -566,13 +573,13 @@ var TouchableMixin = {
|
||||
* @sideeffects
|
||||
* @private
|
||||
*/
|
||||
_remeasureMetricsOnActivation: function(e) {
|
||||
/* @edit begin */
|
||||
UIManager.measure(
|
||||
e.nativeEvent.target,
|
||||
this._handleQueryLayout
|
||||
);
|
||||
/* @edit end */
|
||||
_remeasureMetricsOnActivation: function() {
|
||||
const tag = this.state.touchable.responderID;
|
||||
if (tag == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIManager.measure(tag, this._handleQueryLayout);
|
||||
},
|
||||
|
||||
_handleQueryLayout: function(l, t, w, h, globalX, globalY) {
|
||||
@@ -684,7 +691,7 @@ var TouchableMixin = {
|
||||
}
|
||||
|
||||
if (!IsActive[curState] && IsActive[nextState]) {
|
||||
this._remeasureMetricsOnActivation(e);
|
||||
this._remeasureMetricsOnActivation();
|
||||
}
|
||||
|
||||
if (IsPressingIn[curState] && signal === Signals.LONG_PRESS_DETECTED) {
|
||||
@@ -692,16 +699,9 @@ var TouchableMixin = {
|
||||
}
|
||||
|
||||
if (newIsHighlight && !curIsHighlight) {
|
||||
this._savePressInLocation(e);
|
||||
this.touchableHandleActivePressIn && this.touchableHandleActivePressIn(e);
|
||||
} else if (!newIsHighlight && curIsHighlight && this.touchableHandleActivePressOut) {
|
||||
if (this.touchableGetPressOutDelayMS && this.touchableGetPressOutDelayMS()) {
|
||||
this.pressOutDelayTimeout = setTimeout(() => {
|
||||
this.touchableHandleActivePressOut(e);
|
||||
}, this.touchableGetPressOutDelayMS());
|
||||
} else {
|
||||
this.touchableHandleActivePressOut(e);
|
||||
}
|
||||
this._startHighlight(e);
|
||||
} else if (!newIsHighlight && curIsHighlight) {
|
||||
this._endHighlight(e);
|
||||
}
|
||||
|
||||
if (IsPressingIn[curState] && signal === Signals.RESPONDER_RELEASE) {
|
||||
@@ -714,49 +714,40 @@ var TouchableMixin = {
|
||||
|
||||
var shouldInvokePress = !IsLongPressingIn[curState] || pressIsLongButStillCallOnPress;
|
||||
if (shouldInvokePress && this.touchableHandlePress) {
|
||||
if (!newIsHighlight && !curIsHighlight) {
|
||||
// we never highlighted because of delay, but we should highlight now
|
||||
this._startHighlight(e);
|
||||
this._endHighlight(e);
|
||||
}
|
||||
this.touchableHandlePress(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout);
|
||||
this.touchableDelayTimeout = null;
|
||||
}
|
||||
},
|
||||
|
||||
_startHighlight: function(e) {
|
||||
this._savePressInLocation(e);
|
||||
this.touchableHandleActivePressIn && this.touchableHandleActivePressIn(e);
|
||||
},
|
||||
|
||||
_endHighlight: function(e) {
|
||||
if (this.touchableHandleActivePressOut) {
|
||||
if (this.touchableGetPressOutDelayMS && this.touchableGetPressOutDelayMS()) {
|
||||
this.pressOutDelayTimeout = setTimeout(() => {
|
||||
this.touchableHandleActivePressOut(e);
|
||||
}, this.touchableGetPressOutDelayMS());
|
||||
} else {
|
||||
this.touchableHandleActivePressOut(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
var Touchable = {
|
||||
Mixin: TouchableMixin,
|
||||
TOUCH_TARGET_DEBUG: false, // Highlights all touchable targets. Toggle with Inspector.
|
||||
/**
|
||||
* Renders a debugging overlay to visualize touch target with hitSlop (might not work on Android).
|
||||
*/
|
||||
renderDebugView: ({color, hitSlop}) => {
|
||||
if (!Touchable.TOUCH_TARGET_DEBUG) {
|
||||
return null;
|
||||
}
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
throw Error('Touchable.TOUCH_TARGET_DEBUG should not be enabled in prod!');
|
||||
}
|
||||
const debugHitSlopStyle = {};
|
||||
hitSlop = hitSlop || {top: 0, bottom: 0, left: 0, right: 0};
|
||||
for (const key in hitSlop) {
|
||||
debugHitSlopStyle[key] = -hitSlop[key];
|
||||
}
|
||||
const hexColor = '#' + ('00000000' + normalizeColor(color).toString(16)).substr(-8);
|
||||
return (
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
borderColor: hexColor.slice(0, -2) + '55', // More opaque
|
||||
borderWidth: 1,
|
||||
borderStyle: 'dashed',
|
||||
backgroundColor: hexColor.slice(0, -2) + '0F', // Less opaque
|
||||
...debugHitSlopStyle
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Mixin: TouchableMixin
|
||||
};
|
||||
|
||||
module.exports = Touchable;
|
||||
|
||||
@@ -33,7 +33,7 @@ type Event = Object;
|
||||
|
||||
var DEFAULT_PROPS = {
|
||||
accessibilityRole: 'button',
|
||||
activeOpacity: 0.8,
|
||||
activeOpacity: 0.85,
|
||||
underlayColor: 'black'
|
||||
};
|
||||
|
||||
@@ -55,13 +55,13 @@ var PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
* <TouchableHighlight onPress={this._onPressButton}>
|
||||
* <Image
|
||||
* style={styles.button}
|
||||
* source={require('image!myButton')}
|
||||
* source={require('./myButton')}
|
||||
* />
|
||||
* </TouchableHighlight>
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
* > **NOTE**: TouchableHighlight supports only one child
|
||||
* > **NOTE**: TouchableHighlight must have one child (not zero or more than one)
|
||||
* >
|
||||
* > If you wish to have several child components, wrap them in a View.
|
||||
*/
|
||||
|
||||
@@ -43,7 +43,7 @@ var PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
* <TouchableOpacity onPress={this._onPressButton}>
|
||||
* <Image
|
||||
* style={styles.button}
|
||||
* source={require('image!myButton')}
|
||||
* source={require('./myButton')}
|
||||
* />
|
||||
* </TouchableOpacity>
|
||||
* );
|
||||
@@ -60,12 +60,14 @@ var TouchableOpacity = React.createClass({
|
||||
* active.
|
||||
*/
|
||||
activeOpacity: React.PropTypes.number,
|
||||
focusedOpacity: React.PropTypes.number
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
accessibilityRole: 'button',
|
||||
activeOpacity: 0.2,
|
||||
focusedOpacity: 0.7
|
||||
};
|
||||
},
|
||||
|
||||
@@ -81,10 +83,11 @@ var TouchableOpacity = React.createClass({
|
||||
ensurePositiveDelayProps(nextProps);
|
||||
},
|
||||
|
||||
setOpacityTo: function(value) {
|
||||
setOpacityTo: function(value: number, duration: number) {
|
||||
this.setNativeProps({
|
||||
style: {
|
||||
opacity: value
|
||||
opacity: value,
|
||||
transitionDuration: duration
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -94,26 +97,20 @@ var TouchableOpacity = React.createClass({
|
||||
* defined on your component.
|
||||
*/
|
||||
touchableHandleActivePressIn: function(e: Event) {
|
||||
this.clearTimeout(this._hideTimeout);
|
||||
this._hideTimeout = null;
|
||||
this._opacityActive();
|
||||
if (e.dispatchConfig.registrationName === 'onResponderGrant') {
|
||||
this._opacityActive(0);
|
||||
} else {
|
||||
this._opacityActive(150);
|
||||
}
|
||||
this.props.onPressIn && this.props.onPressIn(e);
|
||||
},
|
||||
|
||||
touchableHandleActivePressOut: function(e: Event) {
|
||||
if (!this._hideTimeout) {
|
||||
this._opacityInactive();
|
||||
}
|
||||
this._opacityInactive(250);
|
||||
this.props.onPressOut && this.props.onPressOut(e);
|
||||
},
|
||||
|
||||
touchableHandlePress: function(e: Event) {
|
||||
this.clearTimeout(this._hideTimeout);
|
||||
this._opacityActive();
|
||||
this._hideTimeout = this.setTimeout(
|
||||
this._opacityInactive,
|
||||
this.props.delayPressOut || 100
|
||||
);
|
||||
this.props.onPress && this.props.onPress(e);
|
||||
},
|
||||
|
||||
@@ -142,19 +139,22 @@ var TouchableOpacity = React.createClass({
|
||||
return this.props.delayPressOut;
|
||||
},
|
||||
|
||||
_opacityActive: function() {
|
||||
this.setOpacityTo(this.props.activeOpacity);
|
||||
_opacityActive: function(duration: number) {
|
||||
this.setOpacityTo(this.props.activeOpacity, duration);
|
||||
},
|
||||
|
||||
_opacityInactive: function() {
|
||||
this.clearTimeout(this._hideTimeout);
|
||||
this._hideTimeout = null;
|
||||
_opacityInactive: function(duration: number) {
|
||||
var childStyle = flattenStyle(this.props.style) || {};
|
||||
this.setOpacityTo(
|
||||
childStyle.opacity === undefined ? 1 : childStyle.opacity
|
||||
childStyle.opacity === undefined ? 1 : childStyle.opacity,
|
||||
duration
|
||||
);
|
||||
},
|
||||
|
||||
_opacityFocused: function() {
|
||||
this.setOpacityTo(this.props.focusedOpacity);
|
||||
},
|
||||
|
||||
_onKeyEnter(e, callback) {
|
||||
var ENTER = 13
|
||||
if (e.keyCode === ENTER) {
|
||||
@@ -197,7 +197,8 @@ var TouchableOpacity = React.createClass({
|
||||
var styles = StyleSheet.create({
|
||||
root: {
|
||||
cursor: 'pointer',
|
||||
transition: 'opacity 0.15s',
|
||||
transitionProperty: 'opacity',
|
||||
transitionDuration: '0.15s',
|
||||
userSelect: 'none'
|
||||
},
|
||||
disabled: {
|
||||
|
||||
@@ -22,12 +22,12 @@ var StyleSheet = require('../../apis/StyleSheet');
|
||||
|
||||
type Event = Object;
|
||||
|
||||
var PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
|
||||
/**
|
||||
* Do not use unless you have a very good reason. All the elements that
|
||||
* respond to press should have a visual feedback when touched. This is
|
||||
* one of the primary reason a "web" app doesn't feel "native".
|
||||
* one of the primary reasons a "web" app doesn't feel "native".
|
||||
*
|
||||
* > **NOTE**: TouchableWithoutFeedback supports only one child
|
||||
* >
|
||||
@@ -144,7 +144,7 @@ const TouchableWithoutFeedback = React.createClass({
|
||||
return this.props.delayPressOut || 0;
|
||||
},
|
||||
|
||||
render: function(): ReactElement<any> {
|
||||
render: function(): React.Element<any> {
|
||||
// Note(avik): remove dynamic typecast once Flow has been upgraded
|
||||
const child = React.Children.only(this.props.children);
|
||||
let children = child.props.children;
|
||||
@@ -153,12 +153,6 @@ const TouchableWithoutFeedback = React.createClass({
|
||||
'TouchableWithoutFeedback does not work well with Text children. Wrap children in a View instead. See ' +
|
||||
((child._owner && child._owner.getName && child._owner.getName()) || '<unknown>')
|
||||
);
|
||||
if (Touchable.TOUCH_TARGET_DEBUG && child.type && child.type.displayName === 'View') {
|
||||
if (!Array.isArray(children)) {
|
||||
children = [children];
|
||||
}
|
||||
children.push(Touchable.renderDebugView({color: 'red', hitSlop: this.props.hitSlop}));
|
||||
}
|
||||
const style = (Touchable.TOUCH_TARGET_DEBUG && child.type && child.type.displayName === 'Text') ?
|
||||
[
|
||||
styles.root,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import AnimationPropTypes from '../../propTypes/AnimationPropTypes';
|
||||
import BorderPropTypes from '../../propTypes/BorderPropTypes';
|
||||
import ColorPropType from '../../propTypes/ColorPropType';
|
||||
import LayoutPropTypes from '../../propTypes/LayoutPropTypes';
|
||||
import { PropTypes } from 'react';
|
||||
import ShadowPropTypes from '../../propTypes/ShadowPropTypes';
|
||||
import TransformPropTypes from '../../propTypes/TransformPropTypes';
|
||||
|
||||
const { number, oneOf, string } = PropTypes;
|
||||
@@ -9,8 +11,10 @@ const autoOrHiddenOrVisible = oneOf([ 'auto', 'hidden', 'visible' ]);
|
||||
const hiddenOrVisible = oneOf([ 'hidden', 'visible' ]);
|
||||
|
||||
module.exports = process.env.NODE_ENV !== 'production' ? {
|
||||
...AnimationPropTypes,
|
||||
...BorderPropTypes,
|
||||
...LayoutPropTypes,
|
||||
...ShadowPropTypes,
|
||||
...TransformPropTypes,
|
||||
backfaceVisibility: hiddenOrVisible,
|
||||
backgroundColor: ColorPropType,
|
||||
@@ -32,7 +36,10 @@ module.exports = process.env.NODE_ENV !== 'production' ? {
|
||||
outline: string,
|
||||
overflowX: autoOrHiddenOrVisible,
|
||||
overflowY: autoOrHiddenOrVisible,
|
||||
transition: string,
|
||||
transitionDelay: string,
|
||||
transitionDuration: string,
|
||||
transitionProperty: string,
|
||||
transitionTimingFunction: string,
|
||||
userSelect: string,
|
||||
visibility: hiddenOrVisible,
|
||||
WebkitOverflowScrolling: oneOf([ 'auto', 'touch' ])
|
||||
|
||||
@@ -1,525 +1,399 @@
|
||||
exports[`components/View prop "children" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}>
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
style={Object {}}>
|
||||
<div
|
||||
className=" __style_df"
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
data-testid="1"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
style={Object {}} />
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/View prop "pointerEvents" 1`] = `
|
||||
<div
|
||||
className=" __style_df __style_pebo"
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"pointerEvents": null,
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"pointerEvents": "box-only",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View prop "style" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`components/View prop "style" 2`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
|
||||
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexGrow": 1,
|
||||
"WebkitFlexShrink": 1,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexGrow": 1,
|
||||
"flexShrink": 1,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 1,
|
||||
"msFlexPositive": 1,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View prop "style" 3`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 1,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 1,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 1,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View prop "style" 4`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
|
||||
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexGrow": 1,
|
||||
"WebkitFlexShrink": 2,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexGrow": 1,
|
||||
"flexShrink": 2,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 2,
|
||||
"msFlexPositive": 1,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View rendered element is a "div" by default 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`components/View rendered element is a "span" when inside <View accessibilityRole="button" /> 1`] = `
|
||||
<button
|
||||
className=" __style_df"
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
role="button"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
style={Object {}}
|
||||
type="button">
|
||||
<span
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
className="
|
||||
rn-alignItems:stretch
|
||||
rn-backgroundColor:transparent
|
||||
rn-borderTopStyle:solid
|
||||
rn-borderRightStyle:solid
|
||||
rn-borderBottomStyle:solid
|
||||
rn-borderLeftStyle:solid
|
||||
rn-borderTopWidth:0px
|
||||
rn-borderRightWidth:0px
|
||||
rn-borderBottomWidth:0px
|
||||
rn-borderLeftWidth:0px
|
||||
rn-boxSizing:border-box
|
||||
rn-color:inherit
|
||||
rn-display:flex
|
||||
rn-flexBasis:auto
|
||||
rn-flexDirection:column
|
||||
rn-flexShrink:0
|
||||
rn-font:inherit
|
||||
rn-listStyle:none
|
||||
rn-marginTop:0px
|
||||
rn-marginRight:0px
|
||||
rn-marginBottom:0px
|
||||
rn-marginLeft:0px
|
||||
rn-minHeight:0px
|
||||
rn-minWidth:0px
|
||||
rn-paddingTop:0px
|
||||
rn-paddingRight:0px
|
||||
rn-paddingBottom:0px
|
||||
rn-paddingLeft:0px
|
||||
rn-position:relative
|
||||
rn-textAlign:inherit
|
||||
rn-textDecoration:none"
|
||||
style={Object {}} />
|
||||
</button>
|
||||
`;
|
||||
|
||||
@@ -99,7 +99,7 @@ class View extends Component {
|
||||
onMagicTap,
|
||||
removeClippedSubviews,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const flattenedStyle = StyleSheet.flatten(style);
|
||||
@@ -107,43 +107,30 @@ class View extends Component {
|
||||
// 'View' needs to set 'flexShrink:0' only when there is no 'flex' or 'flexShrink' style provided
|
||||
const needsFlexReset = !flattenedStyle || (flattenedStyle.flex == null && flattenedStyle.flexShrink == null);
|
||||
|
||||
const normalizedEventHandlers = eventHandlerNames.reduce((handlerProps, handlerName) => {
|
||||
const component = this.context.isInAButtonView ? 'span' : 'div';
|
||||
|
||||
eventHandlerNames.reduce((props, handlerName) => {
|
||||
const handler = this.props[handlerName];
|
||||
if (typeof handler === 'function') {
|
||||
handlerProps[handlerName] = this._normalizeEventForHandler(handler, handlerName);
|
||||
props[handlerName] = this._normalizeEventForHandler(handler);
|
||||
}
|
||||
return handlerProps;
|
||||
}, {});
|
||||
return props;
|
||||
}, otherProps);
|
||||
|
||||
const component = this.context.isInAButtonView ? 'span' : 'div';
|
||||
const props = {
|
||||
...other,
|
||||
...normalizedEventHandlers,
|
||||
style: [
|
||||
styles.initial,
|
||||
style,
|
||||
needsFlexReset && styles.flexReset,
|
||||
pointerEventsStyle
|
||||
]
|
||||
};
|
||||
otherProps.style = [
|
||||
styles.initial,
|
||||
style,
|
||||
needsFlexReset && styles.flexReset,
|
||||
pointerEventsStyle
|
||||
];
|
||||
|
||||
return createDOMElement(component, props);
|
||||
return createDOMElement(component, otherProps);
|
||||
}
|
||||
|
||||
_normalizeEventForHandler(handler, handlerName) {
|
||||
// Browsers fire mouse events after touch events. This causes the
|
||||
// 'onResponderRelease' handler to be called twice for Touchables.
|
||||
// Auto-fix this issue by calling 'preventDefault' to cancel the mouse
|
||||
// events.
|
||||
const shouldCancelEvent = handlerName === 'onResponderRelease';
|
||||
|
||||
_normalizeEventForHandler(handler) {
|
||||
return (e) => {
|
||||
e.nativeEvent = normalizeNativeEvent(e.nativeEvent);
|
||||
const returnValue = handler(e);
|
||||
if (shouldCancelEvent && e.cancelable) {
|
||||
e.preventDefault();
|
||||
}
|
||||
return returnValue;
|
||||
return handler(e);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
14
src/core.js
14
src/core.js
@@ -1,17 +1,22 @@
|
||||
import findNodeHandle from './modules/findNodeHandle';
|
||||
import ReactDefaultInjection from 'react/lib/ReactDefaultInjection';
|
||||
import { render, unmountComponentAtNode } from 'react/lib/ReactMount';
|
||||
import ReactDefaultInjection from 'react-dom/lib/ReactDefaultInjection';
|
||||
import { render, unmountComponentAtNode } from 'react-dom/lib/ReactMount';
|
||||
|
||||
ReactDefaultInjection.inject();
|
||||
|
||||
// APIs
|
||||
import I18nManager from './apis/I18nManager';
|
||||
import Platform from './apis/Platform';
|
||||
import StyleSheet from './apis/StyleSheet';
|
||||
|
||||
// components
|
||||
import Image from './components/Image';
|
||||
import Text from './components/Text';
|
||||
import TextInput from './components/TextInput';
|
||||
import Touchable from './components/Touchable/Touchable';
|
||||
import TouchableHighlight from './components/Touchable/TouchableHighlight';
|
||||
import TouchableOpacity from './components/Touchable/TouchableOpacity';
|
||||
import TouchableWithoutFeedback from './components/Touchable/TouchableWithoutFeedback';
|
||||
import View from './components/View';
|
||||
|
||||
// modules
|
||||
@@ -24,11 +29,16 @@ const ReactNativeCore = {
|
||||
unmountComponentAtNode,
|
||||
// APIs
|
||||
I18nManager,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
// components
|
||||
Image,
|
||||
Text,
|
||||
TextInput,
|
||||
Touchable,
|
||||
TouchableHighlight,
|
||||
TouchableOpacity,
|
||||
TouchableWithoutFeedback,
|
||||
View
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import findNodeHandle from './modules/findNodeHandle';
|
||||
import ReactDefaultInjection from 'react/lib/ReactDefaultInjection';
|
||||
import { render, unmountComponentAtNode } from 'react/lib/ReactMount';
|
||||
import ReactDefaultInjection from 'react-dom/lib/ReactDefaultInjection';
|
||||
import { render, unmountComponentAtNode } from 'react-dom/lib/ReactMount';
|
||||
|
||||
ReactDefaultInjection.inject();
|
||||
|
||||
@@ -9,6 +9,8 @@ import Animated from './apis/Animated';
|
||||
import AppRegistry from './apis/AppRegistry';
|
||||
import AppState from './apis/AppState';
|
||||
import AsyncStorage from './apis/AsyncStorage';
|
||||
import BackAndroid from './apis/BackAndroid';
|
||||
import Clipboard from './apis/Clipboard';
|
||||
import Dimensions from './apis/Dimensions';
|
||||
import Easing from 'animated/lib/Easing';
|
||||
import I18nManager from './apis/I18nManager';
|
||||
@@ -58,6 +60,8 @@ const ReactNative = {
|
||||
AppRegistry,
|
||||
AppState,
|
||||
AsyncStorage,
|
||||
BackAndroid,
|
||||
Clipboard,
|
||||
Dimensions,
|
||||
Easing,
|
||||
I18nManager,
|
||||
|
||||
48
src/modules/ImageLoader/index.js
Normal file
48
src/modules/ImageLoader/index.js
Normal file
@@ -0,0 +1,48 @@
|
||||
let id = 0;
|
||||
const requests = {};
|
||||
|
||||
const ImageLoader = {
|
||||
abort(requestId: number) {
|
||||
let image = requests[`${requestId}`];
|
||||
if (image) {
|
||||
image.onerror = image.onload = image = null;
|
||||
delete requests[`${requestId}`];
|
||||
}
|
||||
},
|
||||
getSize(uri, success, failure) {
|
||||
let complete = false;
|
||||
const interval = setInterval(callback, 16);
|
||||
const requestId = ImageLoader.load(uri, callback, callback);
|
||||
|
||||
function callback() {
|
||||
const image = requests[`${requestId}`];
|
||||
if (image) {
|
||||
const { naturalHeight, naturalWidth } = image;
|
||||
if (naturalHeight && naturalWidth) {
|
||||
success(naturalWidth, naturalHeight);
|
||||
complete = true;
|
||||
}
|
||||
}
|
||||
if (complete) {
|
||||
ImageLoader.abort(requestId);
|
||||
clearInterval(interval);
|
||||
}
|
||||
}
|
||||
},
|
||||
load(uri, onLoad, onError): number {
|
||||
id += 1;
|
||||
const image = new window.Image();
|
||||
image.onerror = onError;
|
||||
image.onload = onLoad;
|
||||
image.src = uri;
|
||||
requests[`${id}`] = image;
|
||||
return id;
|
||||
},
|
||||
prefetch(uri): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
ImageLoader.load(uri, resolve, reject);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default ImageLoader;
|
||||
@@ -6,33 +6,9 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { Component } from 'react';
|
||||
import findNodeHandle from '../findNodeHandle';
|
||||
import UIManager from '../../apis/UIManager';
|
||||
|
||||
type MeasureInWindowOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
) => void
|
||||
|
||||
type MeasureLayoutOnSuccessCallback = (
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number
|
||||
) => void
|
||||
|
||||
type MeasureOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
pageX: number,
|
||||
pageY: number
|
||||
) => void
|
||||
|
||||
const NativeMethodsMixin = {
|
||||
/**
|
||||
* Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
@@ -52,11 +28,8 @@ const NativeMethodsMixin = {
|
||||
/**
|
||||
* Determines the position and dimensions of the view
|
||||
*/
|
||||
measure(callback: MeasureOnSuccessCallback) {
|
||||
UIManager.measure(
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback(this, callback)
|
||||
);
|
||||
measure(callback) {
|
||||
UIManager.measure(findNodeHandle(this), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -74,50 +47,23 @@ const NativeMethodsMixin = {
|
||||
* Note that these measurements are not available until after the rendering
|
||||
* has been completed in native.
|
||||
*/
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
|
||||
UIManager.measureInWindow(
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback(this, callback)
|
||||
);
|
||||
measureInWindow(callback) {
|
||||
UIManager.measureInWindow(findNodeHandle(this), callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Measures the view relative to another view (usually an ancestor)
|
||||
*/
|
||||
measureLayout(
|
||||
relativeToNativeNode: Object,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail: () => void /* currently unused */
|
||||
) {
|
||||
UIManager.measureLayout(
|
||||
findNodeHandle(this),
|
||||
relativeToNativeNode,
|
||||
mountSafeCallback(this, onFail),
|
||||
mountSafeCallback(this, onSuccess)
|
||||
);
|
||||
measureLayout(relativeToNativeNode, onSuccess, onFail) {
|
||||
UIManager.measureLayout(findNodeHandle(this), relativeToNativeNode, onFail, onSuccess);
|
||||
},
|
||||
|
||||
/**
|
||||
* This function sends props straight to the underlying DOM node.
|
||||
*/
|
||||
setNativeProps(nativeProps: Object) {
|
||||
UIManager.updateView(
|
||||
findNodeHandle(this),
|
||||
nativeProps,
|
||||
this
|
||||
);
|
||||
UIManager.updateView(findNodeHandle(this), nativeProps, this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* In the future, we should cleanup callbacks by cancelling them instead of
|
||||
* using this.
|
||||
*/
|
||||
const mountSafeCallback = (context: Component, callback: ?Function) => (...args) => {
|
||||
if (!callback) {
|
||||
return undefined;
|
||||
}
|
||||
return callback.apply(context, args);
|
||||
};
|
||||
|
||||
module.exports = NativeMethodsMixin;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user