From 18c8f26f38d9f80715d0eeceb9c12a90928c0f7b Mon Sep 17 00:00:00 2001 From: Ayush Agrawal <54364088+AyushAgrawal-A2@users.noreply.github.com> Date: Tue, 10 Oct 2023 03:08:09 +0530 Subject: [PATCH] Feat: Adjust the overlay style for changing the phone number's country (#1876) * switched to dropdown menu component * Use latest dropdown container --------- Co-authored-by: Charles Bochet --- .../input/Types/CountryPickerHotkeyScope.ts | 3 + .../CountryPickerDropdownButton.tsx | 146 ++++++++++++++++++ .../CountryPickerDropdownSelect.tsx | 108 +++++++++++++ .../ui/input/components/PhoneInput.tsx | 52 ++----- 4 files changed, 273 insertions(+), 36 deletions(-) create mode 100644 front/src/modules/ui/input/Types/CountryPickerHotkeyScope.ts create mode 100644 front/src/modules/ui/input/components/CountryPickerDropdownButton.tsx create mode 100644 front/src/modules/ui/input/components/CountryPickerDropdownSelect.tsx diff --git a/front/src/modules/ui/input/Types/CountryPickerHotkeyScope.ts b/front/src/modules/ui/input/Types/CountryPickerHotkeyScope.ts new file mode 100644 index 000000000..eb1a05f3f --- /dev/null +++ b/front/src/modules/ui/input/Types/CountryPickerHotkeyScope.ts @@ -0,0 +1,3 @@ +export enum CountryPickerHotkeyScope { + CountryPicker = 'country-picker', +} diff --git a/front/src/modules/ui/input/components/CountryPickerDropdownButton.tsx b/front/src/modules/ui/input/components/CountryPickerDropdownButton.tsx new file mode 100644 index 000000000..f75927184 --- /dev/null +++ b/front/src/modules/ui/input/components/CountryPickerDropdownButton.tsx @@ -0,0 +1,146 @@ +import { useEffect, useMemo, useState } from 'react'; +import { getCountries, getCountryCallingCode } from 'react-phone-number-input'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { hasFlag } from 'country-flag-icons'; +import * as Flags from 'country-flag-icons/react/3x2'; +import { CountryCallingCode } from 'libphonenumber-js'; + +import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu'; +import { useDropdown } from '@/ui/dropdown/hooks/useDropdown'; +import { IconChevronDown } from '@/ui/icon'; + +import { IconWorld } from '../constants/icons'; +import { CountryPickerHotkeyScope } from '../Types/CountryPickerHotkeyScope'; + +import { CountryPickerDropdownSelect } from './CountryPickerDropdownSelect'; + +import 'react-phone-number-input/style.css'; + +type StyledDropdownButtonProps = { + isUnfolded: boolean; +}; + +export const StyledDropdownButtonContainer = styled.div` + align-items: center; + background: ${({ theme }) => theme.background.primary}; + border-radius: ${({ theme }) => theme.border.radius.xs}; + color: ${({ color }) => color ?? 'none'}; + cursor: pointer; + display: flex; + filter: ${(props) => (props.isUnfolded ? 'brightness(0.95)' : 'none')}; + + height: 32px; + + padding-left: ${({ theme }) => theme.spacing(2)}; + padding-right: ${({ theme }) => theme.spacing(2)}; + + padding-right: ${({ theme }) => theme.spacing(2)}; + user-select: none; + + &:hover { + filter: brightness(0.95); + } +`; + +const StyledIconContainer = styled.div` + align-items: center; + color: ${({ theme }) => theme.font.color.tertiary}; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; + justify-content: center; + + svg { + align-items: center; + display: flex; + height: 16px; + justify-content: center; + } +`; + +export type Country = { + countryCode: string; + countryName: string; + callingCode: CountryCallingCode; + Flag: Flags.FlagComponent; +}; + +export const CountryPickerDropdownButton = ({ + value, + onChange, +}: { + value: string; + onChange: (countryCode: string) => void; +}) => { + const theme = useTheme(); + + const [selectedCountry, setSelectedCountry] = useState(); + + const { isDropdownOpen, closeDropdown } = useDropdown({ + dropdownId: 'country-picker', + }); + + const handleChange = (countryCode: string) => { + onChange(countryCode); + closeDropdown(); + }; + + const countries = useMemo(() => { + const regionNamesInEnglish = new Intl.DisplayNames(['en'], { + type: 'region', + }); + + const countryCodes = getCountries(); + + return countryCodes.reduce((result, countryCode) => { + const countryName = regionNamesInEnglish.of(countryCode); + + if (!countryName) return result; + + if (!hasFlag(countryCode)) return result; + + const Flag = Flags[countryCode]; + + const callingCode = getCountryCallingCode(countryCode); + + result.push({ + countryCode, + countryName, + callingCode, + Flag, + }); + + return result; + }, []); + }, []); + + useEffect(() => { + const country = countries.find(({ countryCode }) => countryCode === value); + if (country) { + setSelectedCountry(country); + } + }, [countries, value]); + + return ( + + + {selectedCountry ? : } + + + + } + dropdownComponents={ + + } + dropdownOffset={{ x: -60, y: -34 }} + /> + ); +}; diff --git a/front/src/modules/ui/input/components/CountryPickerDropdownSelect.tsx b/front/src/modules/ui/input/components/CountryPickerDropdownSelect.tsx new file mode 100644 index 000000000..a9b6d39ed --- /dev/null +++ b/front/src/modules/ui/input/components/CountryPickerDropdownSelect.tsx @@ -0,0 +1,108 @@ +import { useMemo, useState } from 'react'; +import styled from '@emotion/styled'; + +import { DropdownMenuItemsContainer } from '@/ui/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSearchInput } from '@/ui/dropdown/components/DropdownMenuSearchInput'; +import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'; +import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator'; +import { MenuItem } from '@/ui/menu-item/components/MenuItem'; +import { MenuItemSelectAvatar } from '@/ui/menu-item/components/MenuItemSelectAvatar'; + +import { Country } from './CountryPickerDropdownButton'; + +import 'react-phone-number-input/style.css'; + +const StyledIconContainer = styled.div` + align-items: center; + color: ${({ theme }) => theme.font.color.tertiary}; + display: flex; + padding-right: ${({ theme }) => theme.spacing(1)}; + + svg { + align-items: center; + display: flex; + height: 16px; + justify-content: center; + } +`; + +const StyledDropdownMenuContainer = styled.ul` + left: 0; + padding: 0; + position: absolute; + top: 24px; +`; + +export const CountryPickerDropdownSelect = ({ + countries, + selectedCountry, + onChange, +}: { + countries: Country[]; + selectedCountry?: Country; + onChange: (countryCode: string) => void; +}) => { + const [searchFilter, setSearchFilter] = useState(''); + + const filteredCountries = useMemo( + () => + countries.filter(({ countryName }) => + countryName + .toLocaleLowerCase() + .includes(searchFilter.toLocaleLowerCase()), + ), + [countries, searchFilter], + ); + + return ( + <> + + + setSearchFilter(event.currentTarget.value)} + autoFocus + /> + + + {filteredCountries?.length === 0 ? ( + + ) : ( + <> + {selectedCountry && ( + onChange(selectedCountry.countryCode)} + text={`${selectedCountry.countryName} (+${selectedCountry.callingCode})`} + avatar={ + + + + } + /> + )} + {filteredCountries.map( + ({ countryCode, countryName, callingCode, Flag }) => + selectedCountry?.countryCode === countryCode ? null : ( + onChange(countryCode)} + text={`${countryName} (+${callingCode})`} + avatar={ + + + + } + /> + ), + )} + + )} + + + + + ); +}; diff --git a/front/src/modules/ui/input/components/PhoneInput.tsx b/front/src/modules/ui/input/components/PhoneInput.tsx index e27645ea6..a2cf8f833 100644 --- a/front/src/modules/ui/input/components/PhoneInput.tsx +++ b/front/src/modules/ui/input/components/PhoneInput.tsx @@ -2,8 +2,13 @@ import { useEffect, useRef, useState } from 'react'; import ReactPhoneNumberInput from 'react-phone-number-input'; import styled from '@emotion/styled'; +import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext'; +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; + import { useRegisterInputEvents } from '../hooks/useRegisterInputEvents'; +import { CountryPickerDropdownButton } from './CountryPickerDropdownButton'; + import 'react-phone-number-input/style.css'; const StyledContainer = styled.div` @@ -17,39 +22,9 @@ const StyledContainer = styled.div` `; const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)` - --PhoneInput-color--focus: transparent; - --PhoneInputCountryFlag-borderColor--focus: transparent; - --PhoneInputCountrySelect-marginRight: ${({ theme }) => theme.spacing(2)}; - --PhoneInputCountrySelectArrow-color: ${({ theme }) => - theme.font.color.tertiary}; - --PhoneInputCountrySelectArrow-opacity: 1; font-family: ${({ theme }) => theme.font.family}; height: 32px; - .PhoneInputCountry { - --PhoneInputCountryFlag-height: 12px; - --PhoneInputCountryFlag-width: 16px; - border-right: 1px solid ${({ theme }) => theme.border.color.light}; - display: flex; - justify-content: center; - margin-left: ${({ theme }) => theme.spacing(2)}; - } - - .PhoneInputCountryIcon { - background: none; - border-radius: ${({ theme }) => theme.border.radius.xs}; - box-shadow: none; - margin-right: 1px; - overflow: hidden; - &:focus { - box-shadow: none !important; - } - } - - .PhoneInputCountrySelectArrow { - margin-right: ${({ theme }) => theme.spacing(2)}; - } - .PhoneInputInput { background: ${({ theme }) => theme.background.transparent.secondary}; border: none; @@ -111,12 +86,17 @@ export const PhoneInput = ({ return ( - + + + ); };