Skip to content

Commit 97ffbc5

Browse files
committed
fix: fix issue [APP-10752]
1 parent a70023b commit 97ffbc5

1 file changed

Lines changed: 50 additions & 52 deletions

File tree

src/client/components/ui/secret-input.tsx

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react';
2+
import { LuX } from 'react-icons/lu';
23
import { cn } from '@/utils/style';
34

45
export interface SecretInputProps
@@ -13,6 +14,11 @@ export interface SecretInputProps
1314
* @default "******"
1415
*/
1516
maskPlaceholder?: string;
17+
/**
18+
* Whether to allow clearing the existing value via the clear button.
19+
* @default true
20+
*/
21+
allowClear?: boolean;
1622
}
1723

1824
/**
@@ -42,70 +48,62 @@ const SecretInput = React.forwardRef<HTMLInputElement, SecretInputProps>(
4248
onChange,
4349
maskPlaceholder = '******',
4450
placeholder,
51+
allowClear = true,
52+
disabled,
53+
onFocus,
4554
...props
4655
},
4756
ref
4857
) => {
4958
const [internalValue, setInternalValue] = React.useState('');
50-
const [isFocused, setIsFocused] = React.useState(false);
51-
const [hasBeenTouched, setHasBeenTouched] = React.useState(false);
59+
const [hasInteracted, setHasInteracted] = React.useState(false);
5260

53-
// Check if there's an existing value (from props)
5461
const hasExistingValue = Boolean(value);
62+
const showMask = hasExistingValue && !hasInteracted;
63+
const showClearBtn =
64+
allowClear && !disabled && (hasExistingValue || internalValue !== '');
5565

56-
// Determine what placeholder to show
57-
const effectivePlaceholder = React.useMemo(() => {
58-
if (hasBeenTouched) {
59-
// After user has interacted, show normal placeholder
60-
return placeholder;
61-
}
62-
if (hasExistingValue && !isFocused) {
63-
// Show masked placeholder when there's existing value and not focused
64-
return maskPlaceholder;
65-
}
66-
// Show normal placeholder
67-
return placeholder;
68-
}, [
69-
hasBeenTouched,
70-
hasExistingValue,
71-
isFocused,
72-
placeholder,
73-
maskPlaceholder,
74-
]);
75-
76-
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
77-
setIsFocused(true);
78-
setHasBeenTouched(true);
79-
props.onFocus?.(e);
80-
};
81-
82-
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
83-
setIsFocused(false);
84-
props.onBlur?.(e);
85-
};
86-
87-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
88-
const newValue = e.target.value;
89-
setInternalValue(newValue);
90-
setHasBeenTouched(true);
91-
onChange?.(newValue);
66+
const emit = (next: string) => {
67+
setHasInteracted(true);
68+
setInternalValue(next);
69+
onChange?.(next);
9270
};
9371

9472
return (
95-
<input
96-
type="password"
97-
className={cn(
98-
'flex h-9 w-full rounded-md border border-zinc-200 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-zinc-500 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-zinc-950 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-800 dark:placeholder:text-zinc-400 dark:focus-visible:ring-zinc-300',
99-
className
73+
<div className="relative w-full">
74+
<input
75+
{...props}
76+
ref={ref}
77+
type="password"
78+
className={cn(
79+
'flex h-9 w-full rounded-md border border-zinc-200 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-zinc-500 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-zinc-950 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-800 dark:placeholder:text-zinc-400 dark:focus-visible:ring-zinc-300',
80+
showClearBtn && 'pr-8',
81+
className
82+
)}
83+
value={internalValue}
84+
placeholder={showMask ? maskPlaceholder : placeholder}
85+
disabled={disabled}
86+
onChange={(e) => emit(e.target.value)}
87+
onFocus={(e) => {
88+
setHasInteracted(true);
89+
onFocus?.(e);
90+
}}
91+
/>
92+
{showClearBtn && (
93+
<button
94+
type="button"
95+
tabIndex={-1}
96+
aria-label="Clear"
97+
onClick={(e) => {
98+
e.preventDefault();
99+
emit('');
100+
}}
101+
className="absolute right-2 top-1/2 flex h-5 w-5 -translate-y-1/2 items-center justify-center rounded-sm text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100"
102+
>
103+
<LuX className="h-3.5 w-3.5" />
104+
</button>
100105
)}
101-
ref={ref}
102-
value={internalValue}
103-
onChange={handleChange}
104-
onFocus={handleFocus}
105-
onBlur={handleBlur}
106-
placeholder={effectivePlaceholder}
107-
{...props}
108-
/>
106+
</div>
109107
);
110108
}
111109
);

0 commit comments

Comments
 (0)