import React, { useCallback, useEffect, useState } from 'react'; import { useTranslations } from '@shoptet/ui-core-web'; import type { SafeOmit, SafePick } from '@shoptet/utils'; import { Field } from '../../Field/Field'; import type { FieldProps } from '../../Field/Field'; import type { FieldHelperTextProps } from '../../Field/FieldHelperText'; import type { FieldLabelProps } from '../../Field/FieldLabel'; import { PasswordInput } from '../../Inputs/PasswordInput/PasswordInput'; import type { PasswordInputProps } from '../../Inputs/PasswordInput/PasswordInput'; import { Tag } from '../../Tag/Tag'; import type { TagProps } from '../../Tag/Tag'; import { dictionary } from './dictionary'; import type { PasswordFieldTranslations } from './dictionary'; import type { PasswordStrength } from './password-strength'; import { getPasswordStrength, PASSWORD_STRENGTH } from './password-strength'; /** @inheritdoc */ export interface PasswordFieldProps extends SafePick, SafePick, SafeOmit, SafePick { /** Label for the input element. */ label: string; /** Visually hide the label. ß*/ labelHidden?: FieldLabelProps['visuallyHidden']; strengthIndicator?: boolean; filled?: boolean; } export const PasswordField = ({ label, tooltip, required, disabled, readOnly, error, warning, hint, filled, justify, labelHidden, strengthIndicator, onChange, onFocus, onBlur, value: externalValue, ref, ...inputProps }: PasswordFieldProps) => { const [passwordStrength, setPasswordStrength] = useState(null); const [focused, setFocused] = useState(false); const [internalValue, setInternalValue] = useState(externalValue || undefined); const translations = useTranslations(dictionary); const value = typeof externalValue === 'string' ? externalValue : internalValue; useEffect(() => { if (typeof internalValue === 'string') { setPasswordStrength(getPasswordStrength(internalValue)); } }, [internalValue]); const onChangeHandler = useCallback( (value: string, event?: React.ChangeEvent) => { onChange?.(value, event); setInternalValue(value); }, [onChange] ); const onFocusHandler = useCallback( (value: string, event?: React.FocusEvent) => { onFocus?.(value, event); setFocused(true); }, [onFocus] ); const onBlurHandler = useCallback( (value: string, event?: React.FocusEvent) => { onBlur?.(value, event); setFocused(false); }, [onBlur] ); const showOverlay = filled && !value && !focused; return ( {label}
{showOverlay && ••••••••••••}
{strengthIndicator && passwordStrength && ( {getStrengthLabel(passwordStrength, translations)} )}
); }; function getStrengthLabel(strength: PasswordStrength, translations: PasswordFieldTranslations) { switch (strength) { case PASSWORD_STRENGTH.short: { return translations.tooShort; } case PASSWORD_STRENGTH.bad: { return translations.weak; } case PASSWORD_STRENGTH.good: { return translations.good; } case PASSWORD_STRENGTH.strong: { return translations.strong; } } } function getStrengthLevel(strength: PasswordStrength): TagProps['level'] { switch (strength) { case PASSWORD_STRENGTH.short: { return 'error'; } case PASSWORD_STRENGTH.bad: { return 'alert'; } case PASSWORD_STRENGTH.good: { return 'success'; } case PASSWORD_STRENGTH.strong: { return 'success'; } } }