feat: login form as a client component, theme toggle
This commit is contained in:
114
components/PhoneInput/PhoneInput.tsx
Normal file
114
components/PhoneInput/PhoneInput.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { IMaskInput } from "react-imask";
|
||||
import {
|
||||
InputBase,
|
||||
type InputBaseProps,
|
||||
type PolymorphicComponentProps,
|
||||
} from "@mantine/core";
|
||||
import { useUncontrolled } from "@mantine/hooks";
|
||||
import CountrySelect from "@/components/PhoneInput/components/CountrySelect";
|
||||
import getFormat from "@/components/PhoneInput/utils/getFormat";
|
||||
import getInitialDataFromValue from "@/components/PhoneInput/utils/getInitialDataFromValue";
|
||||
|
||||
export type Props = {
|
||||
initialCountryCode?: string;
|
||||
defaultValue?: string;
|
||||
} & Omit<
|
||||
PolymorphicComponentProps<typeof IMaskInput, InputBaseProps>,
|
||||
"onChange" | "defaultValue"
|
||||
> & { onChange: (value: string | null) => void };
|
||||
|
||||
const PhoneInput = ({
|
||||
initialCountryCode = "RU",
|
||||
value: _value,
|
||||
onChange: _onChange,
|
||||
defaultValue,
|
||||
...props
|
||||
}: Props) => {
|
||||
const [value, onChange] = useUncontrolled({
|
||||
value: _value,
|
||||
defaultValue,
|
||||
onChange: _onChange,
|
||||
});
|
||||
const initialData = useRef(
|
||||
getInitialDataFromValue(value, initialCountryCode)
|
||||
);
|
||||
const [country, setCountry] = useState(initialData.current.country);
|
||||
const [format, setFormat] = useState(initialData.current.format);
|
||||
const [localValue, setLocalValue] = useState(
|
||||
initialData.current.localValue
|
||||
);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const lastNotifiedValue = useRef<string | null>(value ?? "");
|
||||
|
||||
useEffect(() => {
|
||||
const value = localValue.trim();
|
||||
if (value !== lastNotifiedValue.current) {
|
||||
lastNotifiedValue.current = value;
|
||||
onChange(value);
|
||||
}
|
||||
}, [country.code, localValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
typeof value !== "undefined" &&
|
||||
value !== lastNotifiedValue.current
|
||||
) {
|
||||
const initialData = getInitialDataFromValue(
|
||||
value,
|
||||
initialCountryCode
|
||||
);
|
||||
lastNotifiedValue.current = value;
|
||||
setCountry(initialData.country);
|
||||
setFormat(initialData.format);
|
||||
setLocalValue(initialData.localValue);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const { readOnly, disabled } = props;
|
||||
const leftSectionWidth = 100;
|
||||
|
||||
return (
|
||||
<InputBase
|
||||
{...props}
|
||||
component={IMaskInput}
|
||||
inputRef={inputRef}
|
||||
leftSection={
|
||||
<CountrySelect
|
||||
disabled={disabled || readOnly}
|
||||
country={country}
|
||||
setCountry={country => {
|
||||
setCountry(country);
|
||||
setFormat(getFormat(country.code));
|
||||
setLocalValue("");
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}}
|
||||
leftSectionWidth={leftSectionWidth}
|
||||
/>
|
||||
}
|
||||
leftSectionWidth={leftSectionWidth}
|
||||
styles={{
|
||||
input: {
|
||||
fontSize: 17,
|
||||
color: "primaryColor.1",
|
||||
paddingLeft: `calc(${leftSectionWidth}px + var(--mantine-spacing-sm))`,
|
||||
},
|
||||
section: {
|
||||
borderRight:
|
||||
"1px solid var(--mantine-color-default-border)",
|
||||
},
|
||||
}}
|
||||
inputMode={"numeric"}
|
||||
mask={format.mask}
|
||||
value={localValue}
|
||||
onAccept={value => setLocalValue(value)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PhoneInput;
|
||||
Reference in New Issue
Block a user