mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-04-23 04:00:04 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cda89c5ce | ||
|
|
695eba45af | ||
|
|
92a2cb274a | ||
|
|
b1ca04d11e | ||
|
|
22ab70ea6f | ||
|
|
49f36d8eb1 | ||
|
|
80ba119b83 | ||
|
|
c30b67f6db | ||
|
|
4580f93199 | ||
|
|
4c46126ffe | ||
|
|
f8d5c15405 | ||
|
|
dc54e03625 | ||
|
|
4d5819ae28 |
@@ -27,7 +27,7 @@ online with [React Native for Web: Playground](http://codepen.io/necolas/pen/PZz
|
|||||||
To install in your app:
|
To install in your app:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install --save react react-native-web
|
npm install --save react@15.3 react-native-web
|
||||||
```
|
```
|
||||||
|
|
||||||
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
|
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
|
||||||
@@ -53,6 +53,7 @@ Exported modules:
|
|||||||
|
|
||||||
* Components
|
* Components
|
||||||
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
|
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
|
||||||
|
* [`Button`](docs/components/Button.md)
|
||||||
* [`Image`](docs/components/Image.md)
|
* [`Image`](docs/components/Image.md)
|
||||||
* [`ListView`](docs/components/ListView.md)
|
* [`ListView`](docs/components/ListView.md)
|
||||||
* [`ProgressBar`](docs/components/ProgressBar.md)
|
* [`ProgressBar`](docs/components/ProgressBar.md)
|
||||||
@@ -69,6 +70,7 @@ Exported modules:
|
|||||||
* [`AppRegistry`](docs/apis/AppRegistry.md)
|
* [`AppRegistry`](docs/apis/AppRegistry.md)
|
||||||
* [`AppState`](docs/apis/AppState.md)
|
* [`AppState`](docs/apis/AppState.md)
|
||||||
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
|
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
|
||||||
|
* [`Clipboard`](docs/apis/Clipboard.md)
|
||||||
* [`Dimensions`](docs/apis/Dimensions.md)
|
* [`Dimensions`](docs/apis/Dimensions.md)
|
||||||
* [`I18nManager`](docs/apis/I18nManager.md)
|
* [`I18nManager`](docs/apis/I18nManager.md)
|
||||||
* [`NativeMethods`](docs/apis/NativeMethods.md)
|
* [`NativeMethods`](docs/apis/NativeMethods.md)
|
||||||
|
|||||||
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.
|
||||||
39
docs/components/Button.md
Normal file
39
docs/components/Button.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Button
|
||||||
|
|
||||||
|
A basic button component. Supports a minimal level of customization. You can
|
||||||
|
build your own custom button using `TouchableOpacity` or
|
||||||
|
`TouchableNativeFeedback`.
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
**accessibilityLabel**: string
|
||||||
|
|
||||||
|
Defines the text available to assistive technologies upon interaction with the
|
||||||
|
element. (This is implemented using `aria-label`.)
|
||||||
|
|
||||||
|
**color**: string
|
||||||
|
|
||||||
|
Background color of the button.
|
||||||
|
|
||||||
|
**disabled**: bool = false
|
||||||
|
|
||||||
|
If true, disable all interactions for this component
|
||||||
|
|
||||||
|
**onPress**: function
|
||||||
|
|
||||||
|
This function is called on press.
|
||||||
|
|
||||||
|
**title**: string
|
||||||
|
|
||||||
|
Text to display inside the button.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```js
|
||||||
|
<Button
|
||||||
|
accessibilityLabel="Learn more about this purple button"
|
||||||
|
color="#841584"
|
||||||
|
onPress={onPressLearnMore}
|
||||||
|
title="Learn More"
|
||||||
|
/>
|
||||||
|
```
|
||||||
@@ -38,6 +38,18 @@ which this `ScrollView` renders.
|
|||||||
Fires at most once per frame during scrolling. The frequency of the events can
|
Fires at most once per frame during scrolling. The frequency of the events can
|
||||||
be contolled using the `scrollEventThrottle` prop.
|
be contolled using the `scrollEventThrottle` prop.
|
||||||
|
|
||||||
|
Invoked on scroll with the following event:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
nativeEvent: {
|
||||||
|
contentOffset: { x, y },
|
||||||
|
contentSize: { height, width },
|
||||||
|
layoutMeasurement: { height, width }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**refreshControl**: element
|
**refreshControl**: element
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
@@ -51,8 +63,8 @@ When false, the content does not scroll.
|
|||||||
|
|
||||||
**scrollEventThrottle**: number = 0
|
**scrollEventThrottle**: number = 0
|
||||||
|
|
||||||
This controls how often the scroll event will be fired while scrolling (in
|
This controls how often the scroll event will be fired while scrolling (as a
|
||||||
events per seconds). A higher number yields better accuracy for code that is
|
time interval in ms). A lower number yields better accuracy for code that is
|
||||||
tracking the scroll position, but can lead to scroll performance problems. The
|
tracking the scroll position, but can lead to scroll performance problems. The
|
||||||
default value is `0`, which means the scroll event will be sent only once each
|
default value is `0`, which means the scroll event will be sent only once each
|
||||||
time the view is scrolled.
|
time the view is scrolled.
|
||||||
@@ -104,7 +116,7 @@ export default class ScrollViewExample extends Component {
|
|||||||
contentContainerStyle={styles.container}
|
contentContainerStyle={styles.container}
|
||||||
horizontal
|
horizontal
|
||||||
onScroll={(e) => this.onScroll(e)}
|
onScroll={(e) => this.onScroll(e)}
|
||||||
scrollEventThrottle={60}
|
scrollEventThrottle={100}
|
||||||
style={styles.root}
|
style={styles.root}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
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 { I18nManager, StyleSheet, TouchableHighlight, Text, View } from 'react-native'
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { storiesOf, action } from '@kadira/storybook';
|
|
||||||
|
|
||||||
class RTLExample extends Component {
|
class I18nManagerExample extends Component {
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
I18nManager.setPreferredLanguageRTL(false)
|
I18nManager.setPreferredLanguageRTL(false)
|
||||||
}
|
}
|
||||||
@@ -75,5 +75,5 @@ const styles = StyleSheet.create({
|
|||||||
|
|
||||||
storiesOf('api: I18nManager', module)
|
storiesOf('api: I18nManager', module)
|
||||||
.add('RTL layout', () => (
|
.add('RTL layout', () => (
|
||||||
<RTLExample />
|
<I18nManagerExample />
|
||||||
))
|
))
|
||||||
29
examples/apis/Linking/LinkingExample.js
Normal file
29
examples/apis/Linking/LinkingExample.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Linking, StyleSheet, Text, View } from 'react-native'
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { storiesOf, action } from '@kadira/storybook';
|
||||||
|
|
||||||
|
class LinkingExample extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text onPress={() => { Linking.openURL('https://mathiasbynens.github.io/rel-noopener/malicious.html'); }} style={styles.text}>
|
||||||
|
Linking.openURL (Expect: "The previous tab is safe and intact")
|
||||||
|
</Text>
|
||||||
|
<Text accessibilityRole='link' href='https://mathiasbynens.github.io/rel-noopener/malicious.html' style={styles.text} target='_blank'>
|
||||||
|
target="_blank" (Expect: "The previous tab is safe and intact")
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
text: {
|
||||||
|
marginVertical: 10
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
storiesOf('api: Linking', module)
|
||||||
|
.add('Safe linking', () => (
|
||||||
|
<LinkingExample />
|
||||||
|
));
|
||||||
80
examples/components/Button/ButtonExample.js
Normal file
80
examples/components/Button/ButtonExample.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { action, storiesOf } from '@kadira/storybook';
|
||||||
|
import { Button, StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
|
const onButtonPress = action('Button has been pressed!');
|
||||||
|
|
||||||
|
const examples = [
|
||||||
|
{
|
||||||
|
title: 'Simple Button',
|
||||||
|
description: 'The title and onPress handler are required. It is ' +
|
||||||
|
'recommended to set accessibilityLabel to help make your app usable by ' +
|
||||||
|
'everyone.',
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onPress={onButtonPress}
|
||||||
|
title="Press Me"
|
||||||
|
accessibilityLabel="See an informative alert"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Adjusted color',
|
||||||
|
description: 'Adjusts the color in a way that looks standard on each ' +
|
||||||
|
'platform. On iOS, the color prop controls the color of the text. On ' +
|
||||||
|
'Android, the color adjusts the background color of the button.',
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onPress={onButtonPress}
|
||||||
|
title="Press Purple"
|
||||||
|
color="#841584"
|
||||||
|
accessibilityLabel="Learn more about purple"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Fit to text layout',
|
||||||
|
description: 'This layout strategy lets the title define the width of ' +
|
||||||
|
'the button',
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
|
||||||
|
<Button
|
||||||
|
onPress={onButtonPress}
|
||||||
|
title="This looks great!"
|
||||||
|
accessibilityLabel="This sounds great!"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onPress={onButtonPress}
|
||||||
|
title="Ok!"
|
||||||
|
color="#841584"
|
||||||
|
accessibilityLabel="Ok, Great!"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Disabled Button',
|
||||||
|
description: 'All interactions for the component are disabled.',
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
disabled
|
||||||
|
onPress={onButtonPress}
|
||||||
|
title="I Am Disabled"
|
||||||
|
accessibilityLabel="See an informative alert"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
examples.forEach((example) => {
|
||||||
|
storiesOf('component: Button', module)
|
||||||
|
.add(example.title, () => example.render());
|
||||||
|
});
|
||||||
@@ -1,4 +1,80 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { storiesOf, action } from '@kadira/storybook';
|
import { storiesOf } from '@kadira/storybook';
|
||||||
import { ListView } from 'react-native'
|
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'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { storiesOf, action } from '@kadira/storybook';
|
import { action, storiesOf } from '@kadira/storybook';
|
||||||
import { ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native'
|
import { ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native'
|
||||||
|
|
||||||
|
const onScroll = action('ScrollView.onScroll');
|
||||||
|
|
||||||
storiesOf('component: ScrollView', module)
|
storiesOf('component: ScrollView', module)
|
||||||
.add('vertical', () => (
|
.add('vertical', () => (
|
||||||
<View style={styles.scrollViewContainer}>
|
<View style={styles.scrollViewContainer}>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||||
onScroll={e => { console.log('ScrollView.onScroll', e); } }
|
onScroll={onScroll}
|
||||||
scrollEventThrottle={1000} // 1 event per second
|
scrollEventThrottle={1000} // 1 event per second
|
||||||
style={styles.scrollViewStyle}
|
style={styles.scrollViewStyle}
|
||||||
>
|
>
|
||||||
@@ -24,8 +26,8 @@ storiesOf('component: ScrollView', module)
|
|||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||||
horizontal
|
horizontal
|
||||||
onScroll={e => console.log('ScrollView.onScroll', e)}
|
onScroll={onScroll}
|
||||||
scrollEventThrottle={1} // 1 event per second
|
scrollEventThrottle={16} // ~60 events per second
|
||||||
style={styles.scrollViewStyle}
|
style={styles.scrollViewStyle}
|
||||||
>
|
>
|
||||||
{Array.from({ length: 50 }).map((item, i) => (
|
{Array.from({ length: 50 }).map((item, i) => (
|
||||||
@@ -48,10 +50,10 @@ const styles = StyleSheet.create({
|
|||||||
width: 300
|
width: 300
|
||||||
},
|
},
|
||||||
scrollViewStyle: {
|
scrollViewStyle: {
|
||||||
borderWidth: '1px'
|
borderWidth: 1
|
||||||
},
|
},
|
||||||
scrollViewContentContainerStyle: {
|
scrollViewContentContainerStyle: {
|
||||||
backgroundColor: '#eee',
|
backgroundColor: '#eee',
|
||||||
padding: '10px'
|
padding: 10
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class TextEventsExample extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View style={{ alignItems: 'center' }}>
|
||||||
<TextInput
|
<TextInput
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
placeholder="Enter text to see events"
|
placeholder="Enter text to see events"
|
||||||
@@ -83,7 +83,7 @@ class TextEventsExample extends React.Component {
|
|||||||
onKeyPress={(event) => {
|
onKeyPress={(event) => {
|
||||||
this.updateText('onKeyPress key: ' + event.nativeEvent.key);
|
this.updateText('onKeyPress key: ' + event.nativeEvent.key);
|
||||||
}}
|
}}
|
||||||
style={styles.default}
|
style={[ styles.default, { maxWidth: 200 } ]}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.eventLabel}>
|
<Text style={styles.eventLabel}>
|
||||||
{this.state.curText}{'\n'}
|
{this.state.curText}{'\n'}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-web",
|
"name": "react-native-web",
|
||||||
"version": "0.0.56",
|
"version": "0.0.60",
|
||||||
"description": "React Native for Web",
|
"description": "React Native for Web",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
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;
|
||||||
36
src/apis/Linking/index.js
Normal file
36
src/apis/Linking/index.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const Linking = {
|
||||||
|
addEventListener() {},
|
||||||
|
removeEventListener() {},
|
||||||
|
canOpenUrl() { return true; },
|
||||||
|
getInitialUrl() { return ''; },
|
||||||
|
openURL(url) {
|
||||||
|
iframeOpen(url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tabs opened using JavaScript may redirect the parent tab using
|
||||||
|
* `window.opener.location`, ignoring cross-origin restrictions and enabling
|
||||||
|
* phishing attacks.
|
||||||
|
*
|
||||||
|
* Safari requires that we open the url by injecting a hidden iframe that calls
|
||||||
|
* window.open(), then removes the iframe from the DOM.
|
||||||
|
*
|
||||||
|
* https://mathiasbynens.github.io/rel-noopener/
|
||||||
|
*/
|
||||||
|
const iframeOpen = (url) => {
|
||||||
|
const iframe = document.createElement('iframe');
|
||||||
|
iframe.style.display = 'none';
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||||
|
const script = iframeDoc.createElement('script');
|
||||||
|
script.text = `
|
||||||
|
window.parent = null; window.top = null; window.frameElement = null;
|
||||||
|
var child = window.open("${url}"); child.opener = null;
|
||||||
|
`;
|
||||||
|
iframeDoc.body.appendChild(script);
|
||||||
|
document.body.removeChild(iframe);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Linking;
|
||||||
5
src/components/Button/__tests__/index-test.js
Normal file
5
src/components/Button/__tests__/index-test.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/* eslint-env jasmine, jest */
|
||||||
|
|
||||||
|
describe('components/Button', () => {
|
||||||
|
test.skip('NO TEST COVERAGE', () => {});
|
||||||
|
});
|
||||||
66
src/components/Button/index.js
Normal file
66
src/components/Button/index.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import ColorPropType from '../../propTypes/ColorPropType';
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import StyleSheet from '../../apis/StyleSheet';
|
||||||
|
import TouchableOpacity from '../Touchable/TouchableOpacity';
|
||||||
|
import Text from '../Text';
|
||||||
|
|
||||||
|
class Button extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
accessibilityLabel: PropTypes.string,
|
||||||
|
color: ColorPropType,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
onPress: PropTypes.func.isRequired,
|
||||||
|
title: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
accessibilityLabel,
|
||||||
|
color,
|
||||||
|
disabled,
|
||||||
|
onPress,
|
||||||
|
title
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
accessibilityLabel={accessibilityLabel}
|
||||||
|
accessibilityRole={'button'}
|
||||||
|
disabled={disabled}
|
||||||
|
onPress={onPress}
|
||||||
|
style={[
|
||||||
|
styles.button,
|
||||||
|
color && { backgroundColor: color },
|
||||||
|
disabled && styles.buttonDisabled
|
||||||
|
]}>
|
||||||
|
<Text style={[
|
||||||
|
styles.text,
|
||||||
|
disabled && styles.textDisabled
|
||||||
|
]}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
backgroundColor: '#2196F3',
|
||||||
|
borderRadius: 2
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
textAlign: 'center',
|
||||||
|
color: '#fff',
|
||||||
|
padding: 8,
|
||||||
|
fontWeight: '500'
|
||||||
|
},
|
||||||
|
buttonDisabled: {
|
||||||
|
backgroundColor: '#dfdfdf'
|
||||||
|
},
|
||||||
|
textDisabled: {
|
||||||
|
color: '#a1a1a1'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Button;
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||||
import ImageResizeMode from './ImageResizeMode';
|
import ImageResizeMode from './ImageResizeMode';
|
||||||
import ImageStylePropTypes from './ImageStylePropTypes';
|
import ImageStylePropTypes from './ImageStylePropTypes';
|
||||||
|
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
|
||||||
import StyleSheet from '../../apis/StyleSheet';
|
import StyleSheet from '../../apis/StyleSheet';
|
||||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
|
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
|
||||||
import View from '../View';
|
import View from '../View';
|
||||||
@@ -193,7 +194,7 @@ class Image extends Component {
|
|||||||
this._imageState = status;
|
this._imageState = status;
|
||||||
const isLoaded = this._imageState === STATUS_LOADED;
|
const isLoaded = this._imageState === STATUS_LOADED;
|
||||||
if (isLoaded !== this.state.isLoaded) {
|
if (isLoaded !== this.state.isLoaded) {
|
||||||
window.requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (this._isMounted) {
|
if (this._isMounted) {
|
||||||
this.setState({ isLoaded });
|
this.setState({ isLoaded });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,27 @@ import applyNativeMethods from '../../modules/applyNativeMethods';
|
|||||||
import ListViewDataSource from './ListViewDataSource';
|
import ListViewDataSource from './ListViewDataSource';
|
||||||
import ListViewPropTypes from './ListViewPropTypes';
|
import ListViewPropTypes from './ListViewPropTypes';
|
||||||
import ScrollView from '../ScrollView';
|
import ScrollView from '../ScrollView';
|
||||||
import View from '../View';
|
import StaticRenderer from '../StaticRenderer';
|
||||||
import React, { Component } from 'react';
|
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 {
|
class ListView extends Component {
|
||||||
static propTypes = ListViewPropTypes;
|
static propTypes = ListViewPropTypes;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
initialListSize: 10,
|
initialListSize: DEFAULT_INITIAL_ROWS,
|
||||||
pageSize: 1,
|
pageSize: DEFAULT_PAGE_SIZE,
|
||||||
renderScrollComponent: (props) => <ScrollView {...props} />,
|
renderScrollComponent: (props) => <ScrollView {...props} />,
|
||||||
scrollRenderAheadDistance: 1000,
|
scrollRenderAheadDistance: DEFAULT_SCROLL_RENDER_AHEAD,
|
||||||
onEndReachedThreshold: 1000,
|
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD,
|
||||||
|
scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE,
|
||||||
|
removeClippedSubviews: true,
|
||||||
stickyHeaderIndices: []
|
stickyHeaderIndices: []
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,6 +35,34 @@ class ListView extends Component {
|
|||||||
highlightedRow: {}
|
highlightedRow: {}
|
||||||
};
|
};
|
||||||
this.onRowHighlighted = (sectionId, rowId) => this._onRowHighlighted(sectionId, rowId);
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this._measureAndUpdateScrollProps();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getScrollResponder() {
|
getScrollResponder() {
|
||||||
@@ -40,58 +77,313 @@ class ListView extends Component {
|
|||||||
return this._scrollViewRef && this._scrollViewRef.setNativeProps(props);
|
return this._scrollViewRef && this._scrollViewRef.setNativeProps(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRowHighlighted(sectionId, rowId) {
|
_onRowHighlighted = (sectionId, rowId) => {
|
||||||
this.setState({ highlightedRow: { 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() {
|
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 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
|
const dataSource = this.props.dataSource;
|
||||||
if (renderSectionHeader) {
|
const allRowIDs = dataSource.rowIdentities;
|
||||||
const section = dataSource.getSectionHeaderData(sectionIdx);
|
let rowCount = 0;
|
||||||
const key = `s_${sectionId}`;
|
const sectionHeaderIndices = [];
|
||||||
const child = <View key={key}>{renderSectionHeader(section, sectionId)}</View>;
|
|
||||||
children.push(child);
|
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
|
if (this.props.renderSectionHeader) {
|
||||||
for (let rowIdx = 0, rowCnt = rows.length; rowIdx < rowCnt; rowIdx++) {
|
const shouldUpdateHeader = rowCount >= this._prevRenderedRowsCount &&
|
||||||
const rowId = rows[rowIdx];
|
dataSource.sectionHeaderShouldUpdate(sectionIdx);
|
||||||
const row = dataSource.getRowData(sectionIdx, rowIdx);
|
children.push(
|
||||||
const key = `r_${sectionId}_${rowId}`;
|
<StaticRenderer
|
||||||
const child = <View key={key}>{renderRow(row, sectionId, rowId, this.onRowHighlighted)}</View>;
|
key={`s_${sectionID}`}
|
||||||
children.push(child);
|
render={this.renderSectionHeaderFn(
|
||||||
|
dataSource.getSectionHeaderData(sectionIdx),
|
||||||
|
sectionID
|
||||||
|
)}
|
||||||
|
shouldUpdate={!!shouldUpdateHeader}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
sectionHeaderIndices.push(totalIndex++);
|
||||||
|
}
|
||||||
|
|
||||||
// render optional separator
|
for (let rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {
|
||||||
if (renderSeparator && ((rowIdx !== rows.length - 1) || (sectionIdx === sections.length - 1))) {
|
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 =
|
const adjacentRowHighlighted =
|
||||||
this.state.highlightedRow.sectionID === sectionId && (
|
this.state.highlightedRow.sectionID === sectionID && (
|
||||||
this.state.highlightedRow.rowID === rowId ||
|
this.state.highlightedRow.rowID === rowID ||
|
||||||
this.state.highlightedRow.rowID === rows[rowIdx + 1]);
|
this.state.highlightedRow.rowID === rowIDs[rowIdx + 1]
|
||||||
const separator = renderSeparator(sectionId, rowId, adjacentRowHighlighted);
|
);
|
||||||
children.push(separator);
|
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), {
|
const {
|
||||||
ref: this._setScrollViewRef
|
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);
|
}, 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) => {
|
_setScrollViewRef = (component) => {
|
||||||
this._scrollViewRef = component;
|
this._scrollViewRef = component;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,35 @@ import debounce from 'debounce';
|
|||||||
import View from '../View';
|
import View from '../View';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
const normalizeScrollEvent = (e) => ({
|
||||||
|
nativeEvent: {
|
||||||
|
contentOffset: {
|
||||||
|
get x() {
|
||||||
|
return e.target.scrollLeft;
|
||||||
|
},
|
||||||
|
get y() {
|
||||||
|
return e.target.scrollTop;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentSize: {
|
||||||
|
get height() {
|
||||||
|
return e.target.scrollHeight;
|
||||||
|
},
|
||||||
|
get width() {
|
||||||
|
return e.target.scrollWidth;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layoutMeasurement: {
|
||||||
|
get height() {
|
||||||
|
return e.target.offsetHeight;
|
||||||
|
},
|
||||||
|
get width() {
|
||||||
|
return e.target.offsetWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates the Web-specific scroll throttling and disabling logic
|
* Encapsulates the Web-specific scroll throttling and disabling logic
|
||||||
*/
|
*/
|
||||||
@@ -75,13 +104,13 @@ export default class ScrollViewBase extends Component {
|
|||||||
_handleScrollTick(e) {
|
_handleScrollTick(e) {
|
||||||
const { onScroll } = this.props;
|
const { onScroll } = this.props;
|
||||||
this._state.scrollLastTick = Date.now();
|
this._state.scrollLastTick = Date.now();
|
||||||
if (onScroll) { onScroll(e); }
|
if (onScroll) { onScroll(normalizeScrollEvent(e)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleScrollEnd(e) {
|
_handleScrollEnd(e) {
|
||||||
const { onScroll } = this.props;
|
const { onScroll } = this.props;
|
||||||
this._state.isScrolling = false;
|
this._state.isScrolling = false;
|
||||||
if (onScroll) { onScroll(e); }
|
if (onScroll) { onScroll(normalizeScrollEvent(e)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
_shouldEmitScrollEvent(lastTick, eventThrottle) {
|
_shouldEmitScrollEvent(lastTick, eventThrottle) {
|
||||||
|
|||||||
@@ -236,7 +236,6 @@ const styles = StyleSheet.create({
|
|||||||
overflowY: 'hidden'
|
overflowY: 'hidden'
|
||||||
},
|
},
|
||||||
contentContainer: {
|
contentContainer: {
|
||||||
flexGrow: 1,
|
|
||||||
transform: [ { translateZ: 0 } ]
|
transform: [ { translateZ: 0 } ]
|
||||||
},
|
},
|
||||||
contentContainerHorizontal: {
|
contentContainerHorizontal: {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered
|
|||||||
"WebkitFlexBasis": "auto",
|
"WebkitFlexBasis": "auto",
|
||||||
"WebkitFlexDirection": "column",
|
"WebkitFlexDirection": "column",
|
||||||
"WebkitFlexShrink": 0,
|
"WebkitFlexShrink": 0,
|
||||||
"WebkitTransition": "background-color 0.1s",
|
"WebkitTransition": "0.1s",
|
||||||
"alignItems": "stretch",
|
"alignItems": "stretch",
|
||||||
"backgroundColor": "#939393",
|
"backgroundColor": "#939393",
|
||||||
"borderBottomLeftRadius": "10px",
|
"borderBottomLeftRadius": "10px",
|
||||||
@@ -112,7 +112,7 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered
|
|||||||
"textAlign": "inherit",
|
"textAlign": "inherit",
|
||||||
"textDecoration": "none",
|
"textDecoration": "none",
|
||||||
"top": "0px",
|
"top": "0px",
|
||||||
"transition": "background-color 0.1s",
|
"transition": "0.1s",
|
||||||
"width": "90%",
|
"width": "90%",
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
@@ -129,7 +129,8 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered
|
|||||||
"WebkitFlexBasis": "auto",
|
"WebkitFlexBasis": "auto",
|
||||||
"WebkitFlexDirection": "column",
|
"WebkitFlexDirection": "column",
|
||||||
"WebkitFlexShrink": 0,
|
"WebkitFlexShrink": 0,
|
||||||
"WebkitTransition": "background-color 0.1s",
|
"WebkitTransform": "translateX(0%)",
|
||||||
|
"WebkitTransition": "0.1s",
|
||||||
"alignItems": "stretch",
|
"alignItems": "stretch",
|
||||||
"alignSelf": "flex-start",
|
"alignSelf": "flex-start",
|
||||||
"backgroundColor": "#FAFAFA",
|
"backgroundColor": "#FAFAFA",
|
||||||
@@ -166,6 +167,7 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered
|
|||||||
"msFlexItemAlign": "start",
|
"msFlexItemAlign": "start",
|
||||||
"msFlexNegative": 0,
|
"msFlexNegative": 0,
|
||||||
"msPreferredSize": "auto",
|
"msPreferredSize": "auto",
|
||||||
|
"msTransform": "translateX(0%)",
|
||||||
"paddingBottom": "0px",
|
"paddingBottom": "0px",
|
||||||
"paddingLeft": "0px",
|
"paddingLeft": "0px",
|
||||||
"paddingRight": "0px",
|
"paddingRight": "0px",
|
||||||
@@ -173,7 +175,8 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered
|
|||||||
"position": "relative",
|
"position": "relative",
|
||||||
"textAlign": "inherit",
|
"textAlign": "inherit",
|
||||||
"textDecoration": "none",
|
"textDecoration": "none",
|
||||||
"transition": "background-color 0.1s",
|
"transform": "translateX(0%)",
|
||||||
|
"transition": "0.1s",
|
||||||
"width": "20px",
|
"width": "20px",
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
@@ -278,7 +281,7 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered
|
|||||||
"WebkitFlexBasis": "auto",
|
"WebkitFlexBasis": "auto",
|
||||||
"WebkitFlexDirection": "column",
|
"WebkitFlexDirection": "column",
|
||||||
"WebkitFlexShrink": 0,
|
"WebkitFlexShrink": 0,
|
||||||
"WebkitTransition": "background-color 0.1s",
|
"WebkitTransition": "0.1s",
|
||||||
"alignItems": "stretch",
|
"alignItems": "stretch",
|
||||||
"backgroundColor": "#D5D5D5",
|
"backgroundColor": "#D5D5D5",
|
||||||
"borderBottomLeftRadius": "10px",
|
"borderBottomLeftRadius": "10px",
|
||||||
@@ -323,7 +326,7 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered
|
|||||||
"textAlign": "inherit",
|
"textAlign": "inherit",
|
||||||
"textDecoration": "none",
|
"textDecoration": "none",
|
||||||
"top": "0px",
|
"top": "0px",
|
||||||
"transition": "background-color 0.1s",
|
"transition": "0.1s",
|
||||||
"width": "90%",
|
"width": "90%",
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
@@ -340,7 +343,8 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered
|
|||||||
"WebkitFlexBasis": "auto",
|
"WebkitFlexBasis": "auto",
|
||||||
"WebkitFlexDirection": "column",
|
"WebkitFlexDirection": "column",
|
||||||
"WebkitFlexShrink": 0,
|
"WebkitFlexShrink": 0,
|
||||||
"WebkitTransition": "background-color 0.1s",
|
"WebkitTransform": "translateX(0%)",
|
||||||
|
"WebkitTransition": "0.1s",
|
||||||
"alignItems": "stretch",
|
"alignItems": "stretch",
|
||||||
"alignSelf": "flex-start",
|
"alignSelf": "flex-start",
|
||||||
"backgroundColor": "#BDBDBD",
|
"backgroundColor": "#BDBDBD",
|
||||||
@@ -377,6 +381,7 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered
|
|||||||
"msFlexItemAlign": "start",
|
"msFlexItemAlign": "start",
|
||||||
"msFlexNegative": 0,
|
"msFlexNegative": 0,
|
||||||
"msPreferredSize": "auto",
|
"msPreferredSize": "auto",
|
||||||
|
"msTransform": "translateX(0%)",
|
||||||
"paddingBottom": "0px",
|
"paddingBottom": "0px",
|
||||||
"paddingLeft": "0px",
|
"paddingLeft": "0px",
|
||||||
"paddingRight": "0px",
|
"paddingRight": "0px",
|
||||||
@@ -384,7 +389,8 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered
|
|||||||
"position": "relative",
|
"position": "relative",
|
||||||
"textAlign": "inherit",
|
"textAlign": "inherit",
|
||||||
"textDecoration": "none",
|
"textDecoration": "none",
|
||||||
"transition": "background-color 0.1s",
|
"transform": "translateX(0%)",
|
||||||
|
"transition": "0.1s",
|
||||||
"width": "20px",
|
"width": "20px",
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
@@ -489,7 +495,7 @@ exports[`components/Switch value when "false" an unchecked checkbox is rendered
|
|||||||
"WebkitFlexBasis": "auto",
|
"WebkitFlexBasis": "auto",
|
||||||
"WebkitFlexDirection": "column",
|
"WebkitFlexDirection": "column",
|
||||||
"WebkitFlexShrink": 0,
|
"WebkitFlexShrink": 0,
|
||||||
"WebkitTransition": "background-color 0.1s",
|
"WebkitTransition": "0.1s",
|
||||||
"alignItems": "stretch",
|
"alignItems": "stretch",
|
||||||
"backgroundColor": "#939393",
|
"backgroundColor": "#939393",
|
||||||
"borderBottomLeftRadius": "10px",
|
"borderBottomLeftRadius": "10px",
|
||||||
@@ -534,7 +540,7 @@ exports[`components/Switch value when "false" an unchecked checkbox is rendered
|
|||||||
"textAlign": "inherit",
|
"textAlign": "inherit",
|
||||||
"textDecoration": "none",
|
"textDecoration": "none",
|
||||||
"top": "0px",
|
"top": "0px",
|
||||||
"transition": "background-color 0.1s",
|
"transition": "0.1s",
|
||||||
"width": "90%",
|
"width": "90%",
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
@@ -551,7 +557,8 @@ exports[`components/Switch value when "false" an unchecked checkbox is rendered
|
|||||||
"WebkitFlexBasis": "auto",
|
"WebkitFlexBasis": "auto",
|
||||||
"WebkitFlexDirection": "column",
|
"WebkitFlexDirection": "column",
|
||||||
"WebkitFlexShrink": 0,
|
"WebkitFlexShrink": 0,
|
||||||
"WebkitTransition": "background-color 0.1s",
|
"WebkitTransform": "translateX(0%)",
|
||||||
|
"WebkitTransition": "0.1s",
|
||||||
"alignItems": "stretch",
|
"alignItems": "stretch",
|
||||||
"alignSelf": "flex-start",
|
"alignSelf": "flex-start",
|
||||||
"backgroundColor": "#FAFAFA",
|
"backgroundColor": "#FAFAFA",
|
||||||
@@ -588,6 +595,7 @@ exports[`components/Switch value when "false" an unchecked checkbox is rendered
|
|||||||
"msFlexItemAlign": "start",
|
"msFlexItemAlign": "start",
|
||||||
"msFlexNegative": 0,
|
"msFlexNegative": 0,
|
||||||
"msPreferredSize": "auto",
|
"msPreferredSize": "auto",
|
||||||
|
"msTransform": "translateX(0%)",
|
||||||
"paddingBottom": "0px",
|
"paddingBottom": "0px",
|
||||||
"paddingLeft": "0px",
|
"paddingLeft": "0px",
|
||||||
"paddingRight": "0px",
|
"paddingRight": "0px",
|
||||||
@@ -595,7 +603,8 @@ exports[`components/Switch value when "false" an unchecked checkbox is rendered
|
|||||||
"position": "relative",
|
"position": "relative",
|
||||||
"textAlign": "inherit",
|
"textAlign": "inherit",
|
||||||
"textDecoration": "none",
|
"textDecoration": "none",
|
||||||
"transition": "background-color 0.1s",
|
"transform": "translateX(0%)",
|
||||||
|
"transition": "0.1s",
|
||||||
"width": "20px",
|
"width": "20px",
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
@@ -700,7 +709,7 @@ exports[`components/Switch value when "true" a checked checkbox is rendered 1`]
|
|||||||
"WebkitFlexBasis": "auto",
|
"WebkitFlexBasis": "auto",
|
||||||
"WebkitFlexDirection": "column",
|
"WebkitFlexDirection": "column",
|
||||||
"WebkitFlexShrink": 0,
|
"WebkitFlexShrink": 0,
|
||||||
"WebkitTransition": "background-color 0.1s",
|
"WebkitTransition": "0.1s",
|
||||||
"alignItems": "stretch",
|
"alignItems": "stretch",
|
||||||
"backgroundColor": "#A3D3CF",
|
"backgroundColor": "#A3D3CF",
|
||||||
"borderBottomLeftRadius": "10px",
|
"borderBottomLeftRadius": "10px",
|
||||||
@@ -745,7 +754,7 @@ exports[`components/Switch value when "true" a checked checkbox is rendered 1`]
|
|||||||
"textAlign": "inherit",
|
"textAlign": "inherit",
|
||||||
"textDecoration": "none",
|
"textDecoration": "none",
|
||||||
"top": "0px",
|
"top": "0px",
|
||||||
"transition": "background-color 0.1s",
|
"transition": "0.1s",
|
||||||
"width": "90%",
|
"width": "90%",
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
@@ -755,16 +764,17 @@ exports[`components/Switch value when "true" a checked checkbox is rendered 1`]
|
|||||||
Object {
|
Object {
|
||||||
"MozBoxSizing": "border-box",
|
"MozBoxSizing": "border-box",
|
||||||
"WebkitAlignItems": "stretch",
|
"WebkitAlignItems": "stretch",
|
||||||
"WebkitAlignSelf": "flex-end",
|
"WebkitAlignSelf": "flex-start",
|
||||||
"WebkitBoxAlign": "stretch",
|
"WebkitBoxAlign": "stretch",
|
||||||
"WebkitBoxDirection": "normal",
|
"WebkitBoxDirection": "normal",
|
||||||
"WebkitBoxOrient": "vertical",
|
"WebkitBoxOrient": "vertical",
|
||||||
"WebkitFlexBasis": "auto",
|
"WebkitFlexBasis": "auto",
|
||||||
"WebkitFlexDirection": "column",
|
"WebkitFlexDirection": "column",
|
||||||
"WebkitFlexShrink": 0,
|
"WebkitFlexShrink": 0,
|
||||||
"WebkitTransition": "background-color 0.1s",
|
"WebkitTransform": "translateX(100%)",
|
||||||
|
"WebkitTransition": "0.1s",
|
||||||
"alignItems": "stretch",
|
"alignItems": "stretch",
|
||||||
"alignSelf": "flex-end",
|
"alignSelf": "flex-start",
|
||||||
"backgroundColor": "#009688",
|
"backgroundColor": "#009688",
|
||||||
"borderBottomLeftRadius": "100%",
|
"borderBottomLeftRadius": "100%",
|
||||||
"borderBottomRightRadius": "100%",
|
"borderBottomRightRadius": "100%",
|
||||||
@@ -796,9 +806,10 @@ exports[`components/Switch value when "true" a checked checkbox is rendered 1`]
|
|||||||
"minWidth": "0px",
|
"minWidth": "0px",
|
||||||
"msFlexAlign": "stretch",
|
"msFlexAlign": "stretch",
|
||||||
"msFlexDirection": "column",
|
"msFlexDirection": "column",
|
||||||
"msFlexItemAlign": "end",
|
"msFlexItemAlign": "start",
|
||||||
"msFlexNegative": 0,
|
"msFlexNegative": 0,
|
||||||
"msPreferredSize": "auto",
|
"msPreferredSize": "auto",
|
||||||
|
"msTransform": "translateX(100%)",
|
||||||
"paddingBottom": "0px",
|
"paddingBottom": "0px",
|
||||||
"paddingLeft": "0px",
|
"paddingLeft": "0px",
|
||||||
"paddingRight": "0px",
|
"paddingRight": "0px",
|
||||||
@@ -806,7 +817,8 @@ exports[`components/Switch value when "true" a checked checkbox is rendered 1`]
|
|||||||
"position": "relative",
|
"position": "relative",
|
||||||
"textAlign": "inherit",
|
"textAlign": "inherit",
|
||||||
"textDecoration": "none",
|
"textDecoration": "none",
|
||||||
"transition": "background-color 0.1s",
|
"transform": "translateX(100%)",
|
||||||
|
"transition": "0.1s",
|
||||||
"width": "20px",
|
"width": "20px",
|
||||||
}
|
}
|
||||||
} />
|
} />
|
||||||
|
|||||||
@@ -88,9 +88,9 @@ class Switch extends Component {
|
|||||||
const thumbStyle = [
|
const thumbStyle = [
|
||||||
styles.thumb,
|
styles.thumb,
|
||||||
{
|
{
|
||||||
alignSelf: value ? 'flex-end' : 'flex-start',
|
|
||||||
backgroundColor: thumbCurrentColor,
|
backgroundColor: thumbCurrentColor,
|
||||||
height: thumbHeight,
|
height: thumbHeight,
|
||||||
|
transform: [ { translateX: value ? '100%' : '0%' } ],
|
||||||
width: thumbWidth
|
width: thumbWidth
|
||||||
},
|
},
|
||||||
disabled && styles.disabledThumb
|
disabled && styles.disabledThumb
|
||||||
@@ -151,16 +151,17 @@ const styles = StyleSheet.create({
|
|||||||
...StyleSheet.absoluteFillObject,
|
...StyleSheet.absoluteFillObject,
|
||||||
height: '70%',
|
height: '70%',
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
transition: 'background-color 0.1s',
|
transition: '0.1s',
|
||||||
width: '90%'
|
width: '90%'
|
||||||
},
|
},
|
||||||
disabledTrack: {
|
disabledTrack: {
|
||||||
backgroundColor: '#D5D5D5'
|
backgroundColor: '#D5D5D5'
|
||||||
},
|
},
|
||||||
thumb: {
|
thumb: {
|
||||||
|
alignSelf: 'flex-start',
|
||||||
borderRadius: '100%',
|
borderRadius: '100%',
|
||||||
boxShadow: thumbDefaultBoxShadow,
|
boxShadow: thumbDefaultBoxShadow,
|
||||||
transition: 'background-color 0.1s'
|
transition: '0.1s'
|
||||||
},
|
},
|
||||||
disabledThumb: {
|
disabledThumb: {
|
||||||
backgroundColor: '#BDBDBD'
|
backgroundColor: '#BDBDBD'
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
exports[`components/Text prop "children" 1`] = `
|
exports[`components/Text prop "children" 1`] = `
|
||||||
<span
|
<span
|
||||||
className=""
|
className=""
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"borderBottomWidth": "0px",
|
"borderBottomWidth": "0px",
|
||||||
@@ -27,10 +26,39 @@ exports[`components/Text prop "children" 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`components/Text prop "selectable" 1`] = `
|
exports[`components/Text prop "onPress" 1`] = `
|
||||||
<span
|
<span
|
||||||
className=""
|
className=""
|
||||||
onClick={[Function]}
|
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",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tabIndex={0} />
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`components/Text prop "selectable" 1`] = `
|
||||||
|
<span
|
||||||
|
className=""
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"borderBottomWidth": "0px",
|
"borderBottomWidth": "0px",
|
||||||
@@ -57,7 +85,6 @@ exports[`components/Text prop "selectable" 1`] = `
|
|||||||
exports[`components/Text prop "selectable" 2`] = `
|
exports[`components/Text prop "selectable" 2`] = `
|
||||||
<span
|
<span
|
||||||
className=""
|
className=""
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"MozUserSelect": "none",
|
"MozUserSelect": "none",
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ describe('components/Text', () => {
|
|||||||
|
|
||||||
test('prop "numberOfLines"');
|
test('prop "numberOfLines"');
|
||||||
|
|
||||||
|
test('prop "onPress"', () => {
|
||||||
|
const onPress = (e) => {};
|
||||||
|
const component = renderer.create(<Text onPress={onPress} />);
|
||||||
|
expect(component.toJSON()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
test('prop "selectable"', () => {
|
test('prop "selectable"', () => {
|
||||||
let component = renderer.create(<Text />);
|
let component = renderer.create(<Text />);
|
||||||
expect(component.toJSON()).toMatchSnapshot();
|
expect(component.toJSON()).toMatchSnapshot();
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class Text extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
numberOfLines,
|
numberOfLines,
|
||||||
|
onPress,
|
||||||
selectable,
|
selectable,
|
||||||
style,
|
style,
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
@@ -37,26 +38,35 @@ class Text extends Component {
|
|||||||
ellipsizeMode,
|
ellipsizeMode,
|
||||||
minimumFontScale,
|
minimumFontScale,
|
||||||
onLayout,
|
onLayout,
|
||||||
onPress,
|
|
||||||
suppressHighlighting,
|
suppressHighlighting,
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
...other
|
...other
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
if (onPress) {
|
||||||
|
other.onClick = onPress;
|
||||||
|
other.onKeyDown = this._createEnterHandler(onPress);
|
||||||
|
other.tabIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return createDOMElement('span', {
|
return createDOMElement('span', {
|
||||||
...other,
|
...other,
|
||||||
onClick: this._onPress,
|
|
||||||
style: [
|
style: [
|
||||||
styles.initial,
|
styles.initial,
|
||||||
style,
|
style,
|
||||||
!selectable && styles.notSelectable,
|
!selectable && styles.notSelectable,
|
||||||
numberOfLines === 1 && styles.singleLineStyle
|
numberOfLines === 1 && styles.singleLineStyle,
|
||||||
|
onPress && styles.pressable
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPress = (e) => {
|
_createEnterHandler(fn) {
|
||||||
if (this.props.onPress) { this.props.onPress(e); }
|
return (e) => {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
fn && fn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +84,9 @@ const styles = StyleSheet.create({
|
|||||||
notSelectable: {
|
notSelectable: {
|
||||||
userSelect: 'none'
|
userSelect: 'none'
|
||||||
},
|
},
|
||||||
|
pressable: {
|
||||||
|
cursor: 'pointer'
|
||||||
|
},
|
||||||
singleLineStyle: {
|
singleLineStyle: {
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
|||||||
@@ -270,7 +270,6 @@ const styles = StyleSheet.create({
|
|||||||
borderWidth: 0,
|
borderWidth: 0,
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
color: 'inherit',
|
color: 'inherit',
|
||||||
flex: 1,
|
|
||||||
font: 'inherit',
|
font: 'inherit',
|
||||||
padding: 0
|
padding: 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import Animated from './apis/Animated';
|
|||||||
import AppRegistry from './apis/AppRegistry';
|
import AppRegistry from './apis/AppRegistry';
|
||||||
import AppState from './apis/AppState';
|
import AppState from './apis/AppState';
|
||||||
import AsyncStorage from './apis/AsyncStorage';
|
import AsyncStorage from './apis/AsyncStorage';
|
||||||
|
import Clipboard from './apis/Clipboard';
|
||||||
import Dimensions from './apis/Dimensions';
|
import Dimensions from './apis/Dimensions';
|
||||||
import Easing from 'animated/lib/Easing';
|
import Easing from 'animated/lib/Easing';
|
||||||
import I18nManager from './apis/I18nManager';
|
import I18nManager from './apis/I18nManager';
|
||||||
import InteractionManager from './apis/InteractionManager';
|
import InteractionManager from './apis/InteractionManager';
|
||||||
|
import Linking from './apis/Linking';
|
||||||
import NetInfo from './apis/NetInfo';
|
import NetInfo from './apis/NetInfo';
|
||||||
import PanResponder from './apis/PanResponder';
|
import PanResponder from './apis/PanResponder';
|
||||||
import PixelRatio from './apis/PixelRatio';
|
import PixelRatio from './apis/PixelRatio';
|
||||||
@@ -23,6 +25,7 @@ import Vibration from './apis/Vibration';
|
|||||||
|
|
||||||
// components
|
// components
|
||||||
import ActivityIndicator from './components/ActivityIndicator';
|
import ActivityIndicator from './components/ActivityIndicator';
|
||||||
|
import Button from './components/Button';
|
||||||
import Image from './components/Image';
|
import Image from './components/Image';
|
||||||
import ListView from './components/ListView';
|
import ListView from './components/ListView';
|
||||||
import ProgressBar from './components/ProgressBar';
|
import ProgressBar from './components/ProgressBar';
|
||||||
@@ -56,10 +59,12 @@ const ReactNative = {
|
|||||||
AppRegistry,
|
AppRegistry,
|
||||||
AppState,
|
AppState,
|
||||||
AsyncStorage,
|
AsyncStorage,
|
||||||
|
Clipboard,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
Easing,
|
Easing,
|
||||||
I18nManager,
|
I18nManager,
|
||||||
InteractionManager,
|
InteractionManager,
|
||||||
|
Linking,
|
||||||
NetInfo,
|
NetInfo,
|
||||||
PanResponder,
|
PanResponder,
|
||||||
PixelRatio,
|
PixelRatio,
|
||||||
@@ -70,6 +75,7 @@ const ReactNative = {
|
|||||||
|
|
||||||
// components
|
// components
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
Button,
|
||||||
Image,
|
Image,
|
||||||
ListView,
|
ListView,
|
||||||
ProgressBar,
|
ProgressBar,
|
||||||
|
|||||||
@@ -12,14 +12,7 @@ exports[`modules/createDOMElement prop "accessibilityLiveRegion" 1`] = `
|
|||||||
style={Object {}} />
|
style={Object {}} />
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`modules/createDOMElement prop "accessibilityRole" 1`] = `
|
exports[`modules/createDOMElement prop "accessibilityRole" button 1`] = `
|
||||||
<header
|
|
||||||
className=""
|
|
||||||
role="banner"
|
|
||||||
style={Object {}} />
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`modules/createDOMElement prop "accessibilityRole" 2`] = `
|
|
||||||
<button
|
<button
|
||||||
className=""
|
className=""
|
||||||
role="button"
|
role="button"
|
||||||
@@ -27,6 +20,22 @@ exports[`modules/createDOMElement prop "accessibilityRole" 2`] = `
|
|||||||
type="button" />
|
type="button" />
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`modules/createDOMElement prop "accessibilityRole" link and target="_blank" 1`] = `
|
||||||
|
<a
|
||||||
|
className=""
|
||||||
|
rel=" noopener noreferrer"
|
||||||
|
role="link"
|
||||||
|
style={Object {}}
|
||||||
|
target="_blank" />
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`modules/createDOMElement prop "accessibilityRole" roles 1`] = `
|
||||||
|
<header
|
||||||
|
className=""
|
||||||
|
role="banner"
|
||||||
|
style={Object {}} />
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`modules/createDOMElement prop "accessible" 1`] = `
|
exports[`modules/createDOMElement prop "accessible" 1`] = `
|
||||||
<span
|
<span
|
||||||
className=""
|
className=""
|
||||||
|
|||||||
@@ -23,12 +23,21 @@ describe('modules/createDOMElement', () => {
|
|||||||
expect(component.toJSON()).toMatchSnapshot();
|
expect(component.toJSON()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('prop "accessibilityRole"', () => {
|
describe('prop "accessibilityRole"', () => {
|
||||||
const accessibilityRole = 'banner';
|
test('roles', () => {
|
||||||
let component = renderer.create(createDOMElement('span', { accessibilityRole }));
|
const component = renderer.create(createDOMElement('span', { accessibilityRole: 'banner' }));
|
||||||
expect(component.toJSON()).toMatchSnapshot();
|
expect(component.toJSON()).toMatchSnapshot();
|
||||||
component = renderer.create(createDOMElement('span', { accessibilityRole: 'button' }));
|
});
|
||||||
expect(component.toJSON()).toMatchSnapshot();
|
|
||||||
|
test('button', () => {
|
||||||
|
const component = renderer.create(createDOMElement('span', { accessibilityRole: 'button' }));
|
||||||
|
expect(component.toJSON()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('link and target="_blank"', () => {
|
||||||
|
const component = renderer.create(createDOMElement('span', { accessibilityRole: 'link', target: '_blank' }));
|
||||||
|
expect(component.toJSON()).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('prop "accessible"', () => {
|
test('prop "accessible"', () => {
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ const createDOMElement = (component, rnProps = {}) => {
|
|||||||
domProps.role = accessibilityRole;
|
domProps.role = accessibilityRole;
|
||||||
if (accessibilityRole === 'button') {
|
if (accessibilityRole === 'button') {
|
||||||
domProps.type = 'button';
|
domProps.type = 'button';
|
||||||
|
} else if (accessibilityRole === 'link' && domProps.target === '_blank') {
|
||||||
|
domProps.rel = `${domProps.rel || ''} noopener noreferrer`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type) { domProps.type = type; }
|
if (type) { domProps.type = type; }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const CSS_UNIT_RE = /^[+-]?\d*(?:\.\d+)?(?:[Ee][+-]?\d+)?(\w*)/;
|
const CSS_UNIT_RE = /^[+-]?\d*(?:\.\d+)?(?:[Ee][+-]?\d+)?(%|\w*)/;
|
||||||
|
|
||||||
const getUnit = (str) => str.match(CSS_UNIT_RE)[1];
|
const getUnit = (str) => str.match(CSS_UNIT_RE)[1];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user