---
url: https://nsh.dev/zh-HK/elements/input
description: "原生文字欄位:受控與非受控文字、鍵盤、自動填入、選取、多行輸入及焦點。"
demo: https://nsh.dev/play/index.html#el-input
---

# Input

`Input` 是原生文字輸入欄：在 iOS 和 Mac Catalyst 上是 UITextField（單行）或 UITextView（多行），在 Android 上是 EditText。輸入、游標、選取、自動修正和自動填寫均由平台自行處理。Rust 用 `nsh_base::input()` 建立，TSX 則使用 `nsh/base` 中的 `<Input>`。

```rust
use nsh_base::input;
use nsh_core::prelude::*;

fn name_field() -> impl IntoElement {
    let name = signal(String::new());
    input()
        .value(name)
        .on_change(move |text: &str| name.set(text.to_string()))
        .placeholder("Your name")
        .auto_capitalize(AutoCapitalize::Words)
        .return_key_type(ReturnKeyType::Done)
        .text_base()
        .p_3()
        .border_1()
        .border_color(rgb(0xd0d7de))
        .rounded(px(8.0))
}
```

```tsx
import { signal } from "nsh";
import { Input } from "nsh/base";

function NameField() {
  const name = signal("");
  return (
    <Input
      value={name()}
      onChange={({ value }) => name.set(value)}
      placeholder="Your name"
      autoCapitalize="words"
      returnKeyType="done"
      style={{ fontSize: 16, padding: 12, borderWidth: 1, borderColor: "#d0d7de", borderRadius: 8 }}
    />
  );
}
```

<a id="controlled-and-uncontrolled"></a>

## 受控與非受控

- **受控**：傳入 `value`，並在 `onChange` 中更新。應用程式可以改寫用戶輸入的內容（例如去除空白、格式化）：文字會即時顯示該次編輯，並在新的 `value` 送達時作出修正。每次原生編輯都附帶一個計數，若應用程式傳來的 `value` 是基於最新一次編輯，便會被套用，因此較新的輸入永遠優先於較慢的更新。
- **非受控**：傳入 `defaultValue`（`.default_value(..)`，或不傳），並從 `onChange` 或 `onSubmit` 讀取文字。

用戶輸入期間必須成立的規則屬於 props，由原生層直接處理：`maxLength` 限制長度，`submitBehavior` 決定 return 鍵的行為。請參閱[表單與鍵盤](https://nsh.dev/translations/zh-HK/docs/forms-and-keyboard.md)。

## Props

除了[通用 props](https://nsh.dev/translations/zh-HK/elements.md#common-props)（其中 `style` 的類型為 `TextStyle`：字體、顏色、對齊方式和檢視樣式均適用）之外：

<a id="text"></a>

### 文字

| Prop | Rust | 類型 | 預設值 | 說明 |
|---|---|---|---|---|
| `value` | `.value(..)` | `string` | | 文字，由應用程式控制。 |
| `defaultValue` | `.default_value(..)` | `string` | | 初始文字。 |
| `placeholder` | `.placeholder(..)` | `string` | | 輸入欄為空時顯示。 |
| `multiline` | `.multiline(..)` | `boolean` | `false` | 多行；除非 `submitBehavior` 另有指定，否則按 return 會插入換行。 |
| `scrollbar` | `.scrollbar(..)` | `boolean \| ScrollbarTheme` | `true` | 多行輸入欄的捲軸：使用平台的指示器，或在應用程式設定後使用其內建捲軸（[Scrollbar](https://nsh.dev/translations/zh-HK/elements/scrollbar.md#built-in-bars)）；物件則是它自己的主題，`false` 表示不顯示。 |
| `maxLength` | `.max_length(..)` | `number` | | 用戶最多可輸入的字元數。 |
| `readonly` | `.readonly(..)` | `boolean` | `false` | 用戶可以選取和複製文字，但不能更改。 |
| `masked` | `.masked(..)` | `boolean` | `false` | 隱藏文字（用於密碼）。 |
| `allowFontScaling` | | `boolean` | `true` | 跟隨系統文字大小。 |
| `maxFontSizeMultiplier` | | `number` | `0` | 系統縮放比例的上限（0 表示無上限）。 |

<a id="keyboard"></a>

### 鍵盤

| Prop | Rust | 類型 | 預設值 | 說明 |
|---|---|---|---|---|
| `keyboardType` | `.keyboard_type(KeyboardType::Email)` | `"default" \| "number-pad" \| "decimal-pad" \| "numeric" \| "numbers-and-punctuation" \| "email-address" \| "phone-pad" \| "url"` | `"default"` | 鍵盤佈局（[可用值](https://nsh.dev/translations/zh-HK/docs/forms-and-keyboard.md#keyboards-and-autofill)；`numbers-and-punctuation` 在 iOS 上設有負號）。 |
| `returnKeyType` | `.return_key_type(ReturnKeyType::Next)` | `"default" \| "done" \| "go" \| "next" \| "search" \| "send"` | `"default"` | return 鍵的標籤。 |
| `autoCapitalize` | `.auto_capitalize(AutoCapitalize::None)` | `"none" \| "sentences" \| "words" \| "characters"` | `"sentences"` | 自動大寫。 |
| `autoCorrect` | `.auto_correct(..)` | `boolean` | `true` | 系統的修正功能。 |
| `autoComplete` | `.auto_fill(AutoFill::OneTimeCode)` | `AutoComplete \| TextContentType` | | 輸入欄的內容類型，供 iOS、Android 和網頁自動填寫使用：`"username"`、`"current-password"`、`"new-password"`、`"one-time-code"`、`"email"`、`"tel"`、`"postal-code"`、`"cc-number"`、`"off"` 等 |
| `pasteBehavior` | `.paste_behavior(PasteBehavior::Report)` | `"insert" \| "report"` | `"insert"` | 貼上的行為：`"insert"` 會插入貼上的文字，`"report"` 則不插入任何內容，由應用程式處理 `onPaste` 所回報的內容。圖片和檔案永遠不會被插入。 |
| `submitBehavior` | `.submit_behavior(SubmitBehavior::Submit)` | `"submit" \| "blurAndSubmit" \| "newline"` | `"blurAndSubmit"`（單行）、`"newline"`（多行） | return 鍵的行為：`"submit"` 會回報 `onSubmit` 並保持焦點，`"blurAndSubmit"` 則同時取消焦點，`"newline"` 會插入換行（多行；單行則為提交）。設為提交的多行輸入欄，在實體鍵盤上仍可用 Shift+Return 或 Option / Alt+Return 輸入換行。`"submit"` 會保持鍵盤顯示（例如連續傳送多則訊息）。 |
| `autoFocus` | `.auto_focus(..)` | `boolean` | `false` | 出現時取得焦點。 |

<a id="caret-and-selection"></a>

### 游標與選取

| Prop | Rust | 類型 | 說明 |
|---|---|---|---|
| `caret` | `.caret(..)` | `boolean` | 是否繪製游標（預設為 `true`；`false` 會隱藏游標）。 |
| `selectedRange` | `.selected_range((start, end))` | `{ start: number; end?: number }` | 受控的選取範圍，以 UTF-16 單位計算；`end` 預設為 `start`（即游標）。 |

游標會在視窗顯示且處於作用中時閃爍。在 Linux 的 GPUI 視窗中，游標在十秒內沒有輸入或移動後會保持顯示，與 GTK 應用程式一樣，因此一個閒置的焦點輸入欄不會耗用任何影格。

<a id="colors"></a>

### 顏色

佔位文字、游標和選取高亮的顏色都是樣式鍵，並有對應的 Tailwind class，在 Rust 中則以方法設定（UIKit 以同一種顏色為游標和選取範圍着色，有設定時以游標的顏色為準）：

| 樣式鍵 | Rust | Class | 說明 |
|---|---|---|---|
| `placeholderColor` | `.placeholder_color(..)` | `placeholder:text-*` | 佔位文字的顏色。 |
| `caretColor` | `.caret_color(..)` | `caret-*` | 游標的顏色。 |
| `selectionColor` | `.selection_color(..)` | `selection:text-*` | 選取高亮的顏色。 |

```rust
input()
    .placeholder("Search")
    .text_color(rgb(0x18181b))
    .placeholder_color(rgb(0x9ca3af))
    .caret_color(rgb(0x2563eb))
    .selection_color(rgb(0x93c5fd))
```

```tsx
<Input placeholder="Search" class="text-zinc-900 placeholder:text-zinc-400 selection:text-blue-600" />
<Input placeholder="Search" style={{ placeholderColor: "#9ca3af", caretColor: "#2563eb", selectionColor: "#93c5fd" }} />
```

### iOS

| `ios` 鍵 | Rust | 類型 | 說明 |
|---|---|---|---|
| `textContentType` | `.auto_fill(..)` | `AutoComplete \| TextContentType` | iOS 對 `autoComplete` 的命名（`"oneTimeCode"`、`"newPassword"` 等）。 |
| `inputAccessoryViewID` | `.input_accessory(id)` | `string` | 輸入欄取得焦點時，顯示於鍵盤上方的 `InputAccessoryView` 的 `nativeID`（[`InputAccessoryView`](https://nsh.dev/translations/zh-HK/modules/nsh-mobile.md#inputaccessoryview)）。 |
| `keyboardAppearance` | `.keyboard_appearance(KeyboardAppearance::Dark)` | `"default" \| "light" \| "dark"` | 鍵盤的外觀；`"default"` 跟隨系統外觀。Android 的鍵盤跟隨系統主題。 |

```rust
use nsh_core::element::AutoFill;

input().auto_fill(AutoFill::OneTimeCode).keyboard_type(KeyboardType::Number)
```

```tsx
<Input autoComplete="one-time-code" keyboardType="number-pad" ios={{ textContentType: "oneTimeCode" }} />
```

<a id="events"></a>

## 事件

| 事件 | Rust | Payload | 觸發時機 |
|---|---|---|---|
| `onChange` | `.on_change(\|text: &str\| ..)` | `{ value }` | 用戶更改了文字：`value` 是目前的文字。 |
| `onSubmit` | `.on_submit(\|text: &str\| ..)` | `{ text }` | 在會提交的輸入欄中按下 return 鍵（單行，或 `submitBehavior` `"submit"` / `"blurAndSubmit"`）。 |
| `onFocus` / `onBlur` | `.on_focus` / `.on_blur` | 無 | 輸入欄取得 / 失去焦點。 |
| `onSelectionChange` | `.on_selection_change(\|s: &Selection\| ..)` | `{ start, end }` | 游標或選取範圍移動（UTF-16 單位）。 |
| `onPaste` | `.on_paste(\|e: &PasteEvent\| ..)` | `{ text, html, files, uris }` | 用戶貼上內容：文字、其 HTML 格式（Linux、網頁）、圖片（例如截圖，以 `File` 表示）、本機檔案的 `file:` URL。發生於其插入文字的 `onChange` 之前（[表單與鍵盤](https://nsh.dev/translations/zh-HK/docs/forms-and-keyboard.md#pasting-images-and-files)）。 |
| `onCaretMove` | `.on_caret_move(\|e: &CaretEvent\| ..)` | `{ bounds, pageBounds }` | 游標移動，可能因選取範圍改變，或因文字捲動或換行而移動：`bounds` 是游標在輸入欄中的位置，`pageBounds` 是在視窗中的位置，`Overlay` 的 `anchor`（[表單與鍵盤](https://nsh.dev/translations/zh-HK/docs/forms-and-keyboard.md#the-caret)）。 |
| `onKeyPress` | `.on_key_press(\|key: &str\| ..)` | `{ key }` | 文字更改之前：`key` 是輸入的文字，`"Backspace"` 或 `"Enter"`。 |
| `onContentSizeChange` | `.on_content_size_change(\|size: &Size<Points>\| ..)` | `{ size }` | 文字的大小在輸入時改變（多行時以輸入欄的寬度計算）；空的輸入欄會回報一行空行，與其佔位文字無關。 |
| `onCompositionChange` | `.on_composition_change(\|e: &Composition\| ..)` | `{ composing, text }` | 輸入法的組字開始、改變或結束：`text` 是正在組字的內容（選字前的拼音、轉換前的假名），結束後則為 `""`。已確認的文字會在結束之前送達 `onChange`。 |

隨文字增長的多行輸入欄：

```rust
let height = signal(px(40.0));

input()
    .multiline(true)
    .on_content_size_change(move |size: &Size<Points>| {
        height.set(px((size.height.0 + 16.0).clamp(40.0, 120.0)));
    })
    .h(move || height.get())
    .px_3()
    .py_2()
```

```tsx
const height = signal(40);
<Input
  multiline
  onContentSizeChange={({ size }) => height.set(Math.min(120, Math.max(40, size.height + 16)))}
  style={{ height: height(), paddingHorizontal: 12, paddingVertical: 8 }}
/>;
```

<a id="ref-methods"></a>

## Ref 方法

| 方法 | Rust | 說明 |
|---|---|---|
| `focus()` | `focus()` | 讓輸入欄取得焦點並顯示鍵盤。 |
| `blur()` | `blur()` | 取消輸入欄的焦點。 |
| `setSelection(start, end?)` | `set_selection(start, end)` | 選取 `start..end`（UTF-16 單位）；兩者相等時為游標。 |

按下 return 後將焦點移到下一個欄位：

```rust
use nsh_core::element::AutoFill;

let password = element_ref();

view()
    .child(input().return_key_type(ReturnKeyType::Next).on_submit(move |_: &str| password.focus()))
    .child(input().element_ref(&password).masked(true).auto_fill(AutoFill::Password))
```

```tsx
let password!: InputRef;
<>
  <Input returnKeyType="next" onSubmit={() => password.focus()} />
  <Input ref={password} masked autoComplete="current-password" />
</>;
```

來自 `nsh` 的 `Keyboard.dismiss()`（Rust 中為 `dismiss_keyboard()`）會隱藏鍵盤，不論是哪個輸入欄正在使用它。

<a id="platform-notes"></a>

## 平台備註

- 在 macOS 上，單行文字、佔位文字和游標共用原生輸入欄的字體度量，並一起在內容框內置中。`lineHeight` 會參與佈局；多行編輯器也以它來設定段落間距。
- 位於[捲動檢視](https://nsh.dev/translations/zh-HK/elements/scroll.md#keyboard)內的輸入欄，會由原生層自動避開鍵盤。在其他情況下，請使用 [`env(keyboard-inset-height)`](https://nsh.dev/translations/zh-HK/elements/keyboard.md) 作為底部的 padding 或 margin。
- `disabled` 會停用輸入欄（無法取得焦點）並套用停用樣式；`readonly` 的輸入欄則保留其焦點和選取範圍。
- `inputAccessoryViewID` 適用於 iOS，因為 iOS 的鍵盤設有附屬工具列。
