import { EyeDuotoneIcon, EyeRegularIcon, } from "alouette-icons/phosphor-icons/Eye"; import { EyeSlashDuotoneIcon, EyeSlashRegularIcon, } from "alouette-icons/phosphor-icons/EyeSlash"; import type { ReactNode } from "react"; import { useControllableChecked } from "../../core/useControllableChecked"; import { IconButton } from "../actions/IconButton"; import { InputText, type InputTextProps } from "./InputText"; export interface InputPasswordProps extends Omit< InputTextProps, "endSlot" | "mode" | "multiline" | "secureTextEntry" > { /** Controlled: the password shows in clear. */ visible?: boolean; /** Initial state for uncontrolled usage. Defaults to hidden. */ defaultVisible?: boolean; onVisibleChange?: (visible: boolean) => void; /** * Accessible name of the eye toggle. It never changes: the toggle is * `aria-pressed` while the password shows. Defaults to "Show password". */ toggleLabel?: string; } /** * `InputText` in `password` mode with an eye toggle in its `endSlot` to show * what was typed. `autoComplete` is `current-password` from the mode; pass * `autoComplete="new-password"` on a sign-up form. */ export function InputPassword({ visible: controlledVisible, defaultVisible, onVisibleChange, toggleLabel = "Show password", disabled, ...props }: InputPasswordProps): ReactNode { const [visible, setVisible] = useControllableChecked({ checked: controlledVisible, defaultChecked: defaultVisible, onValueChange: onVisibleChange, }); // react-native's types have no `aria-pressed` (its accessibilityState has no // pressed), but react-native-web forwards it and native ignores it. const ariaPressedProps = { "aria-pressed": visible }; return ( : } activeIcon={visible ? : } aria-label={toggleLabel} {...ariaPressedProps} disabled={disabled} onPress={() => { setVisible(!visible); }} /> } {...props} /> ); }