---
title: "密码输入"
description: "PasswordInput 是带显隐按钮的原生密码框：保持光标位置、对接密码管理器、提交时自动隐藏。"
---

> Documentation Index
> Fetch the complete documentation index at: https://ui-sdk.ndjp.net/llms.txt
> Use this file to discover all available pages before exploring further.

# 密码输入

`PasswordInput`（密码输入）是一个原生密码输入加上显示、隐藏按钮。切换显隐时光标与选区留在原处，密码管理器能识别该填哪种密码，表单提交时会自动把明文重新遮罩。只需要普通文本时用[输入框](https://ui-sdk.ndjp.net/docs/components/input)；短验证码用[验证码输入](https://ui-sdk.ndjp.net/docs/components/otp-input)。

## 组合

```text
PasswordInput
├── PasswordInputControl
└── PasswordInputToggle
```

`PasswordInput` 持有值与显隐状态；`PasswordInputControl` 渲染原生输入；`PasswordInputToggle` 渲染切换按钮，它在 `password` 与 `text` 两种类型之间切换，自身是 `type="button"`，所以在输入里按 `Enter` 仍然提交表单。

表单相关属性（`name`、`form`、`required`、`disabled`、`readOnly`）放在 `PasswordInput` 上；其余原生输入属性（`placeholder`、`id` 等）放在 `PasswordInputControl` 上。

## 用法

```tsx
import { Field, FieldLabel } from "@ui-sdk/react/field";
import { PasswordInput, PasswordInputControl, PasswordInputToggle } from "@ui-sdk/react/password-input";

<Field name="password">
  <FieldLabel>密码</FieldLabel>
  <PasswordInput>
    <PasswordInputControl />
    <PasswordInputToggle aria-label="显示密码">
      {(state) => (state.visible ? "隐藏" : "显示")}
    </PasswordInputToggle>
  </PasswordInput>
</Field>;
```

部件的 children 可以是函数，收到该部件的状态；这里用 `state.visible` 切换按钮内容。放进[字段](https://ui-sdk.ndjp.net/docs/components/field)后，`FieldLabel` 为输入命名，`FieldDescription` 作为说明被一起朗读。

## 显隐状态

`defaultVisible` 让密码一开始就是明文，适合需要被读出或复制的场景，例如展示给访客的 Wi-Fi 密码。要完全受控就传 `visible` 与 `onVisibleChange`：

```tsx
const [visible, setVisible] = React.useState(false);

<PasswordInput visible={visible} onVisibleChange={setVisible}>
  <PasswordInputControl aria-label="密码" />
  <PasswordInputToggle aria-label="显示密码">
    {(state) => (state.visible ? "隐藏" : "显示")}
  </PasswordInputToggle>
</PasswordInput>;
```

`details.reason` 区分 `toggle-press`、`form-submit`、`reset`、`page-hide` 与 `imperative`。表单提交与页面被隐藏（例如跳转离开）时组件会主动隐藏明文，前者让浏览器正常提示保存密码，后者避免明文被浏览器缓存进页面快照。`details.cancel()` 可以拒绝显隐变更，但提交过程中浏览器读取表单的那一刻，输入仍会临时回到密码类型。

## 自动填充与密码管理器

`autoComplete` 默认是 `"current-password"`，用于登录；注册或修改密码时改成 `"new-password"`，浏览器与密码管理器才会提供生成、保存新密码的流程，也可以设 `"off"`。

```tsx
<PasswordInput name="new-password" autoComplete="new-password">
  <PasswordInputControl placeholder="至少 8 个字符" />
  <PasswordInputToggle aria-label="显示密码">
    {(state) => (state.visible ? "隐藏" : "显示")}
  </PasswordInputToggle>
</PasswordInput>
```

组件本身不施加任何密码强度策略。要做强度清单，用受控值 `value` + `onValueChange` 自己推导规则并渲染，自动填充与密码管理器填入的值同样以 `input-change` 报告。

## 大写锁定提示

聚焦期间按键事件会更新大写锁定状态，用 `data-capslock` 写样式，或读根的 `capsLock` 状态渲染提示：

```tsx
<PasswordInput>
  {(state) => (
    <>
      <PasswordInputControl aria-label="密码" />
      <span aria-hidden="true">{state.capsLock ? "!" : ""}</span>
      <PasswordInputToggle aria-label="显示密码">
        {(toggle) => (toggle.visible ? "隐藏" : "显示")}
      </PasswordInputToggle>
    </>
  )}
</PasswordInput>
```

给图标与提示预留固定位置，状态切换时布局才不会跳动。失焦或关闭大写锁定后提示消失。

## 控制器与表单

用 `createPasswordInputController()` 创建控制器传给根，可在字段之外读写：`getValue / setValue / clear / isVisible / setVisible / toggleVisible / focus`。写入方法接受 `{ reason, event }`，像用户输入或按下按钮一样触发变更回调，被取消时返回 `false`，默认原因是 `imperative`。参见[控制器](https://ui-sdk.ndjp.net/docs/concepts/controllers)。

```tsx
const password = createPasswordInputController();

password.setValue("plum-kettle-orbit-42");
password.setVisible(true);
```

原生输入参与 `FormData`、校验与重置：`name` 决定提交字段名（在字段内可用 `Field` 的 `name`），`form` 关联页面中其他表单的 id，`required` 阻止空值提交，重置回到 `defaultValue`。`disabled` 同时禁用输入与按钮且不提交；`readOnly` 只锁住文本，显隐按钮仍然可用。`disabled` 也会从 `Field` 或外层 `<fieldset disabled>` 继承。参见[表单](https://ui-sdk.ndjp.net/docs/concepts/forms)。

## 样式与状态

把根当作字段的盒子来画，原生输入与按钮排在里面。三个部件共享同一套状态属性：

- `data-visible`：当前是明文（输入为文本类型）。
- `data-filled`：已有文本。
- `data-capslock`：聚焦且大写锁定开启。
- `data-disabled`、`data-readonly`、`data-required`。
- 在[字段](https://ui-sdk.ndjp.net/docs/components/field)内，原生输入还会收到 `data-invalid` 等校验属性。

```css
[data-slot="password-input"] {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding-inline: 0.75rem;
}
[data-slot="password-input"][data-visible] { border-color: var(--color-warning); }
[data-slot="password-input-toggle"][aria-pressed="true"] { color: var(--color-brand); }
```

间距用逻辑属性写，字段与按钮才能跟随书写方向。详见[样式与动画](https://ui-sdk.ndjp.net/docs/concepts/styling-and-animation)。

## API

`PasswordInput` 常用属性：

| 属性 | 类型 | 说明 |
| --- | --- | --- |
| `value` / `defaultValue` | `string` | 受控值与非受控初始值，重置回到后者。 |
| `onValueChange` | `(value, details) => void` | 值变化时触发，可 `details.cancel()`。 |
| `visible` / `defaultVisible` | `boolean` | 是否显示明文，受控与非受控。 |
| `onVisibleChange` | `(visible, details) => void` | 显隐将要变化时触发，可取消。 |
| `autoComplete` | `"current-password" \| "new-password" \| "off" \| string` | 自动填充语义。 |
| `name` / `form` | `string` | 提交字段名与关联表单 id。 |
| `required` / `disabled` / `readOnly` | `boolean` | 必填、禁用、只读。 |
| `controller` | `object` | `createPasswordInputController()` 创建的控制器。 |

所有部件的状态里都有 `visible`、`capsLock`、`filled`、`disabled`、`readOnly`、`required`，可用于渲染函数与按状态取类名。

## 键盘与无障碍

密码框没有对应的 WAI-ARIA 模式：输入保持原生密码输入，由 `FieldLabel` 或 `aria-label` 命名；切换按钮遵循 WAI-ARIA 按钮模式中的切换按钮。

- `Tab` 从输入移动到按钮，按钮上 `Space` 或 `Enter` 切换显隐。
- 在输入里按 `Enter` 提交表单，绝不会误触按钮。
- 指针点击按钮后焦点留在输入，光标与选区保持原位；从别处点击按钮同样把焦点带回输入；键盘按下按钮时焦点留在按钮上。

按钮的名字按它的作用命名（例如「显示密码」）并保持不变，当前状态由 `aria-pressed` 表达，`aria-controls` 指向输入。

## 相关组件

- [输入框](https://ui-sdk.ndjp.net/docs/components/input)：普通单行文本输入。
- [验证码输入](https://ui-sdk.ndjp.net/docs/components/otp-input)：分格的一次性验证码。
- [字段](https://ui-sdk.ndjp.net/docs/components/field)：标签、说明与校验信息。

Source: https://ui-sdk.ndjp.net/docs/components/password-input/index.mdx
