/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow * @format */ 'use strict'; import type {Item} from '../../components/ListExampleShared'; import type {SectionBase} from 'react-native'; import { FooterComponent, HeaderComponent, ItemComponent, PlainInput, SeparatorComponent, Spindicator, genNewerItems, pressItem, renderSmallSwitchOption, renderStackedItem, } from '../../components/ListExampleShared'; import RNTesterPage from '../../components/RNTesterPage'; import RNTesterText from '../../components/RNTesterText'; import React from 'react'; import {useRef, useState} from 'react'; import { Alert, Animated, Button, SectionList, StyleSheet, Text, View, } from 'react-native'; const VIEWABILITY_CONFIG = { minimumViewTime: 3000, viewAreaCoveragePercentThreshold: 100, waitForInteraction: true, }; const CONSTANT_SECTION_EXAMPLES = [ { 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', }, ], }, ]; /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ const renderSectionHeader = ({section}) => ( SECTION HEADER: {section.key} ); /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ const renderSectionFooter = ({section}) => ( SECTION FOOTER: {section.key} ); /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ const CustomSeparatorComponent = ({highlighted, text}) => ( {text} ); const EmptySectionList = () => ( This is rendered when the list is empty ); const renderItemComponent = (setItemState: (item: Item) => void) => /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ ({item, separators}) => { if (isNaN(item.key)) { return; } const onPress = () => { const updatedItem = pressItem(item); setItemState(updatedItem); }; return ( ); }; const onScrollToIndexFailed = (info: { index: number, highestMeasuredFrameIndex: number, averageItemLength: number, ... }) => { console.warn('onScrollToIndexFailed. See comment in callback', info); /** * scrollToLocation() can only scroll to viewable area. * For any failure cases this callback will get triggered with `info` object * * The idea is to calculate a yPosition from `info` to call scrollResponder.scrollTo on. * * const scrollResponder = ref.current?.getScrollResponder(); * const positionY = some value we calculate from `info`; * if (scrollResponder != null) { * scrollResponder.scrollTo({x, y:positionY, animated: true}); * } */ }; // $FlowFixMe[missing-local-annot] const ItemSeparatorComponent = info => ( ); // $FlowFixMe[missing-local-annot] const SectionSeparatorComponent = info => ( ); export function SectionList_scrollable(Props: {...}): React.MixedElement { const scrollPos = new Animated.Value(0); const scrollSinkY = Animated.event( [{nativeEvent: {contentOffset: {y: scrollPos}}}], {useNativeDriver: true}, ); const [filterText, setFilterText] = useState(''); const [virtualized, setVirtualized] = useState(true); const [logViewable, setLogViewable] = useState(false); const [debug, setDebug] = useState(false); const [inverted, setInverted] = useState(false); const [data, setData] = useState(genNewerItems(1000)); const filterRegex = new RegExp(String(filterText), 'i'); const filter = (item: Item) => filterRegex.test(item.text) || filterRegex.test(item.title); const filteredData = data.filter(filter); const filteredSectionData = [...CONSTANT_SECTION_EXAMPLES]; let startIndex = 0; const endIndex = filteredData.length - 1; for (let ii = 10; ii <= endIndex + 10; ii += 10) { // $FlowFixMe[incompatible-type] filteredSectionData.push({ key: `${filteredData[startIndex].key} - ${ filteredData[Math.min(ii - 1, endIndex)].key }`, data: filteredData.slice(startIndex, ii), }); startIndex = ii; } const setItemPress = (item: Item) => { if (isNaN(item.key)) { return; } const index = Number(item.key); setData([...data.slice(0, index), item, ...data.slice(index + 1)]); }; const ref = useRef>>(null); const scrollToLocation = (sectionIndex: number, itemIndex: number) => { // $FlowFixMe[method-unbinding] added when improving typing for this parameters if (ref != null && ref.current?.scrollToLocation != null) { ref.current.scrollToLocation({sectionIndex, itemIndex}); } }; const onViewableItemsChanged = (info: { changed: Array<{ key: string, isViewable: boolean, item: {columns: Array, ...}, index: ?number, section?: any, ... }>, ... }) => { // Impressions can be logged here if (logViewable) { console.log( 'onViewableItemsChanged: ', info.changed.map((v: Object) => ({ ...v, item: '...', section: v.section.key, })), ); } }; return ( setFilterText(text)} placeholder="Search..." value={filterText} /> {renderSmallSwitchOption('Virtualized', virtualized, setVirtualized)} {renderSmallSwitchOption('Log Viewable', logViewable, setLogViewable)} {renderSmallSwitchOption('Debug', debug, setDebug)} {renderSmallSwitchOption('Inverted', inverted, setInverted)} scroll to: