diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2022-11-09 10:55:00 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2022-11-24 16:26:28 +0100 |
| commit | 8c40662a0816a43f2423a68596ea2f1b0b3a492a (patch) | |
| tree | 13f7590ae5acd1a594d5a7a3d3e2f911ee04d975 /gui/src | |
| parent | 1fcdf3c0ab63dd78fc491f0537a0a09a367b804c (diff) | |
| download | mullvadvpn-8c40662a0816a43f2423a68596ea2f1b0b3a492a.tar.xz mullvadvpn-8c40662a0816a43f2423a68596ea2f1b0b3a492a.zip | |
Move search bar to it's own file
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/renderer/components/SearchBar.tsx | 105 | ||||
| -rw-r--r-- | gui/src/renderer/components/SplitTunnelingSettings.tsx | 51 | ||||
| -rw-r--r-- | gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx | 65 |
3 files changed, 117 insertions, 104 deletions
diff --git a/gui/src/renderer/components/SearchBar.tsx b/gui/src/renderer/components/SearchBar.tsx new file mode 100644 index 0000000000..a659f6130f --- /dev/null +++ b/gui/src/renderer/components/SearchBar.tsx @@ -0,0 +1,105 @@ +import { useCallback, useRef } from 'react'; +import styled from 'styled-components'; + +import { colors } from '../../config.json'; +import { messages } from '../../shared/gettext'; +import { normalText } from './common-styles'; +import ImageView from './ImageView'; + +export const StyledSearchContainer = styled.div({ + position: 'relative', + display: 'flex', +}); + +export const StyledSearchInput = styled.input.attrs({ type: 'text' })({ + ...normalText, + flex: 1, + border: 'none', + borderRadius: '4px', + padding: '9px 38px', + margin: 0, + color: colors.white60, + backgroundColor: colors.white10, + '::placeholder': { + color: colors.white60, + }, + ':focus': { + color: colors.blue, + backgroundColor: colors.white, + '::placeholder': { + color: colors.blue40, + }, + }, +}); + +export const StyledClearButton = styled.button({ + position: 'absolute', + top: '50%', + transform: 'translateY(-50%)', + right: '9px', + border: 'none', + background: 'none', + padding: 0, +}); + +export const StyledSearchIcon = styled(ImageView)({ + position: 'absolute', + top: '50%', + transform: 'translateY(-50%)', + left: '9px', + [`${StyledSearchInput}:focus ~ &`]: { + backgroundColor: colors.blue, + }, +}); + +export const StyledClearIcon = styled(ImageView)({ + ':hover': { + backgroundColor: colors.white60, + }, + [`${StyledSearchInput}:focus ~ ${StyledClearButton} &`]: { + backgroundColor: colors.blue40, + ':hover': { + backgroundColor: colors.blue, + }, + }, +}); + +interface ISearchBarProps { + searchTerm: string; + onSearch: (searchTerm: string) => void; + className?: string; +} + +export default function SearchBar(props: ISearchBarProps) { + const inputRef = useRef() as React.RefObject<HTMLInputElement>; + + const onInput = useCallback( + (event: React.FormEvent) => { + const element = event.target as HTMLInputElement; + props.onSearch(element.value); + }, + [props.onSearch], + ); + + const onClear = useCallback(() => { + props.onSearch(''); + inputRef.current?.blur(); + }, [props.onSearch]); + + return ( + <StyledSearchContainer className={props.className}> + <StyledSearchInput + ref={inputRef} + value={props.searchTerm} + onInput={onInput} + placeholder={messages.gettext('Search for...')} + /> + <StyledSearchIcon source="icon-search" width={24} tintColor={colors.white60} /> + {props.searchTerm.length > 0 && ( + <StyledClearButton onClick={onClear}> + <StyledClearIcon source="icon-close" width={18} tintColor={colors.white40} /> + </StyledClearButton> + )} + </StyledSearchContainer> + ); +} diff --git a/gui/src/renderer/components/SplitTunnelingSettings.tsx b/gui/src/renderer/components/SplitTunnelingSettings.tsx index f68be19589..7ddf34f55a 100644 --- a/gui/src/renderer/components/SplitTunnelingSettings.tsx +++ b/gui/src/renderer/components/SplitTunnelingSettings.tsx @@ -31,8 +31,6 @@ import { StyledCellButton, StyledCellLabel, StyledCellWarningIcon, - StyledClearButton, - StyledClearIcon, StyledContent, StyledHeaderTitle, StyledHeaderTitleContainer, @@ -43,9 +41,7 @@ import { StyledNoResult, StyledNoResultText, StyledPageCover, - StyledSearchContainer, - StyledSearchIcon, - StyledSearchInput, + StyledSearchBar, StyledSpinnerRow, } from './SplitTunnelingSettingsStyles'; import Switch from './Switch'; @@ -176,7 +172,7 @@ function LinuxSplitTunnelingSettings(props: IPlatformSplitTunnelingSettingsProps </HeaderSubTitle> </SettingsHeader> - <SearchBar searchTerm={searchTerm} onSearch={setSearchTerm} /> + <StyledSearchBar searchTerm={searchTerm} onSearch={setSearchTerm} /> <ApplicationList applications={filteredApplications} rowRenderer={rowRenderer} /> <StyledBrowseButton onClick={launchWithFilePicker}> @@ -435,7 +431,9 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett </HeaderSubTitle> </SettingsHeader> - {splitTunnelingEnabled && <SearchBar searchTerm={searchTerm} onSearch={setSearchTerm} />} + {splitTunnelingEnabled && ( + <StyledSearchBar searchTerm={searchTerm} onSearch={setSearchTerm} /> + )} <Accordion expanded={showSplitSection}> <Cell.Section sectionTitle={excludedTitle}> @@ -566,45 +564,6 @@ function WindowsApplicationRow(props: IWindowsApplicationRowProps) { ); } -interface ISearchBarProps { - searchTerm: string; - onSearch: (searchTerm: string) => void; -} - -function SearchBar(props: ISearchBarProps) { - const inputRef = useRef() as React.RefObject<HTMLInputElement>; - - const onInput = useCallback( - (event: React.FormEvent) => { - const element = event.target as HTMLInputElement; - props.onSearch(element.value); - }, - [props.onSearch], - ); - - const onClear = useCallback(() => { - props.onSearch(''); - inputRef.current?.blur(); - }, [props.onSearch]); - - return ( - <StyledSearchContainer> - <StyledSearchInput - ref={inputRef} - value={props.searchTerm} - onInput={onInput} - placeholder={messages.pgettext('split-tunneling-view', 'Filter...')} - /> - <StyledSearchIcon source="icon-filter" width={24} tintColor={colors.white60} /> - {props.searchTerm.length > 0 && ( - <StyledClearButton onClick={onClear}> - <StyledClearIcon source="icon-close" width={18} tintColor={colors.white40} /> - </StyledClearButton> - )} - </StyledSearchContainer> - ); -} - function includesSearchTerm(application: IApplication, searchTerm: string) { return application.name.toLowerCase().includes(searchTerm.toLowerCase()); } diff --git a/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx b/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx index 6b135486db..028fac30a3 100644 --- a/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx +++ b/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx @@ -6,6 +6,7 @@ import * as Cell from './cell'; import { measurements, normalText } from './common-styles'; import ImageView from './ImageView'; import { NavigationScrollbars } from './NavigationBar'; +import SearchBar from './SearchBar'; import { HeaderTitle } from './SettingsHeader'; export const StyledPageCover = styled.div({}, (props: { show: boolean }) => ({ @@ -87,64 +88,6 @@ export const StyledCellContainer = styled(Cell.Container)({ marginBottom: measurements.rowVerticalMargin, }); -export const StyledSearchContainer = styled.div({ - position: 'relative', - marginBottom: measurements.buttonVerticalMargin, -}); - -export const StyledSearchInput = styled.input.attrs({ type: 'text' })({ - ...normalText, - width: `calc(100% - ${measurements.viewMargin} * 2)`, - border: 'none', - borderRadius: '4px', - padding: '9px 38px', - margin: `0 ${measurements.viewMargin}`, - color: colors.white60, - backgroundColor: colors.white10, - '::placeholder': { - color: colors.white60, - }, - ':focus': { - color: colors.blue, - backgroundColor: colors.white, - '::placeholder': { - color: colors.blue40, - }, - }, -}); - -export const StyledClearButton = styled.button({ - position: 'absolute', - top: '50%', - transform: 'translateY(-50%)', - right: '28px', - border: 'none', - background: 'none', - padding: 0, -}); - -export const StyledSearchIcon = styled(ImageView)({ - position: 'absolute', - top: '50%', - transform: 'translateY(-50%)', - left: '28px', - [`${StyledSearchInput}:focus ~ &`]: { - backgroundColor: colors.blue, - }, -}); - -export const StyledClearIcon = styled(ImageView)({ - ':hover': { - backgroundColor: colors.white60, - }, - [`${StyledSearchInput}:focus ~ ${StyledClearButton} &`]: { - backgroundColor: colors.blue40, - ':hover': { - backgroundColor: colors.blue, - }, - }, -}); - export const StyledNoResult = styled(Cell.CellFooter)({ display: 'flex', flexDirection: 'column', @@ -164,3 +107,9 @@ export const StyledHeaderTitleContainer = styled.div({ export const StyledHeaderTitle = styled(HeaderTitle)({ flex: 1, }); + +export const StyledSearchBar = styled(SearchBar)({ + marginLeft: measurements.viewMargin, + marginRight: measurements.viewMargin, + marginBottom: measurements.buttonVerticalMargin, +}); |
