diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 5232796bd3f3..9dc3e4aa2c7e 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -1,4 +1,5 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native'; +import isEmpty from 'lodash/isEmpty'; import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {LayoutChangeEvent, SectionList as RNSectionList, TextInput as RNTextInput, SectionListRenderItemInfo} from 'react-native'; @@ -10,6 +11,7 @@ import FixedFooter from '@components/FixedFooter'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; import SafeAreaConsumer from '@components/SafeAreaConsumer'; import SectionList from '@components/SectionList'; +import ShowMoreButton from '@components/ShowMoreButton'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useActiveElementRole from '@hooks/useActiveElementRole'; @@ -78,6 +80,9 @@ function BaseSelectionList( const isFocused = useIsFocused(); const [maxToRenderPerBatch, setMaxToRenderPerBatch] = useState(shouldUseDynamicMaxToRenderPerBatch ? 0 : CONST.MAX_TO_RENDER_PER_BATCH.DEFAULT); const [isInitialSectionListRender, setIsInitialSectionListRender] = useState(true); + const [currentPage, setCurrentPage] = useState(1); + + const incrementPage = () => setCurrentPage((prev) => prev + 1); /** * Iterates through the sections and items inside each section, and builds 3 arrays along the way: @@ -153,6 +158,33 @@ function BaseSelectionList( // If `initiallyFocusedOptionKey` is not passed, we fall back to `-1`, to avoid showing the highlight on the first member const [focusedIndex, setFocusedIndex] = useState(() => flattenedSections.allOptions.findIndex((option) => option.keyForList === initiallyFocusedOptionKey)); + const [slicedSections, ShowMoreButtonInstance] = useMemo( + () => [ + sections.map((section) => { + if (isEmpty(section.data)) { + return section; + } + + return { + ...section, + data: section.data.slice(0, CONST.MAX_OPTIONS_SELECTOR_PAGE_LENGTH * currentPage), + }; + }), + flattenedSections.allOptions.length > CONST.MAX_OPTIONS_SELECTOR_PAGE_LENGTH * currentPage ? ( + + ) : null, + ], + // we don't need to add styles here as they change + // we don't need to add flattendedSections here as they will change along with sections + // eslint-disable-next-line react-hooks/exhaustive-deps + [sections, currentPage], + ); + // Disable `Enter` shortcut if the active element is a button or checkbox const disableEnterShortcut = activeElementRole && [CONST.ROLE.BUTTON, CONST.ROLE.CHECKBOX].includes(activeElementRole as ButtonOrCheckBoxRoles); @@ -361,6 +393,8 @@ function BaseSelectionList( } // Remove the focus if the search input is empty else focus on the first non disabled item const newSelectedIndex = textInputValue === '' ? -1 : 0; + // reseting the currrent page to 1 when the user types something + setCurrentPage(1); updateAndScrollToFocusedIndex(newSelectedIndex); }, [canSelectMultiple, flattenedSections.allOptions.length, prevTextInputValue, textInputValue, updateAndScrollToFocusedIndex]); @@ -450,7 +484,7 @@ function BaseSelectionList( {!headerMessage && !canSelectMultiple && customListHeader} ( testID="selection-list" onLayout={onSectionListLayout} style={(!maxToRenderPerBatch || isInitialSectionListRender) && styles.opacity0} + ListFooterComponent={ShowMoreButtonInstance} /> {children}