react-native/RNTester/js/SectionListExample.js

279 lines
7.3 KiB
JavaScript

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {Alert, Animated, Button, StyleSheet, Text, View} = ReactNative;
const RNTesterPage = require('./RNTesterPage');
const infoLog = require('infoLog');
const {
HeaderComponent,
FooterComponent,
ItemComponent,
PlainInput,
SeparatorComponent,
Spindicator,
genItemData,
pressItem,
renderSmallSwitchOption,
renderStackedItem,
} = require('./ListExampleShared');
const VIEWABILITY_CONFIG = {
minimumViewTime: 3000,
viewAreaCoveragePercentThreshold: 100,
waitForInteraction: true,
};
const renderSectionHeader = ({section}) => (
<View style={styles.header}>
<Text style={styles.headerText}>SECTION HEADER: {section.key}</Text>
<SeparatorComponent />
</View>
);
const renderSectionFooter = ({section}) => (
<View style={styles.header}>
<Text style={styles.headerText}>SECTION FOOTER: {section.key}</Text>
<SeparatorComponent />
</View>
);
const CustomSeparatorComponent = ({highlighted, text}) => (
<View
style={[
styles.customSeparator,
highlighted && {backgroundColor: 'rgb(217, 217, 217)'},
]}>
<Text style={styles.separatorText}>{text}</Text>
</View>
);
class SectionListExample extends React.PureComponent<{}, $FlowFixMeState> {
static title = '<SectionList>';
static description = 'Performant, scrollable list of data.';
state = {
data: genItemData(1000),
debug: false,
filterText: '',
logViewable: false,
virtualized: true,
inverted: false,
};
_scrollPos = new Animated.Value(0);
_scrollSinkY = Animated.event(
[{nativeEvent: {contentOffset: {y: this._scrollPos}}}],
{useNativeDriver: true},
);
_sectionListRef: Animated.SectionList;
_captureRef = ref => {
this._sectionListRef = ref;
};
_scrollToLocation(sectionIndex: number, itemIndex: number) {
this._sectionListRef.getNode().scrollToLocation({sectionIndex, itemIndex});
}
render() {
const filterRegex = new RegExp(String(this.state.filterText), 'i');
const filter = item =>
filterRegex.test(item.text) || filterRegex.test(item.title);
const filteredData = this.state.data.filter(filter);
const filteredSectionData = [];
let startIndex = 0;
const endIndex = filteredData.length - 1;
for (let ii = 10; ii <= endIndex + 10; ii += 10) {
filteredSectionData.push({
key: `${filteredData[startIndex].key} - ${
filteredData[Math.min(ii - 1, endIndex)].key
}`,
data: filteredData.slice(startIndex, ii),
});
startIndex = ii;
}
return (
<RNTesterPage noSpacer={true} noScroll={true}>
<View style={styles.searchRow}>
<PlainInput
onChangeText={filterText => {
this.setState(() => ({filterText}));
}}
placeholder="Search..."
value={this.state.filterText}
/>
<View style={styles.optionSection}>
{renderSmallSwitchOption(this, 'virtualized')}
{renderSmallSwitchOption(this, 'logViewable')}
{renderSmallSwitchOption(this, 'debug')}
{renderSmallSwitchOption(this, 'inverted')}
<Spindicator value={this._scrollPos} />
</View>
<View style={styles.scrollToRow}>
<Text>scroll to:</Text>
<Button
title="Item A"
onPress={() => this._scrollToLocation(2, 1)}
/>
<Button
title="Item B"
onPress={() => this._scrollToLocation(3, 6)}
/>
<Button
title="Item C"
onPress={() => this._scrollToLocation(6, 3)}
/>
</View>
</View>
<SeparatorComponent />
<Animated.SectionList
ref={this._captureRef}
ListHeaderComponent={HeaderComponent}
ListFooterComponent={FooterComponent}
SectionSeparatorComponent={info => (
<CustomSeparatorComponent {...info} text="SECTION SEPARATOR" />
)}
ItemSeparatorComponent={info => (
<CustomSeparatorComponent {...info} text="ITEM SEPARATOR" />
)}
debug={this.state.debug}
inverted={this.state.inverted}
enableVirtualization={this.state.virtualized}
onRefresh={() => Alert.alert('onRefresh: nothing to refresh :P')}
onScroll={this._scrollSinkY}
onViewableItemsChanged={this._onViewableItemsChanged}
refreshing={false}
renderItem={this._renderItemComponent}
renderSectionHeader={renderSectionHeader}
renderSectionFooter={renderSectionFooter}
stickySectionHeadersEnabled
sections={[
{
key: 'empty section',
data: [],
},
{
renderItem: renderStackedItem,
key: 's1',
data: [
{
title: 'Item In Header Section',
text: 'Section s1',
key: 'header item',
},
],
},
{
key: 's2',
data: [
{
noImage: true,
title: '1st item',
text: 'Section s2',
key: 'noimage0',
},
{
noImage: true,
title: '2nd item',
text: 'Section s2',
key: 'noimage1',
},
],
},
...filteredSectionData,
]}
style={styles.list}
viewabilityConfig={VIEWABILITY_CONFIG}
/>
</RNTesterPage>
);
}
_renderItemComponent = ({item, separators}) => (
<ItemComponent
item={item}
onPress={this._pressItem}
onHideUnderlay={separators.unhighlight}
onShowUnderlay={separators.highlight}
/>
);
// This is called when items change viewability by scrolling into our out of
// the viewable area.
_onViewableItemsChanged = (info: {
changed: Array<{
key: string,
isViewable: boolean,
item: {columns: Array<*>},
index: ?number,
section?: any,
}>,
}) => {
// Impressions can be logged here
if (this.state.logViewable) {
infoLog(
'onViewableItemsChanged: ',
info.changed.map((v: Object) => ({
...v,
item: '...',
section: v.section.key,
})),
);
}
};
_pressItem = (key: string) => {
!isNaN(key) && pressItem(this, key);
};
}
const styles = StyleSheet.create({
customSeparator: {
backgroundColor: 'rgb(200, 199, 204)',
},
header: {
backgroundColor: '#e9eaed',
},
headerText: {
padding: 4,
fontWeight: '600',
},
list: {
backgroundColor: 'white',
},
optionSection: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
},
searchRow: {
paddingHorizontal: 10,
},
scrollToRow: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 8,
},
separatorText: {
color: 'gray',
alignSelf: 'center',
fontSize: 7,
},
});
module.exports = SectionListExample;