refactor: separation of shared components

This commit is contained in:
2025-08-06 21:43:43 +04:00
parent 6efb75ab30
commit e43a8b0865
28 changed files with 49 additions and 48 deletions

View File

@ -0,0 +1,105 @@
"use client";
import { useEffect, useRef, useState } from "react";
import { IMaskInput } from "react-imask";
import {
InputBase,
type InputBaseProps,
type PolymorphicComponentProps,
} from "@mantine/core";
import CountrySelect from "@/components/ui/PhoneInput/components/CountrySelect";
import { Country } from "@/components/ui/PhoneInput/types";
import getInitialDataFromValue from "@/components/ui/PhoneInput/utils/getInitialDataFromValue";
import getPhoneMask from "@/components/ui/PhoneInput/utils/getPhoneMask";
type AdditionalProps = {
onChange: (value: string | null) => void;
setPhoneMask: (mask: string) => void;
initialCountryCode?: string;
};
type InputProps = Omit<
PolymorphicComponentProps<typeof IMaskInput, InputBaseProps>,
"onChange" | "defaultValue" | "value"
>;
export type Props = AdditionalProps & InputProps;
const PhoneInput = ({
initialCountryCode = "RU",
onChange: _onChange,
setPhoneMask: _setPhoneMask,
...props
}: Props) => {
const [mask, setMask] = useState<string>("");
const initialData = useRef(getInitialDataFromValue(initialCountryCode));
const [country, setCountry] = useState<Country>(
initialData.current.country
);
const [value, setValue] = useState<string>("");
const inputRef = useRef<HTMLInputElement>(null);
const [dropdownWidth, setDropdownWidth] = useState<number>(300);
const onChange = (numberWithoutCode: string) => {
setValue(numberWithoutCode);
_onChange(`+${country.callingCode} ${numberWithoutCode}`);
};
const setPhoneMask = (phoneMask: string, country: Country) => {
setMask(phoneMask);
_setPhoneMask(`+${country.callingCode} ${phoneMask}`);
};
useEffect(() => {
setPhoneMask(initialData.current.format, country);
}, [initialData.current.format]);
const { readOnly, disabled } = props;
const leftSectionWidth = 90;
useEffect(() => {
if (!inputRef.current?.offsetWidth) return;
setDropdownWidth(inputRef.current?.offsetWidth);
}, [inputRef.current?.offsetWidth]);
return (
<InputBase
{...props}
component={IMaskInput}
inputRef={inputRef}
leftSection={
<CountrySelect
disabled={disabled || readOnly}
country={country}
setCountry={country => {
setCountry(country);
setPhoneMask(getPhoneMask(country.code), country);
setValue("");
if (inputRef.current) {
inputRef.current.focus();
}
}}
leftSectionWidth={leftSectionWidth}
inputWidth={dropdownWidth}
/>
}
leftSectionWidth={leftSectionWidth}
styles={{
input: {
fontSize: 17,
paddingLeft: `calc(${leftSectionWidth}px + var(--mantine-spacing-sm))`,
},
section: {
borderRight:
"1px solid var(--mantine-color-default-border)",
},
}}
inputMode={"numeric"}
mask={mask}
value={value}
onAccept={onChange}
/>
);
};
export default PhoneInput;