{
  "$schema": "https://cubixflow.ir/schema/registry-item.json",
  "name": "password-field",
  "type": "registry:ui",
  "title": "Password Field",
  "description": "A labeled password field with icons, visibility toggle, description, and error. type switches between password and text when revealed.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/cubix/password-field.tsx",
      "type": "registry:ui",
      "target": "components/cubix/password-field.tsx",
      "content": "\"use client\"\n\n/*\n  Cubix Password Field - labeled password input specialized from Text Field.\n\n  Weight 400 and tracking-normal keep IRANSans XV readable in fixed-height\n  controls; Latin letter-spacing and medium weight look heavy on Arab script.\n  Icons use data-icon=\"inline-start\" | \"inline-end\" like Button (logical inset).\n  PasswordFieldToggle switches type between password and text for visibility.\n*/\nimport * as React from \"react\"\nimport { Field as FieldPrimitive } from \"@base-ui/react/field\"\nimport { Input as InputPrimitive } from \"@base-ui/react/input\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\n/*\n  Icon-side inline padding matches Button / vertical icon inset:\n  default: 8px · lg: 12px\n*/\n\nconst passwordFieldControlVariants = cva(\n  \"group/password-field-control relative flex w-full min-w-0 items-center rounded-lg border border-input bg-transparent transition-colors outline-none has-disabled:cursor-not-allowed has-disabled:border-transparent has-disabled:bg-muted has-[[data-slot=password-field-input]:focus-visible]:border-primary has-[[data-slot=password-field-input]:focus-visible]:ring-3 has-[[data-slot=password-field-input]:focus-visible]:ring-primary/20 has-[[data-slot=password-field-input]:focus-visible]:ring-offset-1 has-[[data-slot=password-field-input]:focus-visible]:ring-offset-background has-[[data-slot=password-field-input][aria-invalid=true]]:border-destructive has-[[data-slot=password-field-input][aria-invalid=true]]:ring-0 has-[[data-slot=password-field-input][aria-invalid=true]:focus-visible]:border-destructive has-[[data-slot=password-field-input][aria-invalid=true]:focus-visible]:ring-0 dark:bg-input/30 dark:has-disabled:bg-muted dark:has-[[data-slot=password-field-input][aria-invalid=true]]:border-destructive/50 dark:has-[[data-slot=password-field-input][aria-invalid=true]:focus-visible]:border-destructive/50 dark:has-[[data-slot=password-field-input][aria-invalid=true]:focus-visible]:ring-0 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4\",\n  {\n    variants: {\n      size: {\n        default:\n          \"h-10 gap-2 px-3 has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2 [&_svg:not([class*='size-'])]:size-6\",\n        lg: \"h-12 gap-2.5 px-3.5 has-data-[icon=inline-end]:pe-3 has-data-[icon=inline-start]:ps-3 [&_svg:not([class*='size-'])]:size-6\",\n      },\n    },\n    defaultVariants: {\n      size: \"default\",\n    },\n  }\n)\n\nconst passwordFieldInputVariants = cva(\n  \"w-full min-w-0 rounded-lg border border-input bg-transparent font-normal leading-none tracking-normal text-foreground transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-description file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-primary focus-visible:ring-3 focus-visible:ring-primary/20 focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:border-transparent disabled:bg-muted disabled:text-muted-foreground disabled:placeholder:text-muted-foreground aria-invalid:border-destructive aria-invalid:ring-0 aria-invalid:focus-visible:border-destructive aria-invalid:focus-visible:ring-0 dark:bg-input/30 dark:disabled:bg-muted dark:aria-invalid:border-destructive/50 dark:aria-invalid:focus-visible:border-destructive/50\",\n  {\n    variants: {\n      size: {\n        default: \"h-10 px-3 text-label md:text-label\",\n        lg: \"h-12 px-3.5 text-label md:text-label\",\n      },\n      inControl: {\n        true: \"h-full flex-1 rounded-none border-0 bg-transparent p-0 shadow-none ring-0 focus-visible:border-0 focus-visible:ring-0 focus-visible:ring-offset-0 aria-invalid:border-0 aria-invalid:ring-0 aria-invalid:ring-offset-0 disabled:bg-transparent dark:bg-transparent dark:disabled:bg-transparent\",\n        false: \"\",\n      },\n    },\n    defaultVariants: {\n      size: \"default\",\n      inControl: false,\n    },\n  }\n)\n\n/*\n  Passwords are typed in Latin: dir=\"ltr\" keeps trailing symbols in place when\n  the value is revealed, and rtl:text-right keeps the text aligned with a\n  surrounding RTL form (text-align: match-parent is not supported in Chromium).\n*/\nconst PASSWORD_ALIGN_CLASS = \"rtl:text-right\"\n\ntype PasswordFieldSize = NonNullable<VariantProps<typeof passwordFieldInputVariants>[\"size\"]>\n\ntype PasswordFieldVisibilityContextValue = {\n  visible: boolean\n  setVisible: (visible: boolean) => void\n  disabled?: boolean\n}\n\nconst PasswordFieldSizeContext = React.createContext<PasswordFieldSize>(\"default\")\nconst PasswordFieldControlContext = React.createContext(false)\nconst PasswordFieldVisibilityContext =\n  React.createContext<PasswordFieldVisibilityContextValue | null>(null)\n\nconst DEFAULT_SHOW_LABEL = \"نمایش رمز عبور\"\nconst DEFAULT_HIDE_LABEL = \"مخفی کردن رمز عبور\"\n\nfunction PasswordField({\n  className,\n  size = \"default\",\n  visible: visibleProp,\n  defaultVisible = false,\n  onVisibleChange,\n  children,\n  disabled,\n  ...props\n}: React.ComponentProps<typeof FieldPrimitive.Root> & {\n  size?: PasswordFieldSize\n  visible?: boolean\n  defaultVisible?: boolean\n  onVisibleChange?: (visible: boolean) => void\n}) {\n  const [uncontrolledVisible, setUncontrolledVisible] = React.useState(defaultVisible)\n  const isVisibilityControlled = visibleProp !== undefined\n  const visible = isVisibilityControlled ? Boolean(visibleProp) : uncontrolledVisible\n  const setVisible = React.useCallback(\n    (next: boolean) => {\n      if (!isVisibilityControlled) {\n        setUncontrolledVisible(next)\n      }\n      onVisibleChange?.(next)\n    },\n    [isVisibilityControlled, onVisibleChange]\n  )\n\n  return (\n    <PasswordFieldVisibilityContext.Provider value={{ visible, setVisible, disabled }}>\n      <PasswordFieldSizeContext.Provider value={size}>\n        <FieldPrimitive.Root\n          data-slot=\"password-field\"\n          data-size={size}\n          disabled={disabled}\n          className={cn(\n            \"group/password-field flex w-full flex-col gap-2 data-disabled:cursor-not-allowed data-disabled:opacity-70\",\n            className\n          )}\n          {...props}\n        >\n          {children}\n        </FieldPrimitive.Root>\n      </PasswordFieldSizeContext.Provider>\n    </PasswordFieldVisibilityContext.Provider>\n  )\n}\n\nfunction PasswordFieldLabel({\n  className,\n  ...props\n}: React.ComponentProps<typeof FieldPrimitive.Label>) {\n  return (\n    <FieldPrimitive.Label\n      data-slot=\"password-field-label\"\n      className={cn(\n        \"flex w-fit items-center gap-2 text-label leading-none font-normal text-foreground select-none group-data-disabled/password-field:cursor-not-allowed group-data-disabled/password-field:text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction PasswordFieldControl({\n  className,\n  size: sizeProp,\n  onClick,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  size?: PasswordFieldSize\n}) {\n  const contextSize = React.useContext(PasswordFieldSizeContext)\n  const size = sizeProp ?? contextSize\n\n  return (\n    <PasswordFieldControlContext.Provider value={true}>\n      <div\n        data-slot=\"password-field-control\"\n        data-size={size}\n        className={cn(passwordFieldControlVariants({ size }), className)}\n        onClick={(event) => {\n          onClick?.(event)\n          if (event.defaultPrevented) return\n          if ((event.target as HTMLElement).closest(\"button,a,input,textarea\")) {\n            return\n          }\n          const input = event.currentTarget.querySelector<HTMLInputElement>(\n            \"[data-slot=password-field-input]\"\n          )\n          if (!input || input.disabled || input.readOnly) return\n          input.focus()\n        }}\n        {...props}\n      />\n    </PasswordFieldControlContext.Provider>\n  )\n}\n\nfunction PasswordFieldInput({\n  className,\n  size: sizeProp,\n  autoComplete = \"current-password\",\n  dir = \"ltr\",\n  ...props\n}: Omit<React.ComponentProps<typeof InputPrimitive>, \"size\" | \"type\" | \"spellCheck\"> & {\n  size?: PasswordFieldSize\n}) {\n  const contextSize = React.useContext(PasswordFieldSizeContext)\n  const size = sizeProp ?? contextSize\n  const inControl = React.useContext(PasswordFieldControlContext)\n  const visible = React.useContext(PasswordFieldVisibilityContext)?.visible ?? false\n\n  return (\n    <InputPrimitive\n      data-slot=\"password-field-input\"\n      dir={dir}\n      data-size={size}\n      className={cn(\n        passwordFieldInputVariants({ size, inControl }),\n        PASSWORD_ALIGN_CLASS,\n        className\n      )}\n      {...props}\n      type={visible ? \"text\" : \"password\"}\n      autoComplete={autoComplete}\n      spellCheck={false}\n    />\n  )\n}\n\nfunction PasswordFieldEyeIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"1.5\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      aria-hidden=\"true\"\n      className={className}\n    >\n      <path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\" />\n      <circle cx=\"12\" cy=\"12\" r=\"3\" />\n    </svg>\n  )\n}\n\nfunction PasswordFieldEyeOffIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"1.5\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      aria-hidden=\"true\"\n      className={className}\n    >\n      <path d=\"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\" />\n      <path d=\"M14.084 14.158a3 3 0 0 1-4.242-4.242\" />\n      <path d=\"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\" />\n      <path d=\"m2 2 20 20\" />\n    </svg>\n  )\n}\n\nfunction PasswordFieldToggle({\n  className,\n  onClick,\n  disabled,\n  showLabel = DEFAULT_SHOW_LABEL,\n  hideLabel = DEFAULT_HIDE_LABEL,\n  ...props\n}: Omit<React.ComponentProps<\"button\">, \"children\"> & {\n  showLabel?: string\n  hideLabel?: string\n}) {\n  const visibility = React.useContext(PasswordFieldVisibilityContext)\n  const visible = visibility?.visible ?? false\n  const isDisabled = disabled ?? visibility?.disabled\n  const label = visible ? hideLabel : showLabel\n\n  return (\n    <button\n      type=\"button\"\n      data-slot=\"password-field-toggle\"\n      data-icon=\"inline-end\"\n      data-state={visible ? \"visible\" : \"hidden\"}\n      aria-label={label}\n      disabled={isDisabled}\n      className={cn(\n        \"inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-primary/20 disabled:pointer-events-none group-data-disabled/password-field:cursor-not-allowed\",\n        className\n      )}\n      onClick={(event) => {\n        onClick?.(event)\n        if (event.defaultPrevented) return\n        const next = !visible\n        visibility?.setVisible(next)\n        const input = event.currentTarget\n          .closest(\"[data-slot=password-field-control]\")\n          ?.querySelector<HTMLInputElement>(\"[data-slot=password-field-input]\")\n        input?.focus()\n      }}\n      {...props}\n    >\n      {visible ? (\n        <PasswordFieldEyeOffIcon className=\"size-4\" />\n      ) : (\n        <PasswordFieldEyeIcon className=\"size-4\" />\n      )}\n    </button>\n  )\n}\n\nfunction PasswordFieldDescription({\n  className,\n  ...props\n}: React.ComponentProps<typeof FieldPrimitive.Description>) {\n  return (\n    <FieldPrimitive.Description\n      data-slot=\"password-field-description\"\n      className={cn(\n        \"m-0 text-caption font-normal text-muted-foreground group-data-disabled/password-field:cursor-not-allowed\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction PasswordFieldError({\n  className,\n  children,\n  match,\n  ...props\n}: React.ComponentProps<typeof FieldPrimitive.Error>) {\n  return (\n    <FieldPrimitive.Validity>\n      {(validity) => {\n        const isInvalid = validity.validity.valid === false\n        if (!isInvalid && match !== true) {\n          return null\n        }\n\n        return (\n          <FieldPrimitive.Error\n            data-slot=\"password-field-error\"\n            match={match ?? true}\n            className={cn(\"m-0 text-caption font-normal text-destructive\", className)}\n            {...props}\n          >\n            {children}\n          </FieldPrimitive.Error>\n        )\n      }}\n    </FieldPrimitive.Validity>\n  )\n}\n\nexport {\n  PasswordField,\n  PasswordFieldLabel,\n  PasswordFieldControl,\n  PasswordFieldInput,\n  PasswordFieldToggle,\n  PasswordFieldDescription,\n  PasswordFieldError,\n  passwordFieldControlVariants,\n  passwordFieldInputVariants,\n}\nexport type { PasswordFieldSize }\n"
    }
  ]
}

