-
Star
(171)
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 |
Shadcn components supposed to support ssr , so converting it directly to Client component doesnt make sense. I hope there's any better way to toggle.
Shadcn components supposed to support ssr , so converting it directly to Client component doesnt make sense. I hope there's any better way to toggle.
Server components don't support hooks. So any UI, shad or otherwise, that requires interactivity, needs the use client directive.
https://react.dev/reference/rsc/server-components#adding-interactivity-to-server-components
My Input
component doesn't have InputProps
, so I made this small change:
Before:
import { Input, type InputProps } from '@/components/ui/input'
const PasswordInput = React.forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => {
After:
import { Input } from '@/components/ui/input'
const PasswordInput = React.forwardRef<HTMLInputElement, React.ComponentProps<typeof Input>>(({ className, ...props }, ref) => {
In other words, I simply replaced InputProps
with React.ComponentProps<typeof Input>
.
My
Input
component doesn't haveInputProps
, so I made this small change:Before:
import { Input, type InputProps } from '@/components/ui/input' const PasswordInput = React.forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => {After:
import { Input } from '@/components/ui/input' const PasswordInput = React.forwardRef<HTMLInputElement, React.ComponentProps<typeof Input>>(({ className, ...props }, ref) => {In other words, I simply replaced
InputProps
withReact.ComponentProps<typeof Input>
.
That works; but you can also just export InputProps from the input
file like @fillsanches recommended here
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
@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.
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
thanks