"use client";
import { type ReactElement, type ReactNode } from "react";
import { Button, type ButtonProps } from "../button/Button.js";
import { getIcon } from "../icon/config.js";
import { type PropsWithRef } from "../types.js";
import { useEnsuredId } from "../useEnsuredId.js";
import { useToggle } from "../useToggle.js";
import { TextField, type TextFieldProps } from "./TextField.js";
import {
password,
passwordInput,
passwordInputToggle,
} from "./passwordStyles.js";
/**
* @example Simple Example
* ```tsx
* const getVisibilityIcon: GetPasswordVisibilityIcon = (isPasswordVisible) => {
* if (isPasswordVisible) {
* return ;
* }
*
* return ;
* };
* ```
*
* @param isPasswordVisible - `true` when the password is visible and in plain
* text
* @returns a custom icon to use for the password visibility toggle.
*/
export type GetPasswordVisibilityIcon = (
isPasswordVisible: boolean
) => ReactNode;
/**
* @example Simple Example
* ```tsx
* const visibilityIcon: ConfigurableVisibilityIcon = {
* visible: ,
* invisible: ,
* };
* ```
*/
export interface ConfigurableVisibilityIcon {
/**
* The icon to display while the password is currently visible as plain text.
*/
visible: ReactNode;
/**
* The icon to display while the password is currently invisible as the
* password input.
*/
invisible: ReactNode;
}
/**
* @since 6.0.0 Removed the `visibilityStyle`, `visibilityClassName`, and
* `onVisibilityClick` props in favor of `visibilityProps` object.
* @since 6.0.0 Removed the `disableVisibility` and `rightChildren` props in
* favor of just using a `TextField` instead of the `Password` component.
* @since 6.0.0 Merged the `getVisibilityIcon` prop behavior into the
* `visibilityIcon` prop.
*/
export interface PasswordProps extends Omit<
TextFieldProps,
"type" | "rightAddon"
> {
/**
* @defaultValue `"password"`
* @since 6.0.0 Defaults to `"password"`
*/
name?: string;
/**
* @example Configurable Visibility Icon Object
* ```tsx
* ,
* invisible: ,
* }}
* />
* ```
*
* @example Get Password Visibility Icon Function
* ```tsx
* {
* if (isPasswordVisible) {
* return ;
* }
*
* return ;
* }}
* />
* ```
*
* @example Custom Icon
* ```tsx
* }
* />
* ```
*
* @since 6.0.0 Supports dynamically getting the visibility icon with a
* callback function.
*/
visibilityIcon?:
| ConfigurableVisibilityIcon
| GetPasswordVisibilityIcon
| ReactNode;
/**
* The `aria-label` to use for the password visibility icon button.
*
* @defaultValue `"Show password"`
*/
visibilityLabel?: string;
/**
* Any props that should be passed to the password visibility icon button. If
* `id`, `buttonType`, `aria-label`, `aria-pressed`, or `children` are
* provided here, they will override the default implementation.
*/
visibilityProps?: PropsWithRef;
}
/**
* **Client Component**
*
* @example Simple Example
* ```tsx
* import { Password } from "@react-md/core/form/Password";
*
* function Example(): ReactElement {
* return ;
* }
* ```
*
* @see {@link https://react-md.dev/components/password | Password Demos}
*/
export function Password(props: PasswordProps): ReactElement {
const {
ref,
id: propId,
name = "password",
className,
inputClassName,
visibilityIcon: propVisibilityIcon,
visibilityLabel = "Show password",
visibilityProps,
...remaining
} = props;
const { toggled: isPasswordVisible, toggle } = useToggle(false);
let currentVisibilityIcon: ReactNode;
if (
propVisibilityIcon &&
typeof propVisibilityIcon === "object" &&
"visible" in propVisibilityIcon
) {
currentVisibilityIcon = isPasswordVisible
? propVisibilityIcon.visible
: propVisibilityIcon.invisible;
} else if (typeof propVisibilityIcon === "function") {
currentVisibilityIcon = propVisibilityIcon(isPasswordVisible);
} else {
currentVisibilityIcon = propVisibilityIcon;
}
const id = useEnsuredId(propId, "password");
const visibilityIcon = getIcon("password", currentVisibilityIcon);
return (
{
visibilityProps?.onClick?.(event);
if (event.isPropagationStopped()) {
return;
}
toggle();
}}
>
{visibilityProps?.children ?? visibilityIcon}
}
disableRightAddonStyles
/>
);
}