-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #48785 from software-mansion-labs/kicu/search-rout…
…er-initial Add SearchRouter component and context to display it
- Loading branch information
Showing
10 changed files
with
244 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import React from 'react'; | ||
import Icon from '@components/Icon'; | ||
import * as Expensicons from '@components/Icon/Expensicons'; | ||
import {PressableWithoutFeedback} from '@components/Pressable'; | ||
import useTheme from '@hooks/useTheme'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
import Permissions from '@libs/Permissions'; | ||
import {useSearchRouterContext} from './SearchRouterContext'; | ||
|
||
function SearchButton() { | ||
const styles = useThemeStyles(); | ||
const theme = useTheme(); | ||
const {openSearchRouter} = useSearchRouterContext(); | ||
|
||
if (!Permissions.canUseNewSearchRouter()) { | ||
return; | ||
} | ||
|
||
return ( | ||
<PressableWithoutFeedback | ||
accessibilityLabel="" | ||
style={[styles.flexRow, styles.mr2, styles.touchableButtonImage]} | ||
onPress={() => { | ||
openSearchRouter(); | ||
}} | ||
> | ||
<Icon | ||
src={Expensicons.MagnifyingGlass} | ||
fill={theme.icon} | ||
/> | ||
</PressableWithoutFeedback> | ||
); | ||
} | ||
|
||
SearchButton.displayName = 'SearchButton'; | ||
|
||
export default SearchButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import debounce from 'lodash/debounce'; | ||
import React, {useCallback, useState} from 'react'; | ||
import {View} from 'react-native'; | ||
import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; | ||
import Modal from '@components/Modal'; | ||
import type {SearchQueryJSON} from '@components/Search/types'; | ||
import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; | ||
import useResponsiveLayout from '@hooks/useResponsiveLayout'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
import * as SearchUtils from '@libs/SearchUtils'; | ||
import Navigation from '@navigation/Navigation'; | ||
import CONST from '@src/CONST'; | ||
import ROUTES from '@src/ROUTES'; | ||
import {useSearchRouterContext} from './SearchRouterContext'; | ||
import SearchRouterInput from './SearchRouterInput'; | ||
|
||
const SEARCH_DEBOUNCE_DELAY = 200; | ||
|
||
function SearchRouter() { | ||
const styles = useThemeStyles(); | ||
|
||
const {isSmallScreenWidth} = useResponsiveLayout(); | ||
const {isSearchRouterDisplayed, closeSearchRouter} = useSearchRouterContext(); | ||
const [currentQuery, setCurrentQuery] = useState<SearchQueryJSON | undefined>(undefined); | ||
|
||
const clearUserQuery = () => { | ||
setCurrentQuery(undefined); | ||
}; | ||
|
||
const onSearchChange = debounce((userQuery: string) => { | ||
if (!userQuery) { | ||
clearUserQuery(); | ||
return; | ||
} | ||
|
||
const queryJSON = SearchUtils.buildSearchQueryJSON(userQuery); | ||
|
||
if (queryJSON) { | ||
// eslint-disable-next-line | ||
console.log('parsedQuery', queryJSON); | ||
|
||
setCurrentQuery(queryJSON); | ||
} else { | ||
// Handle query parsing error | ||
} | ||
}, SEARCH_DEBOUNCE_DELAY); | ||
|
||
const onSearchSubmit = useCallback(() => { | ||
closeSearchRouter(); | ||
|
||
const query = SearchUtils.buildSearchQueryString(currentQuery); | ||
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query})); | ||
clearUserQuery(); | ||
}, [currentQuery, closeSearchRouter]); | ||
|
||
useKeyboardShortcut( | ||
CONST.KEYBOARD_SHORTCUTS.ENTER, | ||
() => { | ||
if (!currentQuery) { | ||
return; | ||
} | ||
|
||
onSearchSubmit(); | ||
}, | ||
{ | ||
captureOnInputs: true, | ||
shouldBubble: false, | ||
}, | ||
); | ||
|
||
useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ESCAPE, () => { | ||
closeSearchRouter(); | ||
clearUserQuery(); | ||
}); | ||
|
||
const modalType = isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.CENTERED : CONST.MODAL.MODAL_TYPE.POPOVER; | ||
const isFullWidth = isSmallScreenWidth; | ||
|
||
return ( | ||
<Modal | ||
type={modalType} | ||
fullscreen | ||
isVisible={isSearchRouterDisplayed} | ||
popoverAnchorPosition={{right: 20, top: 20}} | ||
onClose={closeSearchRouter} | ||
> | ||
<FocusTrapForModal active={isSearchRouterDisplayed}> | ||
<View style={[styles.flex1, styles.p3]}> | ||
<SearchRouterInput | ||
isFullWidth={isFullWidth} | ||
onChange={onSearchChange} | ||
onSubmit={onSearchSubmit} | ||
/> | ||
</View> | ||
</FocusTrapForModal> | ||
</Modal> | ||
); | ||
} | ||
|
||
SearchRouter.displayName = 'SearchRouter'; | ||
|
||
export default SearchRouter; |
37 changes: 37 additions & 0 deletions
37
src/components/Search/SearchRouter/SearchRouterContext.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import React, {useContext, useMemo, useState} from 'react'; | ||
import type ChildrenProps from '@src/types/utils/ChildrenProps'; | ||
|
||
const defaultSearchContext = { | ||
isSearchRouterDisplayed: false, | ||
openSearchRouter: () => {}, | ||
closeSearchRouter: () => {}, | ||
}; | ||
|
||
type SearchRouterContext = typeof defaultSearchContext; | ||
|
||
const Context = React.createContext<SearchRouterContext>(defaultSearchContext); | ||
|
||
function SearchRouterContextProvider({children}: ChildrenProps) { | ||
const [isSearchRouterDisplayed, setIsSearchRouterDisplayed] = useState(false); | ||
|
||
const routerContext = useMemo(() => { | ||
const openSearchRouter = () => setIsSearchRouterDisplayed(true); | ||
const closeSearchRouter = () => setIsSearchRouterDisplayed(false); | ||
|
||
return { | ||
isSearchRouterDisplayed, | ||
openSearchRouter, | ||
closeSearchRouter, | ||
}; | ||
}, [isSearchRouterDisplayed, setIsSearchRouterDisplayed]); | ||
|
||
return <Context.Provider value={routerContext}>{children}</Context.Provider>; | ||
} | ||
|
||
function useSearchRouterContext() { | ||
return useContext(Context); | ||
} | ||
|
||
SearchRouterContextProvider.displayName = 'SearchRouterContextProvider'; | ||
|
||
export {SearchRouterContextProvider, useSearchRouterContext}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React, {useState} from 'react'; | ||
import BaseTextInput from '@components/TextInput/BaseTextInput'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
import variables from '@styles/variables'; | ||
import CONST from '@src/CONST'; | ||
|
||
type SearchRouterInputProps = { | ||
isFullWidth: boolean; | ||
onChange: (searchTerm: string) => void; | ||
onSubmit: () => void; | ||
}; | ||
|
||
function SearchRouterInput({isFullWidth, onChange, onSubmit}: SearchRouterInputProps) { | ||
const styles = useThemeStyles(); | ||
|
||
const [value, setValue] = useState(''); | ||
|
||
const onChangeText = (text: string) => { | ||
setValue(text); | ||
onChange(text); | ||
}; | ||
|
||
const modalWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; | ||
|
||
return ( | ||
<BaseTextInput | ||
value={value} | ||
onChangeText={onChangeText} | ||
onSubmitEditing={onSubmit} | ||
autoFocus | ||
textInputContainerStyles={[{borderBottomWidth: 0}, modalWidth]} | ||
inputStyle={[styles.searchInputStyle, styles.searchRouterInputStyle, styles.ph2]} | ||
role={CONST.ROLE.PRESENTATION} | ||
autoCapitalize="none" | ||
/> | ||
); | ||
} | ||
|
||
SearchRouterInput.displayName = 'SearchRouterInput'; | ||
|
||
export default SearchRouterInput; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters