From d53c5606ffc8866633029e2aac7866f774a1b5d4 Mon Sep 17 00:00:00 2001 From: RedrockJS Date: Wed, 11 Jun 2025 15:40:17 +0300 Subject: [PATCH] fix: add phone-input --- package-lock.json | 48 +++++++++++++++++++++++ package.json | 4 ++ src/shared/ui/index.ts | 1 + src/shared/ui/input/input.module.scss | 4 ++ src/shared/ui/input/input.tsx | 10 ++++- src/shared/ui/phone-input/index.ts | 1 + src/shared/ui/phone-input/phone-input.tsx | 19 ++++++++- src/widgets/offer-form/ui.tsx | 16 +++++--- 8 files changed, 95 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4fcfb91..ac66e8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,10 @@ "version": "0.1.0", "dependencies": { "@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", "nodemailer": "^7.0.3", "react": "^19.0.0", @@ -682,6 +686,44 @@ "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": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.10.tgz", @@ -4025,6 +4067,12 @@ "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": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", diff --git a/package.json b/package.json index 1db006a..b2ff3b1 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,10 @@ }, "dependencies": { "@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", "nodemailer": "^7.0.3", "react": "^19.0.0", diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts index b794500..53fd45e 100644 --- a/src/shared/ui/index.ts +++ b/src/shared/ui/index.ts @@ -3,3 +3,4 @@ export { Mark } from './mark'; export { Input } from './input'; export { AdvancedPhoneInput } from './advanced-phone-input'; export { TextArea } from './text-area'; +export { PhoneInput } from './phone-input'; diff --git a/src/shared/ui/input/input.module.scss b/src/shared/ui/input/input.module.scss index 7e436c5..2b96349 100644 --- a/src/shared/ui/input/input.module.scss +++ b/src/shared/ui/input/input.module.scss @@ -41,6 +41,10 @@ transition: border-color ease .5s; } + &_error { + border-color: $color-error; + } + &_fullWidth{ width: 100%; } diff --git a/src/shared/ui/input/input.tsx b/src/shared/ui/input/input.tsx index 3f9d697..21375b1 100644 --- a/src/shared/ui/input/input.tsx +++ b/src/shared/ui/input/input.tsx @@ -10,10 +10,17 @@ type InputProps = { className?: string; fullWidth?: boolean; variant?: 'default' | 'ghost'; + error?: string | boolean; } & DetailedHTMLProps, HTMLInputElement>; const Input = forwardRef(function Input( - { className, fullWidth = false, variant = 'default', ...props }: InputProps, + { + className, + fullWidth = false, + variant = 'default', + error = false, + ...props + }: InputProps, ref: Ref, ) { return ( @@ -24,6 +31,7 @@ const Input = forwardRef(function Input( s.Input, s['Input_' + variant], fullWidth && s.Input_fullWidth, + error && s.Input_error, className, )} /> diff --git a/src/shared/ui/phone-input/index.ts b/src/shared/ui/phone-input/index.ts index e69de29..632d7d1 100644 --- a/src/shared/ui/phone-input/index.ts +++ b/src/shared/ui/phone-input/index.ts @@ -0,0 +1 @@ +export { default as PhoneInput } from './phone-input'; diff --git a/src/shared/ui/phone-input/phone-input.tsx b/src/shared/ui/phone-input/phone-input.tsx index f652e58..f520349 100644 --- a/src/shared/ui/phone-input/phone-input.tsx +++ b/src/shared/ui/phone-input/phone-input.tsx @@ -1,10 +1,25 @@ //import s from './phone-input.module.scss'; 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, HTMLInputElement>; + +export default function PhoneInput({ ...props }: PhoneInput) { + const options = maskitoPhoneOptionsGenerator({ + countryIsoCode: 'RU', + metadata, + }); + const maskedInputRef = useMaskito({ options }); -export default function PhoneInput() { return ( <> - + ); } diff --git a/src/widgets/offer-form/ui.tsx b/src/widgets/offer-form/ui.tsx index d004ee5..f39ccdc 100644 --- a/src/widgets/offer-form/ui.tsx +++ b/src/widgets/offer-form/ui.tsx @@ -1,16 +1,22 @@ 'use client'; 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 { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import toast from 'react-hot-toast'; import { sendFormFn } from '@shared/api/api.service'; +import { isValidPhoneNumber } from 'libphonenumber-js/min'; const FormSchema = z.object({ - name: z.string().min(3), - phone: z.string(), + name: z + .string() + .min(3, { message: 'Поле должно содержать не менее 3-х букв' }) + .regex(/^[A-Za-zА-Яа-яЁё]+(?:[ '-][A-Za-zА-Яа-яЁё]+)*$/, { + message: 'Поле содержит некорректные символы', + }), + phone: z.string().refine(isValidPhoneNumber, 'Некорректный номер телефона'), }); type TForm = z.infer; @@ -67,11 +73,11 @@ export default function OfferForm() { control={control} name={'phone'} render={({ field }) => ( - )} />