Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 526x 526x 526x 526x | import {type ReactElement, useState} from 'react';
import {IconButton, InputAdornment, TextField, type TextFieldProps} from '@mui/material';
import {Visibility, VisibilityOff} from '@mui/icons-material';
/**
* A password TextField with a built-in show/hide toggle (FSC-131).
*
* Accepts every standard MUI TextField prop; the `type` is managed internally
* and flips between `password` and `text` when the user clicks the eye icon.
* Existing `slotProps` (e.g. `htmlInput` constraints) are preserved.
*/
export default function PasswordField(props: Omit<TextFieldProps, 'type'>): ReactElement {
const [show, setShow] = useState(false);
const {slotProps, ...rest} = props;
const toggle = (
<InputAdornment position="end">
<IconButton
aria-label={show ? 'Hide password' : 'Show password'}
onClick={(): void => {
setShow((prev) => !prev);
}}
onMouseDown={(e): void => {
// Keep focus in the field when toggling.
e.preventDefault();
}}
edge="end"
// Skip in tab order so keyboard flow goes field -> submit, not field -> eye.
tabIndex={-1}
>
{show ? <VisibilityOff/> : <Visibility/>}
</IconButton>
</InputAdornment>
);
return (
<TextField
{...rest}
type={show ? 'text' : 'password'}
slotProps={{
...slotProps,
input: {
...(typeof slotProps?.input === 'object' ? slotProps.input : {}),
endAdornment: toggle
}
}}
/>
);
}
|