fix: add phone-input

This commit is contained in:
2025-06-11 15:40:17 +03:00
parent 53f214ba28
commit d53c5606ff
8 changed files with 95 additions and 8 deletions

48
package-lock.json generated
View File

@@ -9,6 +9,10 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@hookform/resolvers": "^5.1.1", "@hookform/resolvers": "^5.1.1",
"@maskito/core": "^3.9.0",
"@maskito/phone": "^3.9.0",
"@maskito/react": "^3.9.0",
"libphonenumber-js": "^1.12.9",
"next": "15.3.2", "next": "15.3.2",
"nodemailer": "^7.0.3", "nodemailer": "^7.0.3",
"react": "^19.0.0", "react": "^19.0.0",
@@ -682,6 +686,44 @@
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
}, },
"node_modules/@maskito/core": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.9.0.tgz",
"integrity": "sha512-OgzzgzJTXFZH79mqyHFVUZ5/bUhSW147+JzYVX+DdmQ5zc+mxmFQqsUS5ffVxd2C7/bnEmC7+savYbcae2IhBw==",
"license": "Apache-2.0"
},
"node_modules/@maskito/kit": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.9.0.tgz",
"integrity": "sha512-CD7TQ7WUMtZ8jkhOsislbqht1gMuNHVQsLJG9tXcGvZbegkgJ6wdkggkol1y1/0F5eh/fT+RzzKD9dVjSQon2g==",
"license": "Apache-2.0",
"peer": true,
"peerDependencies": {
"@maskito/core": "^3.9.0"
}
},
"node_modules/@maskito/phone": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@maskito/phone/-/phone-3.9.0.tgz",
"integrity": "sha512-EUCOmOscoQM+vnJwOAiBXVpZVVYkHc7rhnqLqfkslXsZCg5VLNNpzAb8SuFQMxYJYM2NnMawErvL7CjOdVDmvQ==",
"license": "Apache-2.0",
"peerDependencies": {
"@maskito/core": "^3.9.0",
"@maskito/kit": "^3.9.0",
"libphonenumber-js": ">=1.0.0"
}
},
"node_modules/@maskito/react": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@maskito/react/-/react-3.9.0.tgz",
"integrity": "sha512-qYGncdyaPbi50rDkg0gwh64DHPBCT6YMdkWsMbqG57bVjE1S0X9zbZQICieRQXGVkRjlgJenoMu3b5svdH4ysQ==",
"license": "Apache-2.0",
"peerDependencies": {
"@maskito/core": "^3.9.0",
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/@napi-rs/wasm-runtime": { "node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.10", "version": "0.2.10",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz",
@@ -4025,6 +4067,12 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/libphonenumber-js": {
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz",
"integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==",
"license": "MIT"
},
"node_modules/lilconfig": { "node_modules/lilconfig": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",

View File

@@ -12,6 +12,10 @@
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^5.1.1", "@hookform/resolvers": "^5.1.1",
"@maskito/core": "^3.9.0",
"@maskito/phone": "^3.9.0",
"@maskito/react": "^3.9.0",
"libphonenumber-js": "^1.12.9",
"next": "15.3.2", "next": "15.3.2",
"nodemailer": "^7.0.3", "nodemailer": "^7.0.3",
"react": "^19.0.0", "react": "^19.0.0",

View File

@@ -3,3 +3,4 @@ export { Mark } from './mark';
export { Input } from './input'; export { Input } from './input';
export { AdvancedPhoneInput } from './advanced-phone-input'; export { AdvancedPhoneInput } from './advanced-phone-input';
export { TextArea } from './text-area'; export { TextArea } from './text-area';
export { PhoneInput } from './phone-input';

View File

@@ -41,6 +41,10 @@
transition: border-color ease .5s; transition: border-color ease .5s;
} }
&_error {
border-color: $color-error;
}
&_fullWidth{ &_fullWidth{
width: 100%; width: 100%;
} }

View File

@@ -10,10 +10,17 @@ type InputProps = {
className?: string; className?: string;
fullWidth?: boolean; fullWidth?: boolean;
variant?: 'default' | 'ghost'; variant?: 'default' | 'ghost';
error?: string | boolean;
} & DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>; } & DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
const Input = forwardRef(function Input( const Input = forwardRef(function Input(
{ className, fullWidth = false, variant = 'default', ...props }: InputProps, {
className,
fullWidth = false,
variant = 'default',
error = false,
...props
}: InputProps,
ref: Ref<HTMLInputElement>, ref: Ref<HTMLInputElement>,
) { ) {
return ( return (
@@ -24,6 +31,7 @@ const Input = forwardRef(function Input(
s.Input, s.Input,
s['Input_' + variant], s['Input_' + variant],
fullWidth && s.Input_fullWidth, fullWidth && s.Input_fullWidth,
error && s.Input_error,
className, className,
)} )}
/> />

View File

@@ -0,0 +1 @@
export { default as PhoneInput } from './phone-input';

View File

@@ -1,10 +1,25 @@
//import s from './phone-input.module.scss'; //import s from './phone-input.module.scss';
import { Input } from '@shared/ui'; import { Input } from '@shared/ui';
import { useMaskito } from '@maskito/react';
import { maskitoPhoneOptionsGenerator } from '@maskito/phone';
import metadata from 'libphonenumber-js/min/metadata';
import { DetailedHTMLProps, InputHTMLAttributes } from 'react';
type PhoneInput = {
className?: string;
} & DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
export default function PhoneInput({ ...props }: PhoneInput) {
const options = maskitoPhoneOptionsGenerator({
countryIsoCode: 'RU',
metadata,
});
const maskedInputRef = useMaskito({ options });
export default function PhoneInput() {
return ( return (
<> <>
<Input /> <Input {...props} ref={maskedInputRef} type='tel' />
</> </>
); );
} }

View File

@@ -1,16 +1,22 @@
'use client'; 'use client';
import s from './styles.module.scss'; import s from './styles.module.scss';
import { Button, Input } from '@shared/ui'; import { Button, Input, PhoneInput } from '@shared/ui';
import { Controller, useForm } from 'react-hook-form'; import { Controller, useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod'; import { z } from 'zod';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { sendFormFn } from '@shared/api/api.service'; import { sendFormFn } from '@shared/api/api.service';
import { isValidPhoneNumber } from 'libphonenumber-js/min';
const FormSchema = z.object({ const FormSchema = z.object({
name: z.string().min(3), name: z
phone: z.string(), .string()
.min(3, { message: 'Поле должно содержать не менее 3-х букв' })
.regex(/^[A-Za-zА-Яа-яЁё]+(?:[ '-][A-Za-zА-Яа-яЁё]+)*$/, {
message: 'Поле содержит некорректные символы',
}),
phone: z.string().refine(isValidPhoneNumber, 'Некорректный номер телефона'),
}); });
type TForm = z.infer<typeof FormSchema>; type TForm = z.infer<typeof FormSchema>;
@@ -67,11 +73,11 @@ export default function OfferForm() {
control={control} control={control}
name={'phone'} name={'phone'}
render={({ field }) => ( render={({ field }) => (
<Input <PhoneInput
{...field} {...field}
className={s.Unit} className={s.Unit}
type='text' type='text'
placeholder='+7 (999) 123 45 67' placeholder='+7 999 123-45-67'
/> />
)} )}
/> />