TypeScript 97.5%
SQL 1.4%
Python 0.8%
1"use client";2import * as React from "react";3import { Eye, EyeOff } from "lucide-react";4import { Input } from "@/components/ui/input";5import { cn } from "@/lib/utils";67export function PasswordInput({ className, ...props }: React.InputHTMLAttributes<HTMLInputElement>) {8 const [show, setShow] = React.useState(false);9 return (10 <div className="relative">11 <Input type={show ? "text" : "password"} className={cn("pr-10", className)} {...props} />12 <button type="button" onClick={() => setShow((s) => !s)} className="absolute inset-y-0 right-0 flex w-10 items-center justify-center text-fg-subtle hover:text-fg" aria-label={show ? "Hide password" : "Show password"} tabIndex={-1}>13 {show ? <EyeOff className="size-4" /> : <Eye className="size-4" />}14 </button>15 </div>16 );17}1819export const PASSWORD_RULES = [20 { id: "len", label: "At least 10 characters", test: (p: string) => p.length >= 10 },21 { id: "case", label: "Upper and lower case letters", test: (p: string) => /[a-z]/.test(p) && /[A-Z]/.test(p) },22 { id: "num", label: "A number or symbol", test: (p: string) => /[\d\W_]/.test(p) },23];2425export function PasswordRequirements({ password }: { password: string }) {26 return (27 <ul className="mt-2 grid gap-1 text-[12px]" aria-live="polite">28 {PASSWORD_RULES.map((r) => {29 const ok = r.test(password);30 return (31 <li key={r.id} className={cn("flex items-center gap-2 transition-colors", ok ? "text-success" : "text-fg-subtle")}>32 <span className={cn("inline-block size-1.5 rounded-full", ok ? "bg-success" : "bg-border-strong")} aria-hidden />33 {r.label}34 </li>35 );36 })}37 </ul>38 );39}4041export function passwordStrong(p: string): boolean {42 return PASSWORD_RULES.every((r) => r.test(p));43}44