mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-30 23:23:35 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
023ca27a22 |
@@ -70,7 +70,6 @@ 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)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,33 +0,0 @@
|
||||
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 I18nManagerExample extends Component {
|
||||
class RTLExample extends Component {
|
||||
componentWillUnmount() {
|
||||
I18nManager.setPreferredLanguageRTL(false)
|
||||
}
|
||||
@@ -75,5 +75,5 @@ const styles = StyleSheet.create({
|
||||
|
||||
storiesOf('api: I18nManager', module)
|
||||
.add('RTL layout', () => (
|
||||
<I18nManagerExample />
|
||||
<RTLExample />
|
||||
))
|
||||
@@ -1,80 +1,4 @@
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@kadira/storybook';
|
||||
import { ListView, StyleSheet, Text, View } from 'react-native';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { ListView } 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 style={{ alignItems: 'center' }}>
|
||||
<View>
|
||||
<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, { maxWidth: 200 } ]}
|
||||
style={styles.default}
|
||||
/>
|
||||
<Text style={styles.eventLabel}>
|
||||
{this.state.curText}{'\n'}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.58",
|
||||
"description": "React Native for Web",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
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;
|
||||
@@ -2,27 +2,18 @@ import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import ListViewDataSource from './ListViewDataSource';
|
||||
import ListViewPropTypes from './ListViewPropTypes';
|
||||
import ScrollView from '../ScrollView';
|
||||
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;
|
||||
import View from '../View';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class ListView extends Component {
|
||||
static propTypes = ListViewPropTypes;
|
||||
|
||||
static defaultProps = {
|
||||
initialListSize: DEFAULT_INITIAL_ROWS,
|
||||
pageSize: DEFAULT_PAGE_SIZE,
|
||||
initialListSize: 10,
|
||||
pageSize: 1,
|
||||
renderScrollComponent: (props) => <ScrollView {...props} />,
|
||||
scrollRenderAheadDistance: DEFAULT_SCROLL_RENDER_AHEAD,
|
||||
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD,
|
||||
scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE,
|
||||
removeClippedSubviews: true,
|
||||
scrollRenderAheadDistance: 1000,
|
||||
onEndReachedThreshold: 1000,
|
||||
stickyHeaderIndices: []
|
||||
};
|
||||
|
||||
@@ -35,34 +26,6 @@ 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();
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
requestAnimationFrame(() => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
});
|
||||
}
|
||||
|
||||
getScrollResponder() {
|
||||
@@ -77,313 +40,58 @@ 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 children = [];
|
||||
|
||||
const dataSource = this.props.dataSource;
|
||||
const allRowIDs = dataSource.rowIdentities;
|
||||
let rowCount = 0;
|
||||
const sectionHeaderIndices = [];
|
||||
const header = this.props.renderHeader ? this.props.renderHeader() : undefined;
|
||||
const footer = this.props.renderFooter ? this.props.renderFooter() : undefined;
|
||||
|
||||
const header = this.props.renderHeader && this.props.renderHeader();
|
||||
const footer = this.props.renderFooter && this.props.renderFooter();
|
||||
let totalIndex = header ? 1 : 0;
|
||||
// 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];
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
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)) {
|
||||
// render optional separator
|
||||
if (renderSeparator && ((rowIdx !== rows.length - 1) || (sectionIdx === sections.length - 1))) {
|
||||
const adjacentRowHighlighted =
|
||||
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++;
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (++rowCount === this.state.curRenderedRowsCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rowCount >= this.state.curRenderedRowsCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
return React.cloneElement(this.props.renderScrollComponent(this.props), {
|
||||
ref: this._setScrollViewRef
|
||||
}, 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;
|
||||
}
|
||||
|
||||
@@ -236,6 +236,7 @@ const styles = StyleSheet.create({
|
||||
overflowY: 'hidden'
|
||||
},
|
||||
contentContainer: {
|
||||
flexGrow: 1,
|
||||
transform: [ { translateZ: 0 } ]
|
||||
},
|
||||
contentContainerHorizontal: {
|
||||
|
||||
@@ -270,6 +270,7 @@ const styles = StyleSheet.create({
|
||||
borderWidth: 0,
|
||||
boxSizing: 'border-box',
|
||||
color: 'inherit',
|
||||
flex: 1,
|
||||
font: 'inherit',
|
||||
padding: 0
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import Animated from './apis/Animated';
|
||||
import AppRegistry from './apis/AppRegistry';
|
||||
import AppState from './apis/AppState';
|
||||
import AsyncStorage from './apis/AsyncStorage';
|
||||
import Clipboard from './apis/Clipboard';
|
||||
import Dimensions from './apis/Dimensions';
|
||||
import Easing from 'animated/lib/Easing';
|
||||
import I18nManager from './apis/I18nManager';
|
||||
@@ -59,7 +58,6 @@ const ReactNative = {
|
||||
AppRegistry,
|
||||
AppState,
|
||||
AsyncStorage,
|
||||
Clipboard,
|
||||
Dimensions,
|
||||
Easing,
|
||||
I18nManager,
|
||||
|
||||
Reference in New Issue
Block a user