Files
sfera/src/components/ui/phone-input.tsx

108 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import * as React from "react"
import { IMaskInput } from "react-imask"
import { cn } from "@/lib/utils"
export interface PhoneInputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value'> {
onChange?: (value: string) => void
value?: string
}
const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
({ className, onChange, value, ...props }, ref) => {
const handleAccept = (value: string) => {
onChange?.(value)
}
// Фильтруем пропсы, которые могут конфликтовать с IMaskInput
const { min, max, step, ...filteredProps } = props
return (
<IMaskInput
mask="+7 (000) 000-00-00"
value={value}
onAccept={handleAccept}
inputRef={ref}
{...filteredProps}
className={cn(
"flex h-12 w-full rounded-lg border border-input bg-background px-4 py-3 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
"transition-all duration-200 hover:border-primary/50 focus:border-primary",
"cursor-pointer", // Добавляем cursor pointer в соответствии с предпочтениями пользователя
className
)}
/>
)
}
)
PhoneInput.displayName = "PhoneInput"
const GlassPhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
({ className, onChange, value, ...props }, ref) => {
const [isFocused, setIsFocused] = React.useState(false)
const handleAccept = (value: string) => {
onChange?.(value)
}
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
setIsFocused(true)
props.onFocus?.(e)
}
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
setIsFocused(false)
props.onBlur?.(e)
}
// Проверяем валидность номера
const isValid = value ? value.replace(/\D/g, '').length === 11 : false
const isEmpty = !value || value.replace(/\D/g, '').length === 0
// Фильтруем пропсы, которые могут конфликтовать с IMaskInput
const { min, max, step, onFocus, onBlur, ...filteredProps } = props
return (
<div className="relative">
<IMaskInput
mask="+7 (000) 000-00-00"
value={value}
onAccept={handleAccept}
onFocus={handleFocus}
onBlur={handleBlur}
inputRef={ref}
{...filteredProps}
className={cn(
"glass-input text-white placeholder:text-white/50 selection:bg-purple-500/30 flex h-12 w-full rounded-lg px-4 py-3 text-base font-medium outline-none cursor-pointer transition-all duration-300",
isFocused && "ring-2 ring-purple-400/50 border-purple-400/30",
isValid && !isFocused && "border-green-400/30 bg-green-500/5",
!isEmpty && !isValid && !isFocused && "border-yellow-400/30 bg-yellow-500/5",
className
)}
/>
{/* Индикатор валидности */}
<div className="absolute right-3 top-1/2 transform -translate-y-1/2 pointer-events-none">
{isValid && (
<div className="w-5 h-5 rounded-full bg-green-500/20 border border-green-400/30 flex items-center justify-center">
<svg className="w-3 h-3 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
)}
{!isEmpty && !isValid && (
<div className="w-5 h-5 rounded-full bg-yellow-500/20 border border-yellow-400/30 flex items-center justify-center">
<svg className="w-3 h-3 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.464 0L4.34 16.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
</div>
)}
</div>
</div>
)
}
)
GlassPhoneInput.displayName = "GlassPhoneInput"
export { PhoneInput, GlassPhoneInput }