a329189489
Co-authored-by: Jamie Kyle <113370520+jamiebuilds-signal@users.noreply.github.com>
170 lines
4.2 KiB
TypeScript
170 lines
4.2 KiB
TypeScript
// Copyright 2024 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import React, { useState, useCallback, useMemo } from 'react';
|
|
import Fuse from 'fuse.js';
|
|
|
|
import type { LocalizerType } from '../types/Util';
|
|
import type { CountryDataType } from '../util/getCountryData';
|
|
import { Modal } from './Modal';
|
|
import { SearchInput } from './SearchInput';
|
|
|
|
export type PropsType = Readonly<{
|
|
i18n: LocalizerType;
|
|
onChange: (region: string) => void;
|
|
value: string;
|
|
defaultRegion: string;
|
|
countries: ReadonlyArray<CountryDataType>;
|
|
}>;
|
|
|
|
export function CountryCodeSelect({
|
|
i18n,
|
|
onChange,
|
|
value,
|
|
defaultRegion,
|
|
countries,
|
|
}: PropsType): JSX.Element {
|
|
const index = useMemo(() => {
|
|
return new Fuse<CountryDataType>(countries, {
|
|
keys: [
|
|
{
|
|
name: 'displayName',
|
|
weight: 1,
|
|
},
|
|
{
|
|
name: 'code',
|
|
weight: 0.5,
|
|
},
|
|
],
|
|
threshold: 0.1,
|
|
});
|
|
}, [countries]);
|
|
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
|
|
const selectedCountry = useMemo(() => {
|
|
return countries.find(({ region }) => region === value);
|
|
}, [countries, value]);
|
|
|
|
const defaultCode = useMemo(() => {
|
|
return countries.find(({ region }) => region === defaultRegion)?.code ?? '';
|
|
}, [countries, defaultRegion]);
|
|
|
|
const filteredCountries = useMemo(() => {
|
|
if (!searchTerm) {
|
|
return countries;
|
|
}
|
|
return index.search(searchTerm).map(({ item }) => item);
|
|
}, [countries, index, searchTerm]);
|
|
|
|
const onShowModal = useCallback((ev: React.MouseEvent) => {
|
|
ev.preventDefault();
|
|
setIsModalOpen(true);
|
|
}, []);
|
|
|
|
const onCloseModal = useCallback(() => {
|
|
setIsModalOpen(false);
|
|
setSearchTerm('');
|
|
}, []);
|
|
|
|
const onSearchTermChange = useCallback(
|
|
(ev: React.ChangeEvent<HTMLInputElement>) => {
|
|
setSearchTerm(ev.target.value);
|
|
},
|
|
[]
|
|
);
|
|
|
|
const onCountryClick = useCallback(
|
|
(region: string) => {
|
|
onCloseModal();
|
|
onChange(region);
|
|
},
|
|
[onChange, onCloseModal]
|
|
);
|
|
|
|
const modal = (
|
|
<Modal
|
|
i18n={i18n}
|
|
modalName="CountryCodeSelect__Modal"
|
|
moduleClassName="CountryCodeSelect__Modal"
|
|
hasXButton
|
|
padded={false}
|
|
title={i18n('icu:CountryCodeSelect__Modal__title')}
|
|
onClose={onCloseModal}
|
|
>
|
|
<SearchInput
|
|
i18n={i18n}
|
|
moduleClassName="CountryCodeSelect__Modal__Search"
|
|
onChange={onSearchTermChange}
|
|
placeholder={i18n('icu:search')}
|
|
value={searchTerm}
|
|
/>
|
|
<div className="CountryCodeSelect__table">
|
|
{filteredCountries.map(({ displayName, region, code }) => {
|
|
return (
|
|
<CountryButton
|
|
key={region}
|
|
region={region}
|
|
displayName={displayName}
|
|
code={code}
|
|
onClick={onCountryClick}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
<div className="CountryCodeSelect__grow" />
|
|
</Modal>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<button type="button" className="CountryCodeSelect" onClick={onShowModal}>
|
|
<div className="CountryCodeSelect__text">
|
|
{selectedCountry?.displayName ??
|
|
i18n('icu:CountryCodeSelect__placeholder')}
|
|
</div>
|
|
<div className="CountryCodeSelect__value">
|
|
{selectedCountry?.code ?? defaultCode}
|
|
</div>
|
|
<div className="CountryCodeSelect__arrow" />
|
|
</button>
|
|
{isModalOpen ? modal : null}
|
|
</>
|
|
);
|
|
}
|
|
|
|
type CountryButtonPropsType = Readonly<{
|
|
region: string;
|
|
displayName: string;
|
|
code: string;
|
|
onClick: (region: string) => void;
|
|
}>;
|
|
|
|
function CountryButton({
|
|
region,
|
|
displayName,
|
|
code,
|
|
onClick,
|
|
}: CountryButtonPropsType): JSX.Element {
|
|
const onButtonClick = useCallback(
|
|
(ev: React.MouseEvent) => {
|
|
ev.preventDefault();
|
|
onClick(region);
|
|
},
|
|
[region, onClick]
|
|
);
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
className="CountryCodeSelect__CountryButton"
|
|
onClick={onButtonClick}
|
|
>
|
|
<div className="CountryCodeSelect__CountryButton__name">
|
|
{displayName}
|
|
</div>
|
|
<div className="CountryCodeSelect__CountryButton__code">{code}</div>
|
|
</button>
|
|
);
|
|
}
|