/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule SectionList * @flow * @format */ 'use strict'; const MetroListView = require('MetroListView'); const Platform = require('Platform'); const React = require('React'); const VirtualizedSectionList = require('VirtualizedSectionList'); import type {ViewToken} from 'ViewabilityHelper'; import type {Props as VirtualizedSectionListProps} from 'VirtualizedSectionList'; type Item = any; type SectionBase = { /** * The data for rendering items in this section. */ data: $ReadOnlyArray, /** * Optional key to keep track of section re-ordering. If you don't plan on re-ordering sections, * the array index will be used by default. */ key?: string, // Optional props will override list-wide props just for this section. renderItem?: ?(info: { item: SectionItemT, index: number, section: SectionBase, separators: { highlight: () => void, unhighlight: () => void, updateProps: (select: 'leading' | 'trailing', newProps: Object) => void, }, }) => ?React.Element, ItemSeparatorComponent?: ?ReactClass, keyExtractor?: (item: SectionItemT) => string, // TODO: support more optional/override props // onViewableItemsChanged?: ... }; type RequiredProps> = { /** * The actual data to render, akin to the `data` prop in [``](/react-native/docs/flatlist.html). * * General shape: * * sections: $ReadOnlyArray<{ * data: $ReadOnlyArray, * renderItem?: ({item: SectionItem, ...}) => ?React.Element<*>, * ItemSeparatorComponent?: ?ReactClass<{highlighted: boolean, ...}>, * }> */ sections: $ReadOnlyArray, }; type OptionalProps> = { /** * Default renderer for every item in every section. Can be over-ridden on a per-section basis. */ renderItem: (info: { item: Item, index: number, section: SectionT, separators: { highlight: () => void, unhighlight: () => void, updateProps: (select: 'leading' | 'trailing', newProps: Object) => void, }, }) => ?React.Element, /** * Rendered in between each item, but not at the top or bottom. By default, `highlighted`, * `section`, and `[leading/trailing][Item/Separator]` props are provided. `renderItem` provides * `separators.highlight`/`unhighlight` which will update the `highlighted` prop, but you can also * add custom props with `separators.updateProps`. */ ItemSeparatorComponent?: ?ReactClass, /** * Rendered at the very beginning of the list. Can be a React Component Class, a render function, or * a rendered element. */ ListHeaderComponent?: ?(ReactClass | React.Element), /** * Rendered when the list is empty. Can be a React Component Class, a render function, or * a rendered element. */ ListEmptyComponent?: ?(ReactClass | React.Element), /** * Rendered at the very end of the list. Can be a React Component Class, a render function, or * a rendered element. */ ListFooterComponent?: ?(ReactClass | React.Element), /** * Rendered at the top and bottom of each section (note this is different from * `ItemSeparatorComponent` which is only rendered between items). These are intended to separate * sections from the headers above and below and typically have the same highlight response as * `ItemSeparatorComponent`. Also receives `highlighted`, `[leading/trailing][Item/Separator]`, * and any custom props from `separators.updateProps`. */ SectionSeparatorComponent?: ?ReactClass, /** * A marker property for telling the list to re-render (since it implements `PureComponent`). If * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the * `data` prop, stick it here and treat it immutably. */ extraData?: any, /** * How many items to render in the initial batch. This should be enough to fill the screen but not * much more. Note these items will never be unmounted as part of the windowed rendering in order * to improve perceived performance of scroll-to-top actions. */ initialNumToRender: number, /** * Reverses the direction of scroll. Uses scale transforms of -1. */ inverted?: ?boolean, /** * Used to extract a unique key for a given item at the specified index. Key is used for caching * and as the react key to track item re-ordering. The default extractor checks item.key, then * falls back to using the index, like react does. Note that this sets keys for each item, but * each overall section still needs its own key. */ keyExtractor: (item: Item, index: number) => string, /** * Called once when the scroll position gets within `onEndReachedThreshold` of the rendered * content. */ onEndReached?: ?(info: {distanceFromEnd: number}) => void, /** * How far from the end (in units of visible length of the list) the bottom edge of the * list must be from the end of the content to trigger the `onEndReached` callback. * Thus a value of 0.5 will trigger `onEndReached` when the end of the content is * within half the visible length of the list. */ onEndReachedThreshold?: ?number, /** * If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make * sure to also set the `refreshing` prop correctly. */ onRefresh?: ?() => void, /** * Called when the viewability of rows changes, as defined by the * `viewabilityConfig` prop. */ onViewableItemsChanged?: ?(info: { viewableItems: Array, changed: Array, }) => void, /** * Set this true while waiting for new data from a refresh. */ refreshing?: ?boolean, /** * Note: may have bugs (missing content) in some circumstances - use at your own risk. * * This may improve scroll performance for large lists. */ removeClippedSubviews?: boolean, /** * Rendered at the top of each section. These stick to the top of the `ScrollView` by default on * iOS. See `stickySectionHeadersEnabled`. */ renderSectionHeader?: ?(info: {section: SectionT}) => ?React.Element, /** * Rendered at the bottom of each section. */ renderSectionFooter?: ?(info: {section: SectionT}) => ?React.Element, /** * Makes section headers stick to the top of the screen until the next one pushes it off. Only * enabled by default on iOS because that is the platform standard there. */ stickySectionHeadersEnabled?: boolean, legacyImplementation?: ?boolean, }; type Props = RequiredProps & OptionalProps & VirtualizedSectionListProps; const defaultProps = { ...VirtualizedSectionList.defaultProps, stickySectionHeadersEnabled: Platform.OS === 'ios', }; type DefaultProps = typeof defaultProps; /** * A performant interface for rendering sectioned lists, supporting the most handy features: * * - Fully cross-platform. * - Configurable viewability callbacks. * - List header support. * - List footer support. * - Item separator support. * - Section header support. * - Section separator support. * - Heterogeneous data and item rendering support. * - Pull to Refresh. * - Scroll loading. * * If you don't need section support and want a simpler interface, use * [``](/react-native/docs/flatlist.html). * * Simple Examples: * * } * renderSectionHeader={({section}) =>

} * sections={[ // homogenous rendering between sections * {data: [...], title: ...}, * {data: [...], title: ...}, * {data: [...], title: ...}, * ]} * /> * * * * This is a convenience wrapper around [``](docs/virtualizedlist.html), * and thus inherits its props (as well as those of `ScrollView`) that aren't explicitly listed * here, along with the following caveats: * * - Internal state is not preserved when content scrolls out of the render window. Make sure all * your data is captured in the item data or external stores like Flux, Redux, or Relay. * - This is a `PureComponent` which means that it will not re-render if `props` remain shallow- * equal. Make sure that everything your `renderItem` function depends on is passed as a prop * (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on * changes. This includes the `data` prop and parent component state. * - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously * offscreen. This means it's possible to scroll faster than the fill rate and momentarily see * blank content. This is a tradeoff that can be adjusted to suit the needs of each application, * and we are working on improving it behind the scenes. * - By default, the list looks for a `key` prop on each item and uses that for the React key. * Alternatively, you can provide a custom `keyExtractor` prop. * */ class SectionList> extends React.PureComponent< DefaultProps, Props, void, > { props: Props; static defaultProps: DefaultProps = defaultProps; /** * Scrolls to the item at the specified `sectionIndex` and `itemIndex` (within the section) * positioned in the viewable area such that `viewPosition` 0 places it at the top (and may be * covered by a sticky header), 1 at the bottom, and 0.5 centered in the middle. `viewOffset` is a * fixed number of pixels to offset the final target position, e.g. to compensate for sticky * headers. * * Note: cannot scroll to locations outside the render window without specifying the * `getItemLayout` prop. */ scrollToLocation(params: { animated?: ?boolean, itemIndex: number, sectionIndex: number, viewOffset?: number, viewPosition?: number, }) { this._wrapperListRef.scrollToLocation(params); } /** * Tells the list an interaction has occured, which should trigger viewability calculations, e.g. * if `waitForInteractions` is true and the user has not scrolled. This is typically called by * taps on items or by navigation actions. */ recordInteraction() { const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); listRef && listRef.recordInteraction(); } /** * Displays the scroll indicators momentarily. * * @platform ios */ flashScrollIndicators() { const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); listRef && listRef.flashScrollIndicators(); } /** * Provides a handle to the underlying scroll responder. */ getScrollResponder() { const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); if (listRef) { return listRef.getScrollResponder(); } } getScrollableNode() { const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); if (listRef) { return listRef.getScrollableNode(); } } render() { const List = this.props.legacyImplementation ? MetroListView : VirtualizedSectionList; return ; } _wrapperListRef: MetroListView | VirtualizedSectionList; _captureRef = ref => { this._wrapperListRef = ref; }; } module.exports = SectionList;