"use client"; import React, { useState, useEffect, useRef } from "react"; import { cn } from "../../lib/utils"; import { Input, InputProps } from "./input"; import { Label } from "./label"; import { Eye, EyeOff, Check, X } from "lucide-react"; export type StrengthLevel = "empty" | "weak" | "medium" | "strong" | "very-strong"; export interface PasswordStrengthIndicatorProps { /** * The value of the password input */ value: string; /** * Class name for the container */ className?: string; /** * Label text for the password field */ label?: string; /** * Show strength score as text */ showScore?: boolean; /** * Show strength score as number */ showScoreNumber?: boolean; /** * Function called when password changes */ onChange?: (value: string) => void; /** * Function called when strength level changes */ onStrengthChange?: (strength: StrengthLevel) => void; /** * Placeholder text for input */ placeholder?: string; /** * Show toggle for password visibility */ showVisibilityToggle?: boolean; /** * Additional props for the input element */ inputProps?: InputProps; // <--- Use the imported InputProps here } // Password strength calculation based on common rules const calculateStrength = (password: string): { score: number; level: StrengthLevel } => { if (!password) return { score: 0, level: "empty" }; let score = 0; // Length check if (password.length > 5) score += 1; if (password.length > 8) score += 1; // Character variety checks if (/[A-Z]/.test(password)) score += 1; if (/[a-z]/.test(password)) score += 1; if (/[0-9]/.test(password)) score += 1; if (/[^A-Za-z0-9]/.test(password)) score += 1; // Determine level based on score let level: StrengthLevel = "empty"; if (score === 0) level = "empty"; else if (score <= 2) level = "weak"; else if (score <= 4) level = "medium"; else if (score <= 5) level = "strong"; else level = "very-strong"; return { score, level }; }; // Colors for different strength levels const strengthColors = { empty: "bg-gray-200", weak: "bg-red-500", medium: "bg-orange-500", strong: "bg-green-500", "very-strong": "bg-emerald-500", }; // Text labels for different strength levels const strengthLabels = { empty: "Empty", weak: "Weak", medium: "Medium", strong: "Strong", "very-strong": "Very Strong", }; export function PasswordStrengthIndicator({ value, className, label = "Password", showScore = true, showScoreNumber = false, onChange, onStrengthChange, placeholder = "Enter your password", showVisibilityToggle = true, inputProps, }: PasswordStrengthIndicatorProps) { const [password, setPassword] = useState(value || ""); const [showPassword, setShowPassword] = useState(false); const { score, level } = calculateStrength(password); const inputRef = useRef(null); useEffect(() => { if (onStrengthChange) { onStrengthChange(level); } }, [level, onStrengthChange]); const handleChange = (e: React.ChangeEvent) => { const newValue = e.target.value; setPassword(newValue); if (onChange) onChange(newValue); }; const toggleVisibility = () => { setShowPassword(!showPassword); // Focus back on input after toggling visibility setTimeout(() => { if (inputRef.current) inputRef.current.focus(); }, 0); }; return (
{label && (
{showScoreNumber && ( {Math.floor((score / 6) * 10)}/10 )}
)}
{showVisibilityToggle && ( )} {password && (
{level === "weak" ? ( ) : ( )}
)}
{/* Password strength bar */}
{Array.from({ length: 4 }).map((_, i) => (
))}
{/* Strength label */} {showScore && level !== "empty" && (

{strengthLabels[level]}

)}
); }