{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "password-input",
  "title": "Password Input",
  "description": "A password field with a reveal toggle and a strength meter.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "input"
  ],
  "files": [
    {
      "path": "registry/default/ui/password-input.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { EyeIcon, EyeOffIcon } from \"lucide-react\"\n\nimport { cn } from \"@/registry/default/lib/utils\"\nimport { Input } from \"@/registry/default/ui/input\"\n\ntype PasswordStrengthScore = 0 | 1 | 2 | 3 | 4\n\ntype PasswordStrengthLabels = readonly [string, string, string, string, string]\n\nconst DEFAULT_STRENGTH_LABELS: PasswordStrengthLabels = [\n  \"Too weak\",\n  \"Weak\",\n  \"Fair\",\n  \"Good\",\n  \"Strong\",\n]\n\nconst STRENGTH_SEGMENT_COUNT = 4\n\nconst SEGMENT_COLOR_BY_SCORE: Record<PasswordStrengthScore, string> = {\n  0: \"bg-muted\",\n  1: \"bg-destructive\",\n  2: \"bg-amber-500\",\n  3: \"bg-emerald-500\",\n  4: \"bg-emerald-500\",\n}\n\nfunction scorePassword(value: string): PasswordStrengthScore {\n  if (value.length === 0) return 0\n\n  const checks = [\n    value.length >= 8,\n    value.length >= 12,\n    /[a-z]/.test(value) && /[A-Z]/.test(value),\n    /\\d/.test(value),\n    /[^A-Za-z0-9]/.test(value),\n  ]\n  const passed = checks.filter(Boolean).length\n\n  return Math.max(1, Math.min(4, passed - 1)) as PasswordStrengthScore\n}\n\nfunction PasswordInput({\n  className,\n  disabled,\n  ...props\n}: Omit<React.ComponentProps<\"input\">, \"type\">) {\n  const [isVisible, setIsVisible] = React.useState(false)\n\n  return (\n    <div data-slot=\"password-input\" className=\"relative\">\n      <Input\n        type={isVisible ? \"text\" : \"password\"}\n        disabled={disabled}\n        className={cn(\"pr-9\", className)}\n        {...props}\n      />\n      <button\n        type=\"button\"\n        data-slot=\"password-input-toggle\"\n        aria-label={isVisible ? \"Hide password\" : \"Show password\"}\n        aria-pressed={isVisible}\n        disabled={disabled}\n        onClick={() => setIsVisible((previous) => !previous)}\n        className=\"absolute inset-y-0 right-0 flex w-9 items-center justify-center rounded-r-md text-muted-foreground transition-colors outline-none hover:text-foreground focus-visible:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4\"\n      >\n        {isVisible ? <EyeOffIcon /> : <EyeIcon />}\n      </button>\n    </div>\n  )\n}\n\nfunction PasswordStrength({\n  value,\n  getScore = scorePassword,\n  labels = DEFAULT_STRENGTH_LABELS,\n  className,\n  ...props\n}: Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  value: string\n  getScore?: (value: string) => PasswordStrengthScore\n  labels?: PasswordStrengthLabels\n}) {\n  const score = getScore(value)\n  const label = labels[score]\n\n  return (\n    <div\n      data-slot=\"password-strength\"\n      data-score={score}\n      className={cn(\"flex items-center gap-3\", className)}\n      {...props}\n    >\n      <div aria-hidden className=\"grid flex-1 grid-cols-4 gap-1\">\n        {Array.from({ length: STRENGTH_SEGMENT_COUNT }, (_, index) => (\n          <span\n            key={index}\n            className={cn(\n              \"h-1 rounded-full bg-muted transition-colors duration-300\",\n              index < score && SEGMENT_COLOR_BY_SCORE[score]\n            )}\n          />\n        ))}\n      </div>\n      <span\n        role=\"status\"\n        aria-live=\"polite\"\n        className=\"min-w-14 text-right text-xs text-muted-foreground\"\n      >\n        <span className=\"sr-only\">Password strength: </span>\n        {value.length > 0 ? label : \"\"}\n      </span>\n    </div>\n  )\n}\n\nexport {\n  PasswordInput,\n  PasswordStrength,\n  scorePassword,\n  type PasswordStrengthScore,\n  type PasswordStrengthLabels,\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}