mirror of
https://github.com/logto-io/logto.git
synced 2025-03-24 22:41:28 -05:00
* feat(ui): implement phonenumber input field implement phonenumber input field * fix(ui): phone input ui fix phone input ui fix * fix(ui): should not show error if not interacted should not show error if not interacted * fix(ui): fix styling fix styling * feat(ui): add typeMode for phone input add typeMode for phone input * chore(ui): update pnpm-lock update pnpm-lock
111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
import classNames from 'classnames';
|
|
import React, { useState, useMemo, useRef } from 'react';
|
|
|
|
import { CountryCallingCode, CountryMetaData } from '@/hooks/use-phone-number';
|
|
|
|
import { ClearIcon, DownArrowIcon } from '../Icons';
|
|
import * as styles from './index.module.scss';
|
|
import * as phoneInputStyles from './phoneInput.module.scss';
|
|
|
|
export type Props = {
|
|
name: string;
|
|
autoComplete?: AutoCompleteType;
|
|
isDisabled?: boolean;
|
|
className?: string;
|
|
placeholder?: string;
|
|
countryCallingCode?: CountryCallingCode;
|
|
nationalNumber: string;
|
|
countryList?: CountryMetaData[];
|
|
hasError?: boolean;
|
|
onChange: (value: { countryCallingCode?: CountryCallingCode; nationalNumber?: string }) => void;
|
|
};
|
|
|
|
const PhoneInput = ({
|
|
name,
|
|
autoComplete,
|
|
isDisabled,
|
|
className,
|
|
placeholder,
|
|
countryCallingCode,
|
|
nationalNumber,
|
|
countryList,
|
|
hasError = false,
|
|
onChange,
|
|
}: Props) => {
|
|
const [onFocus, setOnFocus] = useState(false);
|
|
const inputReference = useRef<HTMLInputElement>(null);
|
|
|
|
const countrySelector = useMemo(() => {
|
|
if (!countryCallingCode || !countryList) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className={phoneInputStyles.countryCodeSelector}>
|
|
<span>{`+${countryCallingCode}`}</span>
|
|
<DownArrowIcon />
|
|
<select
|
|
onChange={({ target: { value } }) => {
|
|
onChange({ countryCallingCode: value });
|
|
|
|
// Auto Focus to the input
|
|
if (inputReference.current) {
|
|
inputReference.current.focus();
|
|
const { length } = inputReference.current.value;
|
|
inputReference.current.setSelectionRange(length, length);
|
|
}
|
|
}}
|
|
>
|
|
{countryList.map(({ countryCode, countryCallingCode, countryName }) => (
|
|
<option key={countryCode} value={countryCallingCode}>
|
|
{`${countryName ?? countryCode}: +${countryCallingCode}`}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
);
|
|
}, [countryCallingCode, countryList, onChange]);
|
|
|
|
return (
|
|
<div
|
|
className={classNames(
|
|
styles.wrapper,
|
|
onFocus && styles.focus,
|
|
hasError && styles.error,
|
|
className
|
|
)}
|
|
>
|
|
{countrySelector}
|
|
<input
|
|
ref={inputReference}
|
|
name={name}
|
|
disabled={isDisabled}
|
|
placeholder={placeholder}
|
|
value={nationalNumber}
|
|
type="tel"
|
|
inputMode="numeric"
|
|
autoComplete={autoComplete}
|
|
onFocus={() => {
|
|
setOnFocus(true);
|
|
}}
|
|
onBlur={() => {
|
|
setOnFocus(false);
|
|
}}
|
|
onChange={({ target: { value } }) => {
|
|
onChange({ nationalNumber: value });
|
|
}}
|
|
/>
|
|
{nationalNumber && onFocus && (
|
|
<ClearIcon
|
|
className={styles.actionButton}
|
|
onMouseDown={(event) => {
|
|
event.preventDefault();
|
|
onChange({ nationalNumber: '' });
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default PhoneInput;
|