279 lines
7.3 KiB
JavaScript
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;
|