# PasswordField: Руководства

## Паттерны

### Строка пароля при входе

Когда использовать:

- пользователи вводят пароль от существующего аккаунта

Заметки о композиции:

- задайте `autoComplete='current-password'`
- держите переключатель видимым и рядом с полем ввода

Компромиссы:

- привычный паттерн
- возможность показать пароль добавляет в строку ещё одно действие

```tsx
import {
  Label,
  PasswordField,
  PasswordFieldHiddenIcon,
  PasswordFieldInput,
  PasswordFieldShownIcon,
  PasswordFieldToggleIconButton,
} from '@vega-ui/react';
import { useId } from 'react';

export default function SignInPassword() {
  const id = useId();

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-3)', width: '100%' }}>
      <Label htmlFor={id}>Password</Label>
      <PasswordField>
        <PasswordFieldInput id={id} name='password' autoComplete='current-password' />
        <PasswordFieldToggleIconButton aria-label='Show password'>
          <PasswordFieldShownIcon />
          <PasswordFieldHiddenIcon />
        </PasswordFieldToggleIconButton>
      </PasswordField>
    </div>
  );
}
```

### Создание нового пароля

Когда использовать:

- пользователи создают или сбрасывают пароль

Заметки о композиции:

- задайте `autoComplete='new-password'`
- сопроводите поле текстом подсказки или рекомендациями по надёжности

Компромиссы:

- делает онбординг и сброс пароля понятнее
- может приучить слишком полагаться на визуальные подсказки о надёжности

```tsx
import {
  Icon,
  Label,
  PasswordField,
  PasswordFieldHiddenIcon,
  PasswordFieldInput,
  PasswordFieldShownIcon,
  PasswordFieldToggleIconButton,
  Text,
} from '@vega-ui/react';
import { CheckIcon, CircleIcon } from '@vega-ui/icons';
import { useId, useState } from 'react';

const RULES = [
  { label: 'At least 12 characters', test: (value: string) => value.length >= 12 },
  { label: 'Upper and lower case letters', test: (value: string) => /[a-z]/.test(value) && /[A-Z]/.test(value) },
  { label: 'At least one number', test: (value: string) => /\d/.test(value) },
  { label: 'At least one symbol', test: (value: string) => /[^A-Za-z0-9]/.test(value) },
];

export default function NewPasswordChecklist() {
  const id = useId();
  const rulesId = `${id}-rules`;
  const [password, setPassword] = useState('');
  const passed = RULES.filter((rule) => rule.test(password)).length;

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-6)', width: '100%' }}>
      <div style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
        <Label htmlFor={id}>New password</Label>
        <PasswordField>
          <PasswordFieldInput
            id={id}
            name='newPassword'
            autoComplete='new-password'
            value={password}
            aria-describedby={rulesId}
            onChange={(event) => setPassword(event.currentTarget.value)}
          />
          <PasswordFieldToggleIconButton aria-label='Show password'>
            <PasswordFieldShownIcon />
            <PasswordFieldHiddenIcon />
          </PasswordFieldToggleIconButton>
        </PasswordField>
      </div>
      <ul id={rulesId} style={{ display: 'grid', gap: 'var(--spacing-3)', margin: 0, padding: 0, listStyle: 'none' }}>
        {RULES.map((rule) => {
          const ok = rule.test(password);
          return (
            <li
              key={rule.label}
              style={{
                display: 'flex',
                alignItems: 'center',
                gap: 'var(--spacing-4)',
                color: ok ? 'var(--label-primary)' : 'var(--label-tertiary)',
              }}
            >
              <Icon size='xs' aria-hidden style={{ flexShrink: 0 }}>
                {ok ? <CheckIcon strokeWidth={2.5} /> : <CircleIcon />}
              </Icon>
              <Text size={2} style={{ color: 'inherit' }}>{rule.label}</Text>
            </li>
          );
        })}
      </ul>
      <Text size={2} fontWeight={500} role='status' style={{ fontVariantNumeric: 'tabular-nums' }}>
        {passed} of {RULES.length} requirements met
      </Text>
    </div>
  );
}
```

### Собственные иконки видимости

Когда использовать:

- язык продукта требует собственных иконок безопасности

Заметки о композиции:

- оставляйте доступное имя кнопки общим и понятным
- меняйте иконки внутри готовой части-переключателя

Компромиссы:

- соответствует языку продукта
- легко переборщить со стилями, если иконки доминируют над полем

```tsx
import {
  Label,
  PasswordField,
  PasswordFieldHiddenIcon,
  PasswordFieldInput,
  PasswordFieldShownIcon,
  PasswordFieldToggleIconButton,
} from '@vega-ui/react';
import { LockIcon, LockOpenIcon } from '@vega-ui/icons';
import { useId } from 'react';

export default function CustomVisibilityIcons() {
  const id = useId();

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-3)', width: '100%' }}>
      <Label htmlFor={id}>Vault passphrase</Label>
      <PasswordField>
        <PasswordFieldInput id={id} autoComplete='current-password' />
        <PasswordFieldToggleIconButton aria-label='Show passphrase'>
          <PasswordFieldShownIcon><LockIcon /></PasswordFieldShownIcon>
          <PasswordFieldHiddenIcon><LockOpenIcon /></PasswordFieldHiddenIcon>
        </PasswordFieldToggleIconButton>
      </PasswordField>
    </div>
  );
}
```

## Сравнение

### Быстрое правило выбора

Используйте `PasswordField`, когда пользователь вводит секрет и ему может понадобиться ненадолго его показать. Для обычного текстового ввода используйте `TextField`.

### `PasswordField` или `TextField`

Используйте `PasswordField`, когда:

- значение — скрытый секрет с возможностью показать его

Используйте `TextField`, когда:

- поле предназначено для обычного текста

### `PasswordField` или `PinField`

Используйте `PasswordField`, когда:

- секрет произвольной формы

Используйте `PinField`, когда:

- секрет — короткий код фиксированной длины

### Выбирайте этот компонент, когда

- значение должно быть скрыто по умолчанию
- возможность показать и скрыть помогает проверить ввод
- важна работа менеджеров паролей и браузерного ввода секретов

### Не выбирайте этот компонент, когда

- поле не содержит секрета
- значение — одноразовый код (OTP) или PIN фиксированной длины

## Решение проблем

### Браузер не предлагает сохранить пароль

#### Симптом

Браузер не предлагает сохранить пароль, или показанный пароль появляется в подсказках автозаполнения других полей.

#### Вероятная причина

Пароль был показан в момент отправки формы, поэтому у поля был `type='text'`, и браузер воспринял его как обычное текстовое поле.

#### Как проверить

- отправьте форму с показанным и со скрытым паролем и сравните результат

#### Исправление

Контролируйте `shown` и устанавливайте его в `false` в обработчике `onSubmit` формы, до того как браузер соберёт значения.

### Автозаполнение работает по-разному в разных браузерах

#### Симптом

Автозаполнение работает в одном браузере, но не в другом.

#### Вероятная причина

Менеджеры паролей и нативное автозаполнение по-разному интерпретируют поля паролей, особенно в сценариях нового и текущего пароля.

#### Как проверить

- проверьте реальный сценарий в браузере с нужным значением `autoComplete`

#### Исправление

Используйте правильный токен автозаполнения и проверяйте в реальных целевых браузерах.

### Подсказки о надёжности не совпадают с валидацией

#### Симптом

Интерфейс сообщает, что пароль надёжный, но бэкенд всё равно его отклоняет.

#### Вероятная причина

Визуальная оценка надёжности и настоящая политика паролей — разные системы.

#### Как проверить

- сравните подсказки о надёжности на фронтенде с правилами политики на бэкенде

#### Исправление

Документируйте политику отдельно и считайте подсказки о надёжности рекомендательными, а не определяющими.
