-
Star
(177)
You must be signed in to star a gist -
Fork
(13)
You must be signed in to fork a gist
-
-
Save mjbalcueva/b21f39a8787e558d4c536bf68e267398 to your computer and use it in GitHub Desktop.
'use client' | |
import * as React from 'react' | |
import { EyeIcon, EyeOffIcon } from 'lucide-react' | |
import { Button } from '@/components/ui/button' | |
import { Input, type InputProps } from '@/components/ui/input' | |
import { cn } from '@/lib/utils' | |
const PasswordInput = React.forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => { | |
const [showPassword, setShowPassword] = React.useState(false) | |
const disabled = props.value === '' || props.value === undefined || props.disabled | |
return ( | |
<div className="relative"> | |
<Input | |
type={showPassword ? 'text' : 'password'} | |
className={cn('hide-password-toggle pr-10', className)} | |
ref={ref} | |
{...props} | |
/> | |
<Button | |
type="button" | |
variant="ghost" | |
size="sm" | |
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent" | |
onClick={() => setShowPassword((prev) => !prev)} | |
disabled={disabled} | |
> | |
{showPassword && !disabled ? ( | |
<EyeIcon className="h-4 w-4" aria-hidden="true" /> | |
) : ( | |
<EyeOffIcon className="h-4 w-4" aria-hidden="true" /> | |
)} | |
<span className="sr-only">{showPassword ? 'Hide password' : 'Show password'}</span> | |
</Button> | |
{/* hides browsers password toggles */} | |
<style>{` | |
.hide-password-toggle::-ms-reveal, | |
.hide-password-toggle::-ms-clear { | |
visibility: hidden; | |
pointer-events: none; | |
display: none; | |
} | |
`}</style> | |
</div> | |
) | |
}) | |
PasswordInput.displayName = 'PasswordInput' | |
export { PasswordInput } |
"use client" | |
import { useState } from "react" | |
import { PasswordInput } from "@/components/password-input" | |
import { Button } from "@/components/ui/button" | |
import { Label } from "@/components/ui/label" | |
const SampleUseCase = () => { | |
const [currentPassword, setCurrentPassword] = useState("") | |
const [password, setPassword] = useState("") | |
const [passwordConfirmation, setPasswordConfirmation] = useState("") | |
return ( | |
<div className="space-y-4"> | |
<div> | |
<Label htmlFor="current_password">Current Password</Label> | |
<PasswordInput | |
id="current_password" | |
value={currentPassword} | |
onChange={(e) => setCurrentPassword(e.target.value)} | |
autoComplete="current-password" | |
/> | |
</div> | |
<div> | |
<Label htmlFor="password">New Password</Label> | |
<PasswordInput | |
id="password" | |
value={password} | |
onChange={(e) => setPassword(e.target.value)} | |
autoComplete="new-password" | |
/> | |
</div> | |
<div> | |
<Label htmlFor="password_confirmation">Confirm Password</Label> | |
<PasswordInput | |
id="password_confirmation" | |
value={passwordConfirmation} | |
onChange={(e) => setPasswordConfirmation(e.target.value)} | |
autoComplete="new-password" | |
/> | |
</div> | |
<Button type="submit">Save</Button> | |
</div> | |
) | |
} | |
export default SampleUseCase |
thank you, it works perfectly
Based on your ideas, I'm sharing my results with you. Sorry for the language in the code; I speak Spanish.
https://codesandbox.io/p/devbox/pruebas-con-tailwindcss-ngjr23
import { cn } from "@/lib/shadcn/cn";
import React from "react";interface BoxProps extends React.HTMLAttributes {}
const Box = React.forwardRef<HTMLDivElement, BoxProps>(
({ className, ...props }, ref) => (
<div className={cn(className)} ref={ref} {...props} />
),
);Box.displayName = "Box";
export { Box };
This is nice, thank you.
Then I have a mini addition to this if you don't want ESLint error: "no-empty-box-type"
, by using the type directly like this:
import { cn } from "@/lib/utils";
import React from "react";
type BoxProps = React.HTMLAttributes<HTMLDivElement>
const Box = React.forwardRef<HTMLDivElement, BoxProps>(
({ className, ...props }, ref) => (
<div className={cn(className)} ref={ref} {...props} />
),
);
Box.displayName = "Box";
export { Box };
Nice solution!
If you're looking for a reusable and customizable alternative, I created a small React hook package: @omergulcicek/password-input.
It supports TypeScript and lets you use your own icons.
@abustamam
Got it. It's better this way because if I want to add a customization, it will reflect on everyone using the Input, centralizing the typing. Thanks.