UNPKG

@carbon/react

Version:

React components for the Carbon Design System

206 lines (204 loc) 8.46 kB
/** * Copyright IBM Corp. 2016, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { usePrefix } from "../../internal/usePrefix.js"; import { deprecate } from "../../prop-types/deprecate.js"; import { Tooltip } from "../Tooltip/Tooltip.js"; import { useNormalizedInputProps } from "../../internal/useNormalizedInputProps.js"; import { FormContext } from "../FluidForm/FormContext.js"; import { getTextInputProps } from "./util.js"; import classNames from "classnames"; import { forwardRef, useContext, useEffect, useState } from "react"; import PropTypes from "prop-types"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { View, ViewOff } from "@carbon/icons-react"; //#region src/components/TextInput/PasswordInput.tsx /** * Copyright IBM Corp. 2023, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const PasswordInput = forwardRef(({ className, disabled = false, helperText, hideLabel, hidePasswordLabel = "Hide password", id, inline, invalid = false, invalidText, labelText, light, onChange = () => {}, onClick = () => {}, onTogglePasswordVisibility, placeholder, readOnly, size = "md", showPasswordLabel = "Show password", tooltipPosition = "bottom", tooltipAlignment = "end", type = "password", warn = false, warnText, ...rest }, ref) => { const [inputType, setInputType] = useState(type); const prefix = usePrefix(); const normalizedProps = useNormalizedInputProps({ id, invalid, invalidText, warn, warnText, readOnly, disabled }); const { isFluid } = useContext(FormContext); const handleTogglePasswordVisibility = (event) => { setInputType(inputType === "password" ? "text" : "password"); onTogglePasswordVisibility?.(event); }; const sharedTextInputProps = { id, onChange: (evt) => { if (!disabled) onChange(evt); }, onClick: (evt) => { if (!disabled) onClick(evt); }, placeholder, type: inputType, className: classNames(`${prefix}--text-input`, `${prefix}--password-input`, className, { [`${prefix}--text-input--light`]: light, [`${prefix}--text-input--invalid`]: normalizedProps.invalid, [`${prefix}--text-input--warning`]: normalizedProps.warn, [`${prefix}--text-input--${size}`]: size, [`${prefix}--layout--size-${size}`]: size }), readOnly, ref, ...rest }; const inputWrapperClasses = classNames(`${prefix}--form-item`, `${prefix}--text-input-wrapper`, `${prefix}--password-input-wrapper`, { [`${prefix}--text-input-wrapper--readonly`]: readOnly, [`${prefix}--text-input-wrapper--light`]: light, [`${prefix}--text-input-wrapper--inline`]: inline, [`${prefix}--text-input--fluid`]: isFluid }); const labelClasses = classNames(`${prefix}--label`, { [`${prefix}--visually-hidden`]: hideLabel, [`${prefix}--label--disabled`]: disabled, [`${prefix}--label--inline`]: inline, [`${prefix}--label--inline--${size}`]: inline && !!size }); const helperTextClasses = classNames(`${prefix}--form__helper-text`, { [`${prefix}--form__helper-text--disabled`]: disabled, [`${prefix}--form__helper-text--inline`]: inline }); const fieldOuterWrapperClasses = classNames(`${prefix}--text-input__field-outer-wrapper`, { [`${prefix}--text-input__field-outer-wrapper--inline`]: inline }); const fieldWrapperClasses = classNames(`${prefix}--text-input__field-wrapper`, { [`${prefix}--text-input__field-wrapper--warning`]: normalizedProps.warn }); const iconClasses = classNames({ [`${prefix}--text-input__invalid-icon`]: normalizedProps.invalid || normalizedProps.warn, [`${prefix}--text-input__invalid-icon--warning`]: normalizedProps.warn }); const label = typeof labelText !== "undefined" && labelText !== null && /* @__PURE__ */ jsx("label", { htmlFor: id, className: labelClasses, children: labelText }); const helper = typeof helperText !== "undefined" && helperText !== null && /* @__PURE__ */ jsx("div", { id: normalizedProps.helperId, className: helperTextClasses, children: helperText }); const passwordIsVisible = inputType === "text"; const passwordVisibilityIcon = passwordIsVisible ? /* @__PURE__ */ jsx(ViewOff, { className: `${prefix}--icon-visibility-off` }) : /* @__PURE__ */ jsx(View, { className: `${prefix}--icon-visibility-on` }); const passwordVisibilityToggleClasses = classNames(`${prefix}--text-input--password__visibility__toggle`, `${prefix}--btn`, `${prefix}--tooltip__trigger`, `${prefix}--tooltip--a11y`, { [`${prefix}--tooltip--${tooltipPosition}`]: tooltipPosition, [`${prefix}--tooltip--align-${tooltipAlignment}`]: tooltipAlignment }); const tooltipClasses = classNames(`${prefix}--toggle-password-tooltip`, `${prefix}--icon-tooltip`); let align = void 0; if (tooltipPosition === "top" || tooltipPosition === "bottom") { if (tooltipAlignment === "center") align = tooltipPosition; if (tooltipAlignment === "end") align = `${tooltipPosition}-end`; if (tooltipAlignment === "start") align = `${tooltipPosition}-start`; } if (tooltipPosition === "right" || tooltipPosition === "left") align = tooltipPosition; if (!hidePasswordLabel || hidePasswordLabel.trim() === "") console.warn("Warning: The \"hidePasswordLabel\" should not be blank."); else if (!showPasswordLabel || showPasswordLabel.trim() === "") console.warn("Warning: The \"showPasswordLabel\" should not be blank."); const input = /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("input", { ...getTextInputProps({ sharedTextInputProps, invalid: normalizedProps.invalid, invalidId: normalizedProps.invalidId, warn: normalizedProps.warn, warnId: normalizedProps.warnId, hasHelper: Boolean(helperText && !isFluid && (inline || !inline && !normalizedProps.validation)), helperId: normalizedProps.helperId }), disabled, "data-toggle-password-visibility": inputType === "password" }), isFluid && /* @__PURE__ */ jsx("hr", { className: `${prefix}--text-input__divider` }), /* @__PURE__ */ jsx(Tooltip, { align, className: tooltipClasses, label: passwordIsVisible ? hidePasswordLabel : showPasswordLabel, children: /* @__PURE__ */ jsx("button", { type: "button", className: passwordVisibilityToggleClasses, disabled, onClick: handleTogglePasswordVisibility, children: passwordVisibilityIcon }) }) ] }); useEffect(() => { setInputType(type); }, [type]); const Icon = normalizedProps.icon; return /* @__PURE__ */ jsxs("div", { className: inputWrapperClasses, children: [!inline ? label : /* @__PURE__ */ jsxs("div", { className: `${prefix}--text-input__label-helper-wrapper`, children: [label, !isFluid && helper] }), /* @__PURE__ */ jsxs("div", { className: fieldOuterWrapperClasses, children: [/* @__PURE__ */ jsxs("div", { className: fieldWrapperClasses, "data-invalid": normalizedProps.invalid || null, children: [ Icon && /* @__PURE__ */ jsx(Icon, { className: iconClasses }), input, isFluid && !inline && normalizedProps.validation ] }), !isFluid && !inline && (normalizedProps.validation || helper)] })] }); }); PasswordInput.displayName = "PasswordInput"; PasswordInput.propTypes = { className: PropTypes.string, defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), disabled: PropTypes.bool, helperText: PropTypes.node, hideLabel: PropTypes.bool, hidePasswordLabel: PropTypes.string, id: PropTypes.string.isRequired, inline: PropTypes.bool, invalid: PropTypes.bool, readOnly: PropTypes.bool, invalidText: PropTypes.node, labelText: PropTypes.node.isRequired, light: deprecate(PropTypes.bool, "The `light` prop for `PasswordInput` has been deprecated in favor of the new `Layer` component. It will be removed in the next major release."), onChange: PropTypes.func, onClick: PropTypes.func, onTogglePasswordVisibility: PropTypes.func, placeholder: PropTypes.string, showPasswordLabel: PropTypes.string, size: PropTypes.oneOf([ "sm", "md", "lg" ]), tooltipAlignment: PropTypes.oneOf([ "start", "center", "end" ]), tooltipPosition: PropTypes.oneOf([ "top", "right", "bottom", "left" ]), type: PropTypes.oneOf(["password", "text"]), value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), warn: PropTypes.bool, warnText: PropTypes.node }; //#endregion export { PasswordInput as default };