-
Star
(183)
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.
I can't click on the button somehow. I'm on tailwind v3 and shadcn legacy.
Edit: well the problem is the value props. I don't use controlled form :))
@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.