From 1917cb3de99750bd0756df9f35679a530e69e3a4 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 26 May 2026 08:52:34 -0400 Subject: [PATCH] Revert "Merge pull request #90029 from bernhardoj/feat/84877-update-filters-chip-to-open-filters-popup" This reverts commit d9d6c94db23efe9a55f442cc468b505df103fe9a, reversing changes made to b2b05f1a1a1aa2f3950cc9ab7f32161d28aa7da3. --- assets/images/coins.svg | 2 +- assets/images/credit-card-multiple.svg | 1 - .../multifactorAuthentication/fingerprint.svg | 2 +- assets/images/paycheck.svg | 1 - assets/images/user-arrow-left.svg | 1 - src/CONST/index.ts | 4 - .../Icon/chunks/expensify-icons.chunk.ts | 6 - .../PopoverWithMeasuredContent/index.tsx | 4 +- .../PopoverWithMeasuredContent/types.ts | 4 - src/components/SafeTriangle/index.native.tsx | 11 - src/components/SafeTriangle/index.tsx | 194 --------------- src/components/SafeTriangle/types.ts | 8 - .../Search/FilterComponents/CardSelector.tsx | 72 +++--- .../FilterComponents/CategorySelector.tsx | 9 +- .../FilterComponents/CurrencySelector.tsx | 9 +- .../FilterComponents/ExportedToSelector.tsx | 9 +- .../Search/FilterComponents/FeedSelector.tsx | 7 +- .../Search/FilterComponents/InSelector.tsx | 14 +- .../Search/FilterComponents/MultiSelect.tsx | 29 +-- .../ReportField/ReportFieldList.tsx | 12 +- .../ReportField/ReportFieldText.tsx | 4 +- .../FilterComponents/ReportField/index.tsx | 24 +- .../Search/FilterComponents/SingleSelect.tsx | 27 +-- .../Search/FilterComponents/TagSelector.tsx | 9 +- .../FilterComponents/TaxRateSelector.tsx | 9 +- .../Search/FilterComponents/TypeSelector.tsx | 14 +- .../Search/FilterComponents/UserSelector.tsx | 14 +- .../FilterComponents/WorkspaceSelector.tsx | 10 +- .../Search/FilterComponents/index.tsx | 72 +----- .../AdvancedFiltersFullscreen.tsx | 100 -------- .../AdvancedFilters/AdvancedFiltersPopup.tsx | 53 ---- .../AdvancedFilters/AmountFilterComponent.tsx | 229 ------------------ .../AdvancedFilters/DateFilterComponent.tsx | 97 -------- .../AdvancedFilters/FilterItem.tsx | 75 ------ .../AdvancedFilters/FilterList.tsx | 53 ---- .../ReportFieldFilterComponent.tsx | 77 ------ .../AdvancedFilters/SelectedFilterContent.tsx | 176 -------------- .../FilterDropdowns/AdvancedFilters/index.tsx | 27 --- .../useFullscreenAdvancedFilters.tsx | 9 - .../Search/FilterDropdowns/AmountPopup.tsx | 8 +- .../Search/FilterDropdowns/CommonPopup.tsx | 2 +- .../Search/FilterDropdowns/DropdownButton.tsx | 203 +++++++++++++--- .../FilterDropdowns/FilterPopupButton.tsx | 156 ------------ .../SearchList/BaseSearchList/index.tsx | 11 +- .../DatePickerFilterPopup.tsx | 2 +- .../SearchAdvancedFiltersButton.tsx | 89 +++---- .../SearchDisplayDropdownButton.tsx | 44 ++-- .../SearchFiltersBarNarrow.tsx | 6 +- .../SearchPageHeader/SearchFiltersBarWide.tsx | 20 +- .../SearchFiltersClearButton.tsx | 39 --- .../SearchPageHeader/useSearchFiltersBar.tsx | 13 +- src/components/Search/types.ts | 12 - .../TableFilterButtons/buildFilterItems.tsx | 2 +- src/hooks/useAdvancedSearchFilters.ts | 131 +++++----- src/languages/de.ts | 8 +- src/languages/en.ts | 8 +- src/languages/es.ts | 8 +- src/languages/fr.ts | 8 +- src/languages/it.ts | 8 +- src/languages/ja.ts | 8 +- src/languages/nl.ts | 8 +- src/languages/pl.ts | 8 +- src/languages/pt-BR.ts | 8 +- src/languages/zh-hans.ts | 8 +- src/libs/SearchQueryUtils.ts | 28 --- src/libs/SearchUIUtils.ts | 220 ++++------------- src/pages/Search/AdvancedSearchFilters.tsx | 68 +++--- .../domain/Members/DomainMembersPage.tsx | 2 +- src/pages/workspace/WorkspaceMembersPage.tsx | 2 +- src/styles/index.ts | 31 +-- tests/unit/Search/SearchQueryUtilsTest.ts | 120 --------- 71 files changed, 501 insertions(+), 2266 deletions(-) delete mode 100644 assets/images/credit-card-multiple.svg delete mode 100644 assets/images/paycheck.svg delete mode 100644 assets/images/user-arrow-left.svg delete mode 100644 src/components/SafeTriangle/index.native.tsx delete mode 100644 src/components/SafeTriangle/index.tsx delete mode 100644 src/components/SafeTriangle/types.ts delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/AdvancedFiltersFullscreen.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/AdvancedFiltersPopup.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/AmountFilterComponent.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/DateFilterComponent.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/FilterItem.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/FilterList.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/ReportFieldFilterComponent.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/SelectedFilterContent.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/index.tsx delete mode 100644 src/components/Search/FilterDropdowns/AdvancedFilters/useFullscreenAdvancedFilters.tsx delete mode 100644 src/components/Search/FilterDropdowns/FilterPopupButton.tsx delete mode 100644 src/components/Search/SearchPageHeader/SearchFiltersClearButton.tsx diff --git a/assets/images/coins.svg b/assets/images/coins.svg index 3c12b4720526..3cf1c0e38204 100644 --- a/assets/images/coins.svg +++ b/assets/images/coins.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/images/credit-card-multiple.svg b/assets/images/credit-card-multiple.svg deleted file mode 100644 index 93573995e18a..000000000000 --- a/assets/images/credit-card-multiple.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/images/multifactorAuthentication/fingerprint.svg b/assets/images/multifactorAuthentication/fingerprint.svg index 6dbc03eb27e6..f6712ca78e14 100644 --- a/assets/images/multifactorAuthentication/fingerprint.svg +++ b/assets/images/multifactorAuthentication/fingerprint.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/images/paycheck.svg b/assets/images/paycheck.svg deleted file mode 100644 index 30e03272ec5d..000000000000 --- a/assets/images/paycheck.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/images/user-arrow-left.svg b/assets/images/user-arrow-left.svg deleted file mode 100644 index 987b13b3cbe6..000000000000 --- a/assets/images/user-arrow-left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/CONST/index.ts b/src/CONST/index.ts index fcc44cadff3e..fcc1a8fe04b0 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -276,9 +276,6 @@ const CONST = { POPOVER_DATE_RANGE_WIDTH: 672, POPOVER_DATE_MAX_HEIGHT: 366, POPOVER_DATE_MIN_HEIGHT: 322, - ADVANCED_FILTERS_POPOVER_HEIGHT: 520, - ADVANCED_FILTERS_POPOVER_WIDTH: 582, - ADVANCED_FILTERS_CONTENT_WIDTH: 331, TOOLTIP_ANIMATION_DURATION: 500, DROPDOWN_SCROLL_THRESHOLD: 5, // Multiplier for gyroscope animation in order to make it a bit more subtle @@ -9300,7 +9297,6 @@ const CONST = { ADVANCED_FILTER_ITEM: 'Search-AdvancedFilterItem', SAVE_SEARCH_BUTTON: 'Search-SaveSearchButton', VIEW_RESULTS_BUTTON: 'Search-ViewResultsButton', - CLEAR_FILTERS_BUTTON: 'Search-ClearFiltersButton', ACTION_CELL_VIEW: 'Search-ActionCellView', ACTION_CELL_PAY: 'Search-ActionCellPay', ACTION_CELL_ACTION: 'Search-ActionCellAction', diff --git a/src/components/Icon/chunks/expensify-icons.chunk.ts b/src/components/Icon/chunks/expensify-icons.chunk.ts index e531d321f8db..821fb37ef966 100644 --- a/src/components/Icon/chunks/expensify-icons.chunk.ts +++ b/src/components/Icon/chunks/expensify-icons.chunk.ts @@ -70,7 +70,6 @@ import Copy from '@assets/images/copy.svg'; import CreditCardExclamation from '@assets/images/credit-card-exclamation.svg'; import CreditCardHourglass from '@assets/images/credit-card-hourglass.svg'; import CreditCardLock from '@assets/images/credit-card-lock.svg'; -import CreditCardMultiple from '@assets/images/credit-card-multiple.svg'; import CreditCardWithPlaneHourglass from '@assets/images/credit-card-with-plane-hourglass.svg'; import CreditCardWithPlane from '@assets/images/credit-card-with-plane.svg'; import CreditCard from '@assets/images/creditcard.svg'; @@ -187,7 +186,6 @@ import OfflineCloud from '@assets/images/offline-cloud.svg'; import Offline from '@assets/images/offline.svg'; import Paperclip from '@assets/images/paperclip.svg'; import Pause from '@assets/images/pause.svg'; -import Paycheck from '@assets/images/paycheck.svg'; import Pencil from '@assets/images/pencil.svg'; import Percent from '@assets/images/percent.svg'; import Phone from '@assets/images/phone.svg'; @@ -251,7 +249,6 @@ import TreasureChest from '@assets/images/treasure-chest.svg'; import Unlock from '@assets/images/unlock.svg'; import UploadAlt from '@assets/images/upload-alt.svg'; import Upload from '@assets/images/upload.svg'; -import UserArrowLeft from '@assets/images/user-arrow-left.svg'; import UserCheck from '@assets/images/user-check.svg'; import UserEye from '@assets/images/user-eye.svg'; import UserLock from '@assets/images/user-lock.svg'; @@ -329,7 +326,6 @@ const Expensicons = { ConnectionComplete, Copy, CreditCard, - CreditCardMultiple, Crop, CreditCardHourglass, CreditCardExclamation, @@ -431,7 +427,6 @@ const Expensicons = { OdometerEnd, Paperclip, Pause, - Paycheck, Pencil, Percent, Phone, @@ -477,7 +472,6 @@ const Expensicons = { Upload, UploadAlt, User, - UserArrowLeft, UserCheck, Users, VideoSlash, diff --git a/src/components/PopoverWithMeasuredContent/index.tsx b/src/components/PopoverWithMeasuredContent/index.tsx index 29f369ab9da4..9c063dce4982 100644 --- a/src/components/PopoverWithMeasuredContent/index.tsx +++ b/src/components/PopoverWithMeasuredContent/index.tsx @@ -11,7 +11,7 @@ import type PopoverWithMeasuredContentProps from './types'; * This component is a perf optimization, it return BOTTOM_DOCKED early, for small screens avoiding Popover measurement logic calculations. * It defers rendering of PopoverWithMeasuredContentBase to idle time to avoid blocking more priority UI updates with measurements. */ -function PopoverWithMeasuredContent({shouldWrapModalChildrenInScrollViewIfBottomDockedInLandscapeMode, smallScreenModalType, ...props}: PopoverWithMeasuredContentProps) { +function PopoverWithMeasuredContent({shouldWrapModalChildrenInScrollViewIfBottomDockedInLandscapeMode, ...props}: PopoverWithMeasuredContentProps) { // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); @@ -32,7 +32,7 @@ function PopoverWithMeasuredContent({shouldWrapModalChildrenInScrollViewIfBottom return ( & { /** Whether to skip re-measurement when becoming visible (for components with static dimensions) */ shouldSkipRemeasurement?: boolean; - - /** The modal type for modal when rendered in small screen */ - smallScreenModalType?: BaseModalProps['type']; }; export default PopoverWithMeasuredContentProps; diff --git a/src/components/SafeTriangle/index.native.tsx b/src/components/SafeTriangle/index.native.tsx deleted file mode 100644 index 3e39c794c481..000000000000 --- a/src/components/SafeTriangle/index.native.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import type SafeTriangleProps from './types'; - -/** - * A component that provides a "safe triangle" wrapper. - * On native platforms, hover interactions are not applicable, so this is a no-op wrapper. - */ -function SafeTriangle({children}: SafeTriangleProps) { - return children; -} - -export default SafeTriangle; diff --git a/src/components/SafeTriangle/index.tsx b/src/components/SafeTriangle/index.tsx deleted file mode 100644 index 64ad15112770..000000000000 --- a/src/components/SafeTriangle/index.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import React, {useEffect, useRef, useState} from 'react'; -import {View} from 'react-native'; -import {Polygon, Svg} from 'react-native-svg'; -import useThemeStyles from '@hooks/useThemeStyles'; -import {isMobile} from '@libs/Browser'; -import htmlDivElementRef from '@src/types/utils/htmlDivElementRef'; -import type SafeTriangleProps from './types'; - -type Point = [number, number]; - -type SafeTriangleOverlayProps = { - submenuRef: React.RefObject; - containerRef: React.RefObject; -}; - -type Rect = { - top: number; - left: number; - width: number; - height: number; -}; - -/** Time in ms before the safe triangle is cleared after cursor stops moving toward submenu */ -const SAFE_TRIANGLE_CLEAR_DELAY_MS = 50; - -const OFFSET = 2; - -function isPointInPolygon(point: Point, polygon: Point[]) { - const [x, y] = point; - let isInside = false; - const length = polygon.length; - for (let i = 0, j = length - 1; i < length; j = i++) { - const [xi, yi] = polygon.at(i) ?? [0, 0]; - const [xj, yj] = polygon.at(j) ?? [0, 0]; - const intersect = yi >= y !== yj >= y && x <= ((xj - xi) * (y - yi)) / (yj - yi) + xi; - if (intersect) { - isInside = !isInside; - } - } - return isInside; -} - -function SafeTriangleOverlay({submenuRef, containerRef}: SafeTriangleOverlayProps) { - const styles = useThemeStyles(); - - const [points, setPoints] = useState(null); - const [svgRect, setSvgRect] = useState(null); - - const apexRef = useRef(null); - const lastCursorPosition = useRef(null); - const lastCursorTime = useRef(0); - const timeoutRef = useRef(undefined); - - const getCursorSpeed = (x: number, y: number): number | null => { - const currentTime = performance.now(); - const elapsedTime = currentTime - lastCursorTime.current; - - if (lastCursorPosition.current === null || elapsedTime === 0) { - lastCursorPosition.current = [x, y]; - lastCursorTime.current = currentTime; - return null; - } - - const deltaX = x - lastCursorPosition.current[0]; - const deltaY = y - lastCursorPosition.current[1]; - const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); - const speed = distance / elapsedTime; // px / ms - - lastCursorPosition.current = [x, y]; - lastCursorTime.current = currentTime; - - return speed; - }; - - const clearTriangle = () => { - setPoints(null); - setSvgRect(null); - apexRef.current = null; - }; - - const onMouseMove = (event: MouseEvent) => { - clearTimeout(timeoutRef.current); - - const {clientX, clientY} = event; - const speed = getCursorSpeed(clientX, clientY); - - if (!submenuRef.current) { - clearTriangle(); - return; - } - - const rect = submenuRef.current.getBoundingClientRect(); - if (!rect) { - clearTriangle(); - return; - } - - // If speed is slow, update the apex to the current cursor position - if (speed === null || speed < 0.1) { - apexRef.current = [clientX, clientY]; - } - - const [x, y] = apexRef.current ?? [clientX, clientY]; - - // Create a polygon from apex to the submenu's left edge - const cursorPoint: Point = [0, y - rect.top]; - // We subtract OFFSET from x-coordinates to account for the offset in the container's left style - const topLeftSubMenuPoint: Point = [rect.left - x - OFFSET, 0]; - const bottomLeftSubMenuPoint: Point = [rect.left - x - OFFSET, rect.bottom - rect.top]; - - const polygon = [cursorPoint, topLeftSubMenuPoint, bottomLeftSubMenuPoint]; - - // Check if the current mouse position is within the safe triangle - // The polygon points are relative to [x + OFFSET, rect.top], so we adjust the mouse position accordingly - const isSafe = isPointInPolygon([clientX - x + OFFSET, clientY - rect.top], polygon); - - if (isSafe) { - const pointsString = polygon.map((p) => p.join(',')).join(' '); - setPoints(pointsString); - setSvgRect({ - top: rect.top, - left: x + OFFSET, - height: rect.height, - width: rect.left - x - OFFSET, - }); - timeoutRef.current = setTimeout(clearTriangle, SAFE_TRIANGLE_CLEAR_DELAY_MS); - } else { - clearTriangle(); - } - }; - - useEffect(() => { - const container = htmlDivElementRef(containerRef).current; - if (!container) { - return; - } - - container.addEventListener('mousemove', onMouseMove, true); - - return () => { - container.removeEventListener('mousemove', onMouseMove, true); - clearTimeout(timeoutRef.current); - }; - }, [onMouseMove, containerRef]); - - if (!points || !svgRect) { - return null; - } - - return ( - - - - ); -} - -/** - * A component that creates a "safe triangle" area between the cursor and a submenu. - * This prevents the submenu from switching when the user moves the cursor - * diagonally towards the submenu by rendering an invisible SVG overlay. - */ -function SafeTriangle({submenuRef, children}: SafeTriangleProps) { - const styles = useThemeStyles(); - const containerRef = useRef(null); - - if (isMobile()) { - return children; - } - - return ( - - {children} - - - ); -} - -export default SafeTriangle; diff --git a/src/components/SafeTriangle/types.ts b/src/components/SafeTriangle/types.ts deleted file mode 100644 index 5173cbb3acb7..000000000000 --- a/src/components/SafeTriangle/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type {View} from 'react-native'; - -type SafeTriangleProps = { - submenuRef: React.RefObject; - children: React.ReactNode; -}; - -export default SafeTriangleProps; diff --git a/src/components/Search/FilterComponents/CardSelector.tsx b/src/components/Search/FilterComponents/CardSelector.tsx index ab786d71123f..5f7e280426f4 100644 --- a/src/components/Search/FilterComponents/CardSelector.tsx +++ b/src/components/Search/FilterComponents/CardSelector.tsx @@ -2,10 +2,9 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import ActivityIndicator from '@components/ActivityIndicator'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import CardListItem from '@components/SelectionList/ListItem/CardListItem'; import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections'; -import type {TextInputOptions} from '@components/SelectionList/types'; +import type {Section} from '@components/SelectionList/SelectionListWithSections/types'; import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; @@ -24,12 +23,12 @@ import ONYXKEYS from '@src/ONYXKEYS'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import ListFilterView from './ListFilterViewWrapper'; -type CardSelectorProps = SearchFilterSelectionListProps & { +type CardSelectorProps = { value: string[] | undefined; onChange: (cards: string[]) => void; }; -function CardSelector({value = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: CardSelectorProps) { +function CardSelector({value = [], onChange}: CardSelectorProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -42,6 +41,7 @@ function CardSelector({value = [], selectionListTextInputStyle, selectionListSty const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES); const [workspaceCardFeeds, workspaceCardFeedsMetadata] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); + const [searchAdvancedFiltersForm, searchAdvancedFiltersFormMetadata] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM); const personalDetails = usePersonalDetails(); useEffect(() => { @@ -72,30 +72,36 @@ function CardSelector({value = [], selectionListTextInputStyle, selectionListSty !!item.cardName?.toLocaleLowerCase().includes(debouncedSearchTerm.toLocaleLowerCase()) || (item.isVirtual && translate('workspace.expensifyCard.virtual').toLocaleLowerCase().includes(debouncedSearchTerm.toLocaleLowerCase())); - const selectedData = [...individualCardsSectionData, ...closedCardsSectionData].filter((item) => item.isSelected && searchFunction(item)); - const unselectedIndividualCardsData = individualCardsSectionData.filter((item) => !item.isSelected && searchFunction(item)); - const unselectedClosedCardsData = closedCardsSectionData.filter((item) => !item.isSelected && searchFunction(item)); - - const itemCount = selectedData.length + unselectedIndividualCardsData.length + unselectedClosedCardsData.length; - const sectionHeaderCount = unselectedClosedCardsData.length > 0 ? 1 : 0; - - const sections = [ - { - title: undefined, - data: selectedData, - sectionIndex: 0, - }, - { - title: undefined, - data: unselectedIndividualCardsData, - sectionIndex: 1, - }, - { - title: translate('search.filters.card.closedCards'), - data: unselectedClosedCardsData, - sectionIndex: 2, - }, - ]; + let sections: Array> = []; + let itemCount = 0; + let sectionHeaderCount = 0; + + if (searchAdvancedFiltersForm) { + const selectedData = [...individualCardsSectionData, ...closedCardsSectionData].filter((item) => item.isSelected && searchFunction(item)); + const unselectedIndividualCardsData = individualCardsSectionData.filter((item) => !item.isSelected && searchFunction(item)); + const unselectedClosedCardsData = closedCardsSectionData.filter((item) => !item.isSelected && searchFunction(item)); + + itemCount = selectedData.length + unselectedIndividualCardsData.length + unselectedClosedCardsData.length; + sectionHeaderCount = unselectedClosedCardsData.length > 0 ? 1 : 0; + + sections = [ + { + title: undefined, + data: selectedData, + sectionIndex: 0, + }, + { + title: undefined, + data: unselectedIndividualCardsData, + sectionIndex: 1, + }, + { + title: translate('search.filters.card.closedCards'), + data: unselectedClosedCardsData, + sectionIndex: 2, + }, + ]; + } const updateNewCards = (item: CardFilterItem) => { if (!item.keyForList) { @@ -111,18 +117,14 @@ function CardSelector({value = [], selectionListTextInputStyle, selectionListSty } }; - const textInputOptions: TextInputOptions = { + const textInputOptions = { value: searchTerm, label: translate('common.search'), onChangeText: setSearchTerm, headerMessage: debouncedSearchTerm.trim() && sections.every((section) => !section.data.length) ? translate('common.noResultsFound') : '', - style: { - containerStyle: selectionListTextInputStyle, - }, - disableAutoFocus: !autoFocus, }; - const isLoadingOnyxData = isLoadingOnyxValue(userCardListMetadata, workspaceCardFeedsMetadata); + const isLoadingOnyxData = isLoadingOnyxValue(userCardListMetadata, workspaceCardFeedsMetadata, searchAdvancedFiltersFormMetadata); const shouldShowLoadingState = isLoadingOnyxData || (!areCardsLoaded && !isOffline); const reasonAttributes: SkeletonSpanReasonAttributes = {context: 'SearchFiltersCardPage', isLoadingFromOnyx: isLoadingOnyxData}; @@ -152,8 +154,6 @@ function CardSelector({value = [], selectionListTextInputStyle, selectionListSty textInputOptions={textInputOptions} shouldStopPropagation canSelectMultiple - style={selectionListStyle} - footerContent={footer} /> )} diff --git a/src/components/Search/FilterComponents/CategorySelector.tsx b/src/components/Search/FilterComponents/CategorySelector.tsx index 27ce0a2626fb..57ef195d60fe 100644 --- a/src/components/Search/FilterComponents/CategorySelector.tsx +++ b/src/components/Search/FilterComponents/CategorySelector.tsx @@ -1,6 +1,5 @@ import React from 'react'; import type {OnyxCollection} from 'react-native-onyx'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import {getDecodedCategoryName} from '@libs/CategoryUtils'; @@ -12,12 +11,12 @@ import {getEmptyObject} from '@src/types/utils/EmptyObject'; import getEmptyArray from '@src/types/utils/getEmptyArray'; import MultiSelect from './MultiSelect'; -type CategorySelectorProps = SearchFilterSelectionListProps & { +type CategorySelectorProps = { value: string[] | undefined; onChange: (categories: string[]) => void; }; -function CategorySelector({value = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: CategorySelectorProps) { +function CategorySelector({value = [], onChange}: CategorySelectorProps) { const {translate} = useLocalize(); const [policyIDs = getEmptyArray()] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {selector: filterPolicyIDSelector}); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); @@ -78,11 +77,7 @@ function CategorySelector({value = [], selectionListTextInputStyle, selectionLis value={selectedCategoriesItems} items={categoryItems} isSearchable={categoryItems.length >= CONST.STANDARD_LIST_ITEM_LIMIT} - autoFocus={autoFocus} searchPlaceholder={translate('common.category')} - selectionListTextInputStyle={selectionListTextInputStyle} - selectionListStyle={selectionListStyle} - footer={footer} onChange={(categories) => onChange(categories.map((category) => category.value))} /> ); diff --git a/src/components/Search/FilterComponents/CurrencySelector.tsx b/src/components/Search/FilterComponents/CurrencySelector.tsx index 84ff911b10df..9a8e1a3374ec 100644 --- a/src/components/Search/FilterComponents/CurrencySelector.tsx +++ b/src/components/Search/FilterComponents/CurrencySelector.tsx @@ -1,15 +1,14 @@ import React from 'react'; import {useCurrencyListActions, useCurrencyListState} from '@components/CurrencyListContextProvider'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import {getCurrencyOptions} from '@libs/SearchUIUtils'; import MultiSelect from './MultiSelect'; -type CurrencySelectorProps = SearchFilterSelectionListProps & { +type CurrencySelectorProps = { value: string[] | undefined; onChange: (item: string[]) => void; }; -function CurrencySelector({value = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: CurrencySelectorProps) { +function CurrencySelector({value = [], onChange}: CurrencySelectorProps) { const {currencyList} = useCurrencyListState(); const {getCurrencySymbol} = useCurrencyListActions(); const currencyOptions = getCurrencyOptions(currencyList, getCurrencySymbol); @@ -19,11 +18,7 @@ function CurrencySelector({value = [], selectionListTextInputStyle, selectionLis onChange(currencies.map((currency) => currency.value))} /> ); diff --git a/src/components/Search/FilterComponents/ExportedToSelector.tsx b/src/components/Search/FilterComponents/ExportedToSelector.tsx index 07378885dca2..e167f637da8c 100644 --- a/src/components/Search/FilterComponents/ExportedToSelector.tsx +++ b/src/components/Search/FilterComponents/ExportedToSelector.tsx @@ -2,7 +2,6 @@ import React from 'react'; import {View} from 'react-native'; import type {TupleToUnion} from 'type-fest'; import Icon from '@components/Icon'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -21,7 +20,7 @@ import getEmptyArray from '@src/types/utils/getEmptyArray'; import type IconAsset from '@src/types/utils/IconAsset'; import MultiSelect from './MultiSelect'; -type ExportedToSelectorProps = SearchFilterSelectionListProps & { +type ExportedToSelectorProps = { value: string[] | undefined; onChange: (exportedTo: string[]) => void; }; @@ -31,7 +30,7 @@ const STANDARD_EXPORT_TEMPLATE_ID_TO_DISPLAY_LABEL: Record = { [CONST.REPORT.EXPORT_OPTIONS.EXPENSE_LEVEL_EXPORT]: CONST.REPORT.EXPORT_OPTION_LABELS.EXPENSE_LEVEL_EXPORT, }; -function ExportedToSelector({value = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: ExportedToSelectorProps) { +function ExportedToSelector({value = [], onChange}: ExportedToSelectorProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const StyleUtils = useStyleUtils(); @@ -137,10 +136,6 @@ function ExportedToSelector({value = [], selectionListTextInputStyle, selectionL value={selectedExportedTo} items={exportedToPickerOptions} isSearchable={exportedToPickerOptions.length >= CONST.STANDARD_LIST_ITEM_LIMIT} - autoFocus={autoFocus} - selectionListTextInputStyle={selectionListTextInputStyle} - selectionListStyle={selectionListStyle} - footer={footer} onChange={(exportedTo) => onChange(exportedTo.map((e) => e.value))} /> ); diff --git a/src/components/Search/FilterComponents/FeedSelector.tsx b/src/components/Search/FilterComponents/FeedSelector.tsx index 18028166c34b..7b53977b0f7c 100644 --- a/src/components/Search/FilterComponents/FeedSelector.tsx +++ b/src/components/Search/FilterComponents/FeedSelector.tsx @@ -1,16 +1,15 @@ import React, {useEffect} from 'react'; import useFilterFeedData from '@components/Search/hooks/useFilterFeedData'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import useNetwork from '@hooks/useNetwork'; import {openSearchCardFiltersPage} from '@libs/actions/Search'; import MultiSelect from './MultiSelect'; -type FeedSelectorProps = SearchFilterSelectionListProps & { +type FeedSelectorProps = { value: string[] | undefined; onChange: (item: string[]) => void; }; -function FeedSelector({value, selectionListStyle, footer, onChange}: FeedSelectorProps) { +function FeedSelector({value, onChange}: FeedSelectorProps) { const {isOffline} = useNetwork(); const {feedOptions, feedValue} = useFilterFeedData(value); @@ -25,8 +24,6 @@ function FeedSelector({value, selectionListStyle, footer, onChange}: FeedSelecto onChange(feeds.map((feed) => feed.value))} /> ); diff --git a/src/components/Search/FilterComponents/InSelector.tsx b/src/components/Search/FilterComponents/InSelector.tsx index 56ecbe1b0139..f75003429607 100644 --- a/src/components/Search/FilterComponents/InSelector.tsx +++ b/src/components/Search/FilterComponents/InSelector.tsx @@ -1,10 +1,8 @@ import React, {useEffect} from 'react'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import {useOptionsList} from '@components/OptionListContextProvider'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMemberListItem'; import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections'; -import type {TextInputOptions} from '@components/SelectionList/types'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; @@ -24,7 +22,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import passthroughPolicyTagListSelector from '@src/selectors/PolicyTagList'; import ListFilterView from './ListFilterViewWrapper'; -type InSelectorProps = SearchFilterSelectionListProps & { +type InSelectorProps = { value: string[] | undefined; onChange: (ins: string[]) => void; }; @@ -41,7 +39,7 @@ function getSelectedOptionData(option: Option & Pick): O return {...option, isSelected: true, keyForList: option.keyForList ?? option.reportID}; } -function InSelector({value = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: InSelectorProps) { +function InSelector({value = [], onChange}: InSelectorProps) { const {translate} = useLocalize(); const personalDetails = usePersonalDetails(); const {options, areOptionsInitialized} = useOptionsList(); @@ -154,15 +152,11 @@ function InSelector({value = [], selectionListTextInputStyle, selectionListStyle const isLoadingNewOptions = !!isSearchingForReports; const shouldShowLoadingPlaceholder = !areOptionsInitialized || !value || !personalDetails; - const textInputOptions: TextInputOptions = { + const textInputOptions = { value: searchTerm, label: translate('common.search'), onChangeText: setSearchTerm, headerMessage, - style: { - containerStyle: selectionListTextInputStyle, - }, - disableAutoFocus: !autoFocus, }; const itemCount = sections.flatMap((section) => section.data).length; @@ -182,8 +176,6 @@ function InSelector({value = [], selectionListTextInputStyle, selectionListStyle isLoadingNewOptions={isLoadingNewOptions} shouldShowLoadingPlaceholder={shouldShowLoadingPlaceholder} shouldShowTextInput - style={selectionListStyle} - footerContent={footer} /> ); diff --git a/src/components/Search/FilterComponents/MultiSelect.tsx b/src/components/Search/FilterComponents/MultiSelect.tsx index a9d95f113275..585072ed130f 100644 --- a/src/components/Search/FilterComponents/MultiSelect.tsx +++ b/src/components/Search/FilterComponents/MultiSelect.tsx @@ -2,11 +2,9 @@ import React, {useState} from 'react'; import type {ReactNode} from 'react'; import {View} from 'react-native'; import ActivityIndicator from '@components/ActivityIndicator'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import SelectionList from '@components/SelectionList'; import MultiSelectListItem from '@components/SelectionList/ListItem/MultiSelectListItem'; import type {ListItem} from '@components/SelectionList/ListItem/types'; -import type {TextInputOptions} from '@components/SelectionList/types'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; @@ -23,7 +21,7 @@ type MultiSelectItem = { leftElement?: ReactNode; }; -type MultiSelectProps = SearchFilterSelectionListProps & { +type MultiSelectProps = { /** The list of all items to show up in the list */ items: Array>; @@ -41,23 +39,9 @@ type MultiSelectProps = SearchFilterSelectionListProps & { /** Whether the data for the popover is loading */ loading?: boolean; - - /** Whether the text input should be auto-focused or not. Defaults to true. */ - autoFocus?: boolean; }; -function MultiSelect({ - loading, - value, - items, - isSearchable, - searchPlaceholder, - selectionListTextInputStyle, - selectionListStyle, - autoFocus = true, - footer, - onChange, -}: MultiSelectProps) { +function MultiSelect({loading, value, items, isSearchable, searchPlaceholder, onChange}: MultiSelectProps) { const theme = useTheme(); const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -93,15 +77,11 @@ function MultiSelect({ } }; - const textInputOptions: TextInputOptions = { + const textInputOptions = { value: searchTerm, label: isSearchable ? (searchPlaceholder ?? translate('common.search')) : undefined, onChangeText: setSearchTerm, headerMessage, - style: { - containerStyle: selectionListTextInputStyle, - }, - disableAutoFocus: !autoFocus, }; const reasonAttributes: SkeletonSpanReasonAttributes = {context: 'MultiSelectDataLoading'}; @@ -126,8 +106,7 @@ function MultiSelect({ ListItem={MultiSelectListItem} onSelectRow={updateSelectedItems} textInputOptions={textInputOptions} - style={{contentContainerStyle: [styles.pb0], ...selectionListStyle}} - footerContent={footer} + style={{contentContainerStyle: [styles.pb0]}} /> )} diff --git a/src/components/Search/FilterComponents/ReportField/ReportFieldList.tsx b/src/components/Search/FilterComponents/ReportField/ReportFieldList.tsx index 3daac4752d80..c013bb9491ba 100644 --- a/src/components/Search/FilterComponents/ReportField/ReportFieldList.tsx +++ b/src/components/Search/FilterComponents/ReportField/ReportFieldList.tsx @@ -4,24 +4,22 @@ import type {PolicyReportField} from '@src/types/onyx'; type ReportFieldListProps = { field: PolicyReportField; - value: string | undefined; - allowDeselect?: boolean; - onChange: (newValue: string | undefined) => void; + value: string; + onChange: (newValue: string) => void; }; -function ReportFieldList({field, value, allowDeselect, onChange}: ReportFieldListProps) { +function ReportFieldList({field, value, onChange}: ReportFieldListProps) { const items = field.values.map((fieldValue) => ({ value: fieldValue, text: fieldValue, })); - const selectedValue = value ? {text: value, value} : undefined; + const selectedValue = {text: value, value}; return ( onChange(item?.value)} + onChange={(item) => onChange(item.value)} hasHeader /> ); diff --git a/src/components/Search/FilterComponents/ReportField/ReportFieldText.tsx b/src/components/Search/FilterComponents/ReportField/ReportFieldText.tsx index 98ca3365f13c..73a082a986d3 100644 --- a/src/components/Search/FilterComponents/ReportField/ReportFieldText.tsx +++ b/src/components/Search/FilterComponents/ReportField/ReportFieldText.tsx @@ -6,7 +6,7 @@ import type {PolicyReportField} from '@src/types/onyx'; type ReportFieldTextProps = { field: PolicyReportField; - value: string | undefined; + value: string; onChange: (newValue: string) => void; }; @@ -20,7 +20,7 @@ function ReportFieldText({field, value, onChange}: ReportFieldTextProps) { onChangeText={onChange} accessibilityLabel={field.name} role={CONST.ROLE.PRESENTATION} - containerStyles={[styles.ph5, styles.pv2]} + containerStyles={[styles.ph5, styles.mb2]} /> ); } diff --git a/src/components/Search/FilterComponents/ReportField/index.tsx b/src/components/Search/FilterComponents/ReportField/index.tsx index 6d446634a043..9cbb07164099 100644 --- a/src/components/Search/FilterComponents/ReportField/index.tsx +++ b/src/components/Search/FilterComponents/ReportField/index.tsx @@ -1,5 +1,5 @@ import React, {useImperativeHandle, useRef, useState} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -35,16 +35,13 @@ type ReportFieldBaseProps = { ref: React.Ref; values: ReportFieldValues | undefined; selectedField: PolicyReportField | null; - allowDeselectSingleSelection?: boolean; - style?: StyleProp; onFieldSelected: (field: PolicyReportField | null) => void; }; type SelectedReportFieldProps = { ref: React.Ref; field: PolicyReportField; - value: string | undefined; - allowDeselectSingleSelection?: boolean; + value: string; }; type SelectedDateReportFieldProps = { @@ -64,8 +61,8 @@ function getFilterKey(fieldName: string) { return `${CONST.SEARCH.REPORT_FIELD.DEFAULT_PREFIX}${suffix}` as const; } -function SelectedReportField({ref, field, value: initialValue, allowDeselectSingleSelection}: SelectedReportFieldProps) { - const [value, setValue] = useState(initialValue); +function SelectedReportField({ref, field, value: initialValue}: SelectedReportFieldProps) { + const [value, setValue] = useState(initialValue); const fieldType = field.type as Exclude, typeof CONST.REPORT_FIELD_TYPES.FORMULA | typeof CONST.REPORT_FIELD_TYPES.DATE>; const UpdateReportFieldComponent = { @@ -91,7 +88,6 @@ function SelectedReportField({ref, field, value: initialValue, allowDeselectSing ); @@ -133,6 +129,7 @@ function SelectedDateReportField({ref, field, value: initialValue, selectedDateM isDateModifierSelected: () => !!selectedDateModifier, applySelectedFieldAndGoBack: () => { dateFilterRef.current?.save(); + onDateModifierSelected(null); }, resetSelectedFieldAndGoBack: () => { if (selectedDateModifier === CONST.SEARCH.DATE_MODIFIERS.RANGE) { @@ -177,7 +174,7 @@ function SelectedDateReportField({ref, field, value: initialValue, selectedDateM ); } -function ReportFieldBase({ref, values: initialValues = {}, selectedField, allowDeselectSingleSelection, style, onFieldSelected}: ReportFieldBaseProps) { +function ReportFieldBase({ref, values: initialValues = {}, selectedField, onFieldSelected}: ReportFieldBaseProps) { const {translate, localeCompare} = useLocalize(); const styles = useThemeStyles(); const policyReportFieldsSelector = (policies: OnyxCollection) => createAllPolicyReportFieldsSelector(policies, localeCompare); @@ -190,7 +187,7 @@ function ReportFieldBase({ref, values: initialValues = {}, selectedField, allowD const getValue = (fieldName: string) => { const filterKey = getFilterKey(fieldName); - return values[filterKey]?.trim(); + return values[filterKey]?.trim() ?? ''; }; const getDateValue = (fieldName: string) => { @@ -260,7 +257,7 @@ function ReportFieldBase({ref, values: initialValues = {}, selectedField, allowD if (selectedField) { return ( - <> + {!selectedDateModifier && ( )} - + ); } @@ -297,7 +293,7 @@ function ReportFieldBase({ref, values: initialValues = {}, selectedField, allowD }); return ( - + {listItems.map((item) => ( = { value: T; }; -type SingleSelectProps = SearchFilterSelectionListProps & { +type SingleSelectProps = { /** The list of all items to show up in the list */ items: Array>; @@ -22,7 +21,7 @@ type SingleSelectProps = SearchFilterSelectionListProps & { value: SingleSelectItem | undefined; /** Function to call when changes are applied */ - onChange: (item: SingleSelectItem | undefined) => void; + onChange: (item: SingleSelectItem) => void; /** Whether the search input should be displayed */ isSearchable?: boolean; @@ -30,13 +29,15 @@ type SingleSelectProps = SearchFilterSelectionListProps & { /** Search input place holder */ searchPlaceholder?: string; + /** Custom styles for the SelectionList */ + selectionListStyle?: SelectionListStyle; + /** Whether SelectionList of popup should stay mounted when popup is not visible. */ shouldShowList?: boolean; /** Custom height for each item in the list */ itemHeight?: number; - allowDeselect?: boolean; hasTitle?: boolean; hasHeader?: boolean; }; @@ -46,15 +47,12 @@ function SingleSelect({ items, isSearchable, searchPlaceholder, - selectionListTextInputStyle, selectionListStyle, shouldShowList = true, hasTitle, hasHeader, - itemHeight, - footer, - allowDeselect, onChange, + itemHeight, }: SingleSelectProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -98,23 +96,15 @@ function SingleSelect({ return; } - if (allowDeselect && newItem.value === selectedItem?.value) { - setSelectedItem(undefined); - onChange(undefined); - return; - } setSelectedItem(newItem); onChange(newItem); }; - const textInputOptions: TextInputOptions = { + const textInputOptions = { value: searchTerm, label: isSearchable ? (searchPlaceholder ?? translate('common.search')) : undefined, onChangeText: setSearchTerm, headerMessage: noResultsFound ? translate('common.noResultsFound') : undefined, - style: { - containerStyle: selectionListTextInputStyle, - }, }; return ( @@ -140,7 +130,6 @@ function SingleSelect({ shouldUpdateFocusedIndex={isSearchable} initiallyFocusedItemKey={isSearchable ? value?.value : undefined} shouldShowLoadingPlaceholder={!noResultsFound} - footerContent={footer} /> diff --git a/src/components/Search/FilterComponents/TagSelector.tsx b/src/components/Search/FilterComponents/TagSelector.tsx index 12651370d21f..aa3fb23c7892 100644 --- a/src/components/Search/FilterComponents/TagSelector.tsx +++ b/src/components/Search/FilterComponents/TagSelector.tsx @@ -1,6 +1,5 @@ import React from 'react'; import type {OnyxCollection} from 'react-native-onyx'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import {getCleanedTagName, getTagNamesFromTagsLists} from '@libs/PolicyUtils'; @@ -13,12 +12,12 @@ import {getEmptyObject} from '@src/types/utils/EmptyObject'; import getEmptyArray from '@src/types/utils/getEmptyArray'; import MultiSelect from './MultiSelect'; -type TagSelectorProps = SearchFilterSelectionListProps & { +type TagSelectorProps = { value: string[] | undefined; onChange: (tags: string[]) => void; }; -function TagSelector({value = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: TagSelectorProps) { +function TagSelector({value = [], onChange}: TagSelectorProps) { const {translate} = useLocalize(); const [policyIDs = getEmptyArray()] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {selector: filterPolicyIDSelector}); const [allPolicyTagLists = getEmptyObject>>()] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS, {selector: passthroughPolicyTagListSelector}); @@ -54,10 +53,6 @@ function TagSelector({value = [], selectionListTextInputStyle, selectionListStyl value={selectedTagsItems} items={tagItems} isSearchable={tagItems.length >= CONST.STANDARD_LIST_ITEM_LIMIT} - autoFocus={autoFocus} - selectionListTextInputStyle={selectionListTextInputStyle} - selectionListStyle={selectionListStyle} - footer={footer} onChange={(tags) => onChange(tags.map((tag) => tag.value))} /> ); diff --git a/src/components/Search/FilterComponents/TaxRateSelector.tsx b/src/components/Search/FilterComponents/TaxRateSelector.tsx index 749f9dd820c1..20d0566a03fc 100644 --- a/src/components/Search/FilterComponents/TaxRateSelector.tsx +++ b/src/components/Search/FilterComponents/TaxRateSelector.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import useOnyx from '@hooks/useOnyx'; import {getAllTaxRates} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; @@ -8,12 +7,12 @@ import {filterPolicyIDSelector} from '@src/selectors/Search'; import type {Policy} from '@src/types/onyx'; import MultiSelect from './MultiSelect'; -type TaxRateSelectorProps = SearchFilterSelectionListProps & { +type TaxRateSelectorProps = { value: string[] | undefined; onChange: (taxRates: string[]) => void; }; -function TaxRateSelector({value = [], selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: TaxRateSelectorProps) { +function TaxRateSelector({value = [], onChange}: TaxRateSelectorProps) { const [policyIDs] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {selector: filterPolicyIDSelector}); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); @@ -38,10 +37,6 @@ function TaxRateSelector({value = [], selectionListTextInputStyle, selectionList value={selectedTaxRates} items={taxItems} isSearchable={taxItems.length >= CONST.STANDARD_LIST_ITEM_LIMIT} - autoFocus={autoFocus} - selectionListTextInputStyle={selectionListTextInputStyle} - selectionListStyle={selectionListStyle} - footer={footer} onChange={(taxRates) => onChange(taxRates.map((taxRate) => taxRate.value))} /> ); diff --git a/src/components/Search/FilterComponents/TypeSelector.tsx b/src/components/Search/FilterComponents/TypeSelector.tsx index e59b78aa0468..0170b30fd89e 100644 --- a/src/components/Search/FilterComponents/TypeSelector.tsx +++ b/src/components/Search/FilterComponents/TypeSelector.tsx @@ -1,6 +1,5 @@ import React from 'react'; import type {OnyxCollection} from 'react-native-onyx'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import {getTypeOptions} from '@libs/SearchUIUtils'; @@ -10,7 +9,7 @@ import {emailSelector} from '@src/selectors/Session'; import type {Policy} from '@src/types/onyx'; import SingleSelect from './SingleSelect'; -type TypeSelectorProps = SearchFilterSelectionListProps & { +type TypeSelectorProps = { value: string | undefined; onChange: (item: string) => void; }; @@ -44,7 +43,7 @@ function typeOptionsPoliciesSelector(policies: OnyxCollection): OnyxColl return result; } -function TypeSelector({value = CONST.SEARCH.DATA_TYPES.EXPENSE, selectionListStyle, footer, onChange}: TypeSelectorProps) { +function TypeSelector({value = CONST.SEARCH.DATA_TYPES.EXPENSE, onChange}: TypeSelectorProps) { const {translate} = useLocalize(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: typeOptionsPoliciesSelector}); const [sessionEmail] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector}); @@ -56,14 +55,7 @@ function TypeSelector({value = CONST.SEARCH.DATA_TYPES.EXPENSE, selectionListSty // text is only needed when the list is searchable value={{value, text: ''}} items={types} - selectionListStyle={selectionListStyle} - footer={footer} - onChange={(item) => { - if (!item) { - return; - } - onChange(item.value); - }} + onChange={(item) => onChange(item.value)} /> ); } diff --git a/src/components/Search/FilterComponents/UserSelector.tsx b/src/components/Search/FilterComponents/UserSelector.tsx index 8e55f2af07c3..c153f4428781 100644 --- a/src/components/Search/FilterComponents/UserSelector.tsx +++ b/src/components/Search/FilterComponents/UserSelector.tsx @@ -1,6 +1,5 @@ import React, {useRef} from 'react'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import SelectionList from '@components/SelectionList'; import UserSelectionListItem from '@components/SelectionList/ListItem/UserSelectionListItem'; import type {ListItem, SelectionListHandle} from '@components/SelectionList/types'; @@ -14,18 +13,17 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ListFilterWrapper from './ListFilterViewWrapper'; -type UserSelectorProps = SearchFilterSelectionListProps & { +type UserSelectorProps = { value: string[] | undefined; - autoFocus?: boolean; onChange: (options: string[]) => void; }; -function UserSelector({value = [], selectionListTextInputStyle, selectionListStyle, autoFocus = true, footer, onChange}: UserSelectorProps) { +function UserSelector({value = [], onChange}: UserSelectorProps) { const selectionListRef = useRef | null>(null); const styles = useThemeStyles(); const {translate} = useLocalize(); const personalDetails = usePersonalDetails(); - const shouldFocusInputOnScreenFocus = autoFocus && canFocusInputOnScreenFocus(); + const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const initialSelectedAccountIDs = value.reduce>((acc, id) => { const participant = personalDetails?.[id]; @@ -74,9 +72,6 @@ function UserSelector({value = [], selectionListTextInputStyle, selectionListSty onChangeText: setSearchTerm, headerMessage, disableAutoFocus: !shouldFocusInputOnScreenFocus, - style: { - containerStyle: selectionListTextInputStyle, - }, } : undefined; @@ -94,8 +89,7 @@ function UserSelector({value = [], selectionListTextInputStyle, selectionListSty onSelectRow={selectUser} isLoadingNewOptions={isLoadingNewOptions} shouldShowLoadingPlaceholder={!areOptionsInitialized} - style={{contentContainerStyle: [styles.pb0], ...selectionListStyle}} - footerContent={footer} + style={{contentContainerStyle: [styles.pb0]}} /> ); diff --git a/src/components/Search/FilterComponents/WorkspaceSelector.tsx b/src/components/Search/FilterComponents/WorkspaceSelector.tsx index 00ddc12e2a8e..53d0f65ae6a0 100644 --- a/src/components/Search/FilterComponents/WorkspaceSelector.tsx +++ b/src/components/Search/FilterComponents/WorkspaceSelector.tsx @@ -1,18 +1,16 @@ import React from 'react'; -import type {SearchFilterSelectionListProps} from '@components/Search/types'; import useAdvancedSearchFilters from '@hooks/useAdvancedSearchFilters'; import type {Icon} from '@src/types/onyx/OnyxCommon'; import type {MultiSelectItem} from './MultiSelect'; import MultiSelect from './MultiSelect'; -type WorkspaceSelectorProps = SearchFilterSelectionListProps & { +type WorkspaceSelectorProps = { policyIDQuery: string[] | undefined; value: string[] | undefined; - autoFocus?: boolean; onChange: (item: string[]) => void; }; -function WorkspaceSelector({policyIDQuery, value, selectionListTextInputStyle, selectionListStyle, autoFocus, footer, onChange}: WorkspaceSelectorProps) { +function WorkspaceSelector({policyIDQuery, value, onChange}: WorkspaceSelectorProps) { const {workspaces, shouldShowWorkspaceSearchInput} = useAdvancedSearchFilters(); const workspaceOptions: Array> = workspaces .flatMap((section) => section.data) @@ -30,12 +28,8 @@ function WorkspaceSelector({policyIDQuery, value, selectionListTextInputStyle, s onChange(policyIDs.map((id) => id.value))} isSearchable={shouldShowWorkspaceSearchInput} - selectionListTextInputStyle={selectionListTextInputStyle} - selectionListStyle={selectionListStyle} - footer={footer} /> ); } diff --git a/src/components/Search/FilterComponents/index.tsx b/src/components/Search/FilterComponents/index.tsx index 90773737f3d8..4812cebd9d4a 100644 --- a/src/components/Search/FilterComponents/index.tsx +++ b/src/components/Search/FilterComponents/index.tsx @@ -1,11 +1,11 @@ import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; -import type {SearchAmountFilterKeys, SearchDateFilterKeys, SearchFilterSelectionListProps} from '@components/Search/types'; +import type {SearchAmountFilterKeys, SearchDateFilterKeys} from '@components/Search/types'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import {FILTER_VIEW_MAP, getMultiSelectFilterOptions, getSingleSelectFilterOptions} from '@libs/SearchUIUtils'; +import {FILTER_LABEL_MAP, getMultiSelectFilterOptions, getSingleSelectFilterOptions} from '@libs/SearchUIUtils'; import type {SearchFilter} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -25,12 +25,11 @@ import UserSelector from './UserSelector'; import WorkspaceSelector from './WorkspaceSelector'; type FilterKeys = Exclude; -type FilterComponentsProps = SearchFilterSelectionListProps & { +type FilterComponentsProps = { filterKey: FilterKeys; value: SearchAdvancedFiltersForm[FilterKeys] | undefined; policyIDQuery: string[] | undefined; - allowDeselectSingleSelection?: boolean; - onChange: (value: SearchAdvancedFiltersForm[FilterKeys] | undefined) => void; + onChange: (value: SearchAdvancedFiltersForm[FilterKeys]) => void; }; type TextInputFilterComponentsProps = { @@ -42,16 +41,14 @@ type TextInputFilterComponentsProps = { | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.TITLE | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_ID; value: string | undefined; - autoFocus?: boolean; onChange: (value: string) => void; }; type SingleSelectFilterKeys = typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_TYPE; -type SingleSelectFilterComponentsProps = SearchFilterSelectionListProps & { +type SingleSelectFilterComponentsProps = { filterKey: SingleSelectFilterKeys; value: SearchAdvancedFiltersForm[SingleSelectFilterKeys] | undefined; - allowDeselect: boolean; - onChange: (value: SearchAdvancedFiltersForm[SingleSelectFilterKeys] | undefined) => void; + onChange: (value: SearchAdvancedFiltersForm[SingleSelectFilterKeys]) => void; }; type MultiSelectFilterKeys = @@ -60,17 +57,17 @@ type MultiSelectFilterKeys = | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_STATUS | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.STATUS; -type MultiSelectFilterComponentsProps = SearchFilterSelectionListProps & { +type MultiSelectFilterComponentsProps = { filterKey: MultiSelectFilterKeys; value: SearchAdvancedFiltersForm[MultiSelectFilterKeys] | undefined; onChange: (values: SearchAdvancedFiltersForm[MultiSelectFilterKeys]) => void; }; -function TextInputFilterComponents({filterKey, value, autoFocus, onChange}: TextInputFilterComponentsProps) { +function TextInputFilterComponents({filterKey, value, onChange}: TextInputFilterComponentsProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const label = translate(FILTER_VIEW_MAP[filterKey].labelKey); + const label = translate(FILTER_LABEL_MAP[filterKey]); return ( ); } -function SingleSelectFilterComponents({filterKey, value, selectionListTextInputStyle, selectionListStyle, footer, allowDeselect, onChange}: SingleSelectFilterComponentsProps) { +function SingleSelectFilterComponents({filterKey, value, onChange}: SingleSelectFilterComponentsProps) { const {translate} = useLocalize(); const items = getSingleSelectFilterOptions(filterKey, translate); @@ -92,16 +88,12 @@ function SingleSelectFilterComponents({filterKey, value, selectionListTextInputS option.value === value)} - selectionListTextInputStyle={selectionListTextInputStyle} - selectionListStyle={selectionListStyle} - footer={footer} - allowDeselect={allowDeselect} - onChange={(item) => onChange(item?.value)} + onChange={(item) => onChange(item.value)} /> ); } -function MultiSelectFilterComponents({filterKey, value = [], selectionListStyle, footer, onChange}: MultiSelectFilterComponentsProps) { +function MultiSelectFilterComponents({filterKey, value = [], onChange}: MultiSelectFilterComponentsProps) { const {translate} = useLocalize(); const typeSelector = (searchAdvancedFiltersForm: OnyxEntry) => { return searchAdvancedFiltersForm?.type; @@ -117,8 +109,6 @@ function MultiSelectFilterComponents({filterKey, value = [], selectionListStyle, { if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.STATUS) { onChange(selectedItems.length > 0 ? selectedItems.map((item) => item.value) : CONST.SEARCH.STATUS.EXPENSE.ALL); @@ -130,17 +120,7 @@ function MultiSelectFilterComponents({filterKey, value = [], selectionListStyle, ); } -function FilterComponents({ - filterKey, - value, - policyIDQuery, - selectionListTextInputStyle, - selectionListStyle, - autoFocus, - footer, - allowDeselectSingleSelection, - onChange, -}: FilterComponentsProps) { +function FilterComponents({filterKey, value, policyIDQuery, onChange}: FilterComponentsProps) { switch (filterKey) { case CONST.SEARCH.SYNTAX_FILTER_KEYS.FEED: case CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID: @@ -161,10 +141,6 @@ function FilterComponents({ return ( ); @@ -173,8 +149,6 @@ function FilterComponents({ return ( ); @@ -190,7 +164,6 @@ function FilterComponents({ key={filterKey} filterKey={filterKey} value={value as string | undefined} - autoFocus={autoFocus} onChange={onChange} /> ); @@ -201,10 +174,6 @@ function FilterComponents({ ); @@ -217,10 +186,6 @@ function FilterComponents({ key={filterKey} filterKey={filterKey} value={value as SingleSelectFilterComponentsProps['value'] | undefined} - selectionListTextInputStyle={selectionListTextInputStyle} - selectionListStyle={selectionListStyle} - footer={footer} - allowDeselect={!!allowDeselectSingleSelection} onChange={onChange} /> ); @@ -235,9 +200,6 @@ function FilterComponents({ key={filterKey} filterKey={filterKey} value={value} - selectionListTextInputStyle={selectionListTextInputStyle} - selectionListStyle={selectionListStyle} - footer={footer} onChange={onChange} /> ); @@ -250,10 +212,6 @@ function FilterComponents({ ); @@ -262,10 +220,6 @@ function FilterComponents({ ); diff --git a/src/components/Search/FilterDropdowns/AdvancedFilters/AdvancedFiltersFullscreen.tsx b/src/components/Search/FilterDropdowns/AdvancedFilters/AdvancedFiltersFullscreen.tsx deleted file mode 100644 index 061abe5c0ae4..000000000000 --- a/src/components/Search/FilterDropdowns/AdvancedFilters/AdvancedFiltersFullscreen.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React, {useState} from 'react'; -import {View} from 'react-native'; -import Button from '@components/Button'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapperContainer from '@components/ScreenWrapper/ScreenWrapperContainer'; -import type {PopoverComponentProps} from '@components/Search/FilterDropdowns/FilterPopupButton'; -import useUpdateFilterQuery from '@components/Search/hooks/useUpdateFilterQuery'; -import type {SearchQueryJSON} from '@components/Search/types'; -import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; -import useThemeStyles from '@hooks/useThemeStyles'; -import {getAdvancedFiltersToReset} from '@libs/SearchQueryUtils'; -import {FILTER_VIEW_MAP} from '@libs/SearchUIUtils'; -import type {SearchFilter} from '@libs/SearchUIUtils'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {SearchAdvancedFiltersForm} from '@src/types/form'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import FilterList from './FilterList'; -import SelectedFilterContent from './SelectedFilterContent'; - -type AdvancedFiltersFullscreenProps = { - queryJSON: SearchQueryJSON; - closeOverlay: PopoverComponentProps['closeOverlay']; -}; - -function AdvancedFiltersFullscreen({queryJSON, closeOverlay}: AdvancedFiltersFullscreenProps) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - - const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM); - - const [selectedFilter, setSelectedFilter] = useState(null); - const [values, setValues] = useState>(searchAdvancedFiltersForm ?? {}); - - const updateFilterQueryParams = useUpdateFilterQuery(queryJSON, true); - - const advancedFiltersToReset = searchAdvancedFiltersForm ? getAdvancedFiltersToReset(searchAdvancedFiltersForm) : undefined; - - return ( - - { - if (selectedFilter) { - setSelectedFilter(null); - return; - } - closeOverlay(); - }} - /> - - {!!selectedFilter && ( - - { - setValues((prevValues) => ({...prevValues, ...newValues})); - setSelectedFilter(null); - }} - /> - - )} - {!selectedFilter && ( - <> - {!isEmptyObject(advancedFiltersToReset) && ( - @@ -102,9 +208,36 @@ function DropdownButton({label, value, medium = false, labelStyle, innerStyles, )} )} - /> + + {/* Dropdown overlay */} + + {popoverContent} + + ); } -export default DropdownButton; -export type {DropdownButtonProps}; +export type {PopoverComponentProps, DropdownButtonProps}; +export default withViewportOffsetTop(DropdownButton); diff --git a/src/components/Search/FilterDropdowns/FilterPopupButton.tsx b/src/components/Search/FilterDropdowns/FilterPopupButton.tsx deleted file mode 100644 index adddb13d4259..000000000000 --- a/src/components/Search/FilterDropdowns/FilterPopupButton.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import {willAlertModalBecomeVisibleSelector} from '@selectors/Modal'; -import type {ReactNode, RefObject} from 'react'; -import React, {useRef, useState} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; -import {View} from 'react-native'; -import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent'; -import type PopoverWithMeasuredContentProps from '@components/PopoverWithMeasuredContent/types'; -import withViewportOffsetTop from '@components/withViewportOffsetTop'; -import useOnyx from '@hooks/useOnyx'; -import usePopoverPosition from '@hooks/usePopoverPosition'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; - -type PopoverComponentProps = { - isExpanded: boolean; - closeOverlay: () => void; - setPopoverWidth?: (width: number | undefined) => void; -}; - -type ButtonComponentProps = { - onPress: () => void; - ref: RefObject; - isExpanded: boolean; -}; - -type FilterPopupButtonProps = { - /** The viewport's offset */ - viewportOffsetTop: number; - - /** Wrapper style for the outer view */ - wrapperStyle?: StyleProp; - - outerModalStyle?: ViewStyle; - popoverWidth?: number; - popoverAnchorAlignment?: AnchorAlignment; - smallScreenModalType?: PopoverWithMeasuredContentProps['smallScreenModalType']; - - /** The component to render in the popover */ - PopoverComponent: (props: PopoverComponentProps) => ReactNode; - - /** The component to render as the button */ - renderButton: (props: ButtonComponentProps) => ReactNode; -}; - -const ANCHOR_ORIGIN = { - horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, - vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, -}; - -function FilterPopupButton({ - viewportOffsetTop, - popoverWidth, - wrapperStyle, - outerModalStyle, - smallScreenModalType, - popoverAnchorAlignment: popoverAnchorAlignmentProp, - PopoverComponent, - renderButton, -}: FilterPopupButtonProps) { - // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to distinguish RHP and narrow layout - // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {isSmallScreenWidth} = useResponsiveLayout(); - - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - const {windowHeight} = useWindowDimensions(); - const triggerRef = useRef(null); - const anchorRef = useRef(null); - const [isOverlayVisible, setIsOverlayVisible] = useState(false); - const [customPopoverWidth, setCustomPopoverWidth] = useState(undefined); - const {calculatePopoverPosition} = usePopoverPosition(); - - const [popoverTriggerPosition, setPopoverTriggerPosition] = useState({ - horizontal: 0, - vertical: 0, - }); - - const [willAlertModalBecomeVisible] = useOnyx(ONYXKEYS.MODAL, {selector: willAlertModalBecomeVisibleSelector}); - - const popoverAnchorAlignment = popoverAnchorAlignmentProp ?? ANCHOR_ORIGIN; - - /** - * Toggle the overlay between open & closed - */ - const toggleOverlay = () => { - setIsOverlayVisible((previousValue) => { - if (!previousValue && willAlertModalBecomeVisible) { - return false; - } - - return !previousValue; - }); - }; - - /** - * Calculate popover position and toggle overlay - */ - const calculatePopoverPositionAndToggleOverlay = () => { - calculatePopoverPosition(anchorRef, popoverAnchorAlignment).then((position) => { - setPopoverTriggerPosition({...position, vertical: position.vertical}); - toggleOverlay(); - }); - }; - - const actualPopoverWidth = customPopoverWidth ?? popoverWidth ?? CONST.POPOVER_DROPDOWN_WIDTH; - const containerStyles = isSmallScreenWidth ? styles.w100 : {width: actualPopoverWidth}; - - const popoverContent = PopoverComponent({closeOverlay: toggleOverlay, isExpanded: isOverlayVisible, setPopoverWidth: setCustomPopoverWidth}); - - return ( - - {/* Dropdown Trigger */} - {renderButton({ref: triggerRef, onPress: calculatePopoverPositionAndToggleOverlay, isExpanded: isOverlayVisible})} - - {/* Dropdown overlay */} - - {popoverContent} - - - ); -} - -export type {PopoverComponentProps, FilterPopupButtonProps}; -export default withViewportOffsetTop(FilterPopupButton); diff --git a/src/components/Search/SearchList/BaseSearchList/index.tsx b/src/components/Search/SearchList/BaseSearchList/index.tsx index 7ac21fc26c9f..8af495aaacb5 100644 --- a/src/components/Search/SearchList/BaseSearchList/index.tsx +++ b/src/components/Search/SearchList/BaseSearchList/index.tsx @@ -2,21 +2,17 @@ import {useIsFocused} from '@react-navigation/native'; import {FlashList} from '@shopify/flash-list'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {GestureResponderEvent, NativeSyntheticEvent} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import Animated from 'react-native-reanimated'; import type {SearchListItem} from '@components/Search/SearchList/ListItem/types'; import type {ExtendedTargetedEvent} from '@components/SelectionList/ListItem/types'; import {useEditingCellState} from '@components/TransactionItemRow/EditableCell'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; -import useOnyx from '@hooks/useOnyx'; import useStableIndexedHandler from '@hooks/useStableIndexedHandler'; import {isMobileChrome} from '@libs/Browser'; import {addKeyDownPressListener, removeKeyDownPressListener} from '@libs/KeyboardShortcut/KeyDownPressListener'; import {isFocusRestoreInProgress} from '@libs/NavigationFocusReturn'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Modal} from '@src/types/onyx'; import type BaseSearchListProps from './types'; const AnimatedFlashListComponent = Animated.createAnimatedComponent(FlashList); @@ -46,9 +42,6 @@ function BaseSearchList({ const isFocused = useIsFocused(); const {focusedCellId, isEditingCell} = useEditingCellState(); - const modalVisibilitySelector = (modal: OnyxEntry) => modal?.isVisible; - const [isModalVisible] = useOnyx(ONYXKEYS.MODAL, {selector: modalVisibilitySelector}); - const setHasKeyBeenPressed = useCallback(() => { if (hasKeyBeenPressed.current) { return; @@ -61,7 +54,7 @@ function BaseSearchList({ const [focusedIndex, setFocusedIndex] = useArrowKeyFocusManager({ initialFocusedIndex: -1, maxIndex: flattenedItemsLength - 1, - isActive: isFocused && !isModalVisible, + isActive: isFocused, onFocusedIndexChange: (index: number) => { scrollToIndex?.(index); }, @@ -125,7 +118,7 @@ function BaseSearchList({ captureOnInputs: true, shouldBubble: false, shouldPreventDefault: false, - isActive: isFocused && focusedIndex >= 0 && !isModalVisible, + isActive: isFocused && focusedIndex >= 0, // Propagation is controlled manually in selectFocusedOption based on editing state shouldStopPropagation: false, }); diff --git a/src/components/Search/SearchPageHeader/DatePickerFilterPopup.tsx b/src/components/Search/SearchPageHeader/DatePickerFilterPopup.tsx index cdcfa30b78af..7db38d1c2304 100644 --- a/src/components/Search/SearchPageHeader/DatePickerFilterPopup.tsx +++ b/src/components/Search/SearchPageHeader/DatePickerFilterPopup.tsx @@ -1,6 +1,6 @@ import React from 'react'; import DateSelectPopup from '@components/Search/FilterDropdowns/DateSelectPopup'; -import type {PopoverComponentProps} from '@components/Search/FilterDropdowns/FilterPopupButton'; +import type {PopoverComponentProps} from '@components/Search/FilterDropdowns/DropdownButton'; import type {SearchDateFilterKeys} from '@components/Search/types'; import type {SearchDateValues} from '@libs/SearchQueryUtils'; import {getDatePresets} from '@libs/SearchUIUtils'; diff --git a/src/components/Search/SearchPageHeader/SearchAdvancedFiltersButton.tsx b/src/components/Search/SearchPageHeader/SearchAdvancedFiltersButton.tsx index e63068085d15..f701468e8fa4 100644 --- a/src/components/Search/SearchPageHeader/SearchAdvancedFiltersButton.tsx +++ b/src/components/Search/SearchPageHeader/SearchAdvancedFiltersButton.tsx @@ -2,10 +2,6 @@ import React from 'react'; import Button from '@components/Button'; import Icon from '@components/Icon'; import {PressableWithFeedback} from '@components/Pressable'; -import AdvancedFilters from '@components/Search/FilterDropdowns/AdvancedFilters'; -import useFullscreenAdvancedFilters from '@components/Search/FilterDropdowns/AdvancedFilters/useFullscreenAdvancedFilters'; -import FilterPopupButton from '@components/Search/FilterDropdowns/FilterPopupButton'; -import type {PopoverComponentProps} from '@components/Search/FilterDropdowns/FilterPopupButton'; import type {SearchQueryJSON} from '@components/Search/types'; import useFilterFormValues from '@hooks/useFilterFormValues'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -14,7 +10,10 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSearchFilterSync from '@hooks/useSearchFilterSync'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import {updateAdvancedFilters} from '@libs/actions/Search'; +import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; type SearchAdvancedFiltersButtonProp = { queryJSON: SearchQueryJSON; @@ -24,70 +23,44 @@ function SearchAdvancedFiltersButton({queryJSON}: SearchAdvancedFiltersButtonPro const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); - const fullscreen = useFullscreenAdvancedFilters(); - const {isMediumScreenWidth} = useResponsiveLayout(); + const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); const expensifyIcons = useMemoizedLazyExpensifyIcons(['Filter']); const filterFormValues = useFilterFormValues(queryJSON); useSearchFilterSync(queryJSON, filterFormValues); - const filtersPopup = ({closeOverlay}: PopoverComponentProps) => ( - - ); + const openAdvancedFilters = () => { + updateAdvancedFilters(filterFormValues); + Navigation.navigate(ROUTES.SEARCH_ADVANCED_FILTERS.getRoute()); + }; - if (fullscreen || isMediumScreenWidth) { + if (shouldUseNarrowLayout || isMediumScreenWidth) { return ( - ( - - - - )} - smallScreenModalType={fullscreen ? CONST.MODAL.MODAL_TYPE.CENTERED_SWIPEABLE_TO_RIGHT : undefined} - // The default outerModalStyle in FilterPopupButton apply a max height to avoid keyboard. We don't want - // that in fullscreen because the keyboard avoiding logic will be handled by the ScreenWrapperContainer in AdvancedFiltersFullscreen. - outerModalStyle={fullscreen ? {} : undefined} - /> + + + ); } return ( - ( -