Skip to content

Commit

Permalink
feat: implement password input masking
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanhopperlowe authored and cjellick committed Dec 9, 2024
1 parent 224d7e7 commit fdb8473
Showing 1 changed file with 51 additions and 11 deletions.
62 changes: 51 additions & 11 deletions ui/admin/app/components/ui/input.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { type VariantProps, cva } from "class-variance-authority";
import { EyeIcon, EyeOffIcon } from "lucide-react";
import * as React from "react";

import { cn } from "~/lib/utils";

import { buttonVariants } from "~/components/ui/button";

const inputVariants = cva(
"flex h-9 w-full rounded-md px-3 bg-transparent border border-input text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
cn(
"flex items-center h-9 w-full rounded-md bg-transparent border border-input text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50"
),
{
variants: {
variant: {
default: "",
ghost: "shadow-none cursor-pointer hover:border-primary px-0 mb-0 font-bold outline-none border-transparent focus:border-primary",
ghost: "shadow-none cursor-pointer hover:border-primary px-0 mb-0 font-bold outline-none border-transparent has-[:focus-visible]:border-primary",
},
},
defaultVariants: {
Expand All @@ -20,18 +25,53 @@ const inputVariants = cva(

export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement>,
VariantProps<typeof inputVariants> {}
VariantProps<typeof inputVariants> {
disableToggle?: boolean;
}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, variant, type, ...props }, ref) => {
({ className, variant, type, disableToggle = false, ...props }, ref) => {
const isPassword = type === "password";
const [isVisible, setIsVisible] = React.useState(false);

const internalType = isPassword
? isVisible && !disableToggle
? "text"
: "password"
: type;

const toggleVisible = React.useCallback(
() => setIsVisible((prev) => !prev),
[]
);

return (
<input
type={type}
data-1p-ignore={type !== "password"}
className={cn(inputVariants({ variant, className }))}
ref={ref}
{...props}
/>
<div className={cn(inputVariants({ variant, className }))}>
<input
type={internalType}
data-1p-ignore={!isPassword}
className={cn(
"w-full p-3 bg-transparent border-none focus-visible:border-none focus-visible:outline-none rounded-full"
)}
ref={ref}
{...props}
/>

{isPassword && !disableToggle && (
<button
type="button"
onClick={toggleVisible}
className={buttonVariants({
variant: "ghost",
size: "icon",
shape: "default",
className: "min-h-full !h-full rounded-s-none",
})}
>
{!isVisible ? <EyeIcon /> : <EyeOffIcon />}
</button>
)}
</div>
);
}
);
Expand Down

0 comments on commit fdb8473

Please sign in to comment.