Diese Seite wurde maschinell übersetzt und kann Fehler enthalten. Das englische Original lesen

Elemente

Input

Input ist ein natives Textfeld: ein UITextField (einzeilig) oder UITextView (mehrzeilig) auf iOS und Mac Catalyst, ein EditText auf Android. Eingabe, Caret, Auswahl, Autokorrektur und Autofill stammen von der Plattform selbst. In Rust erzeugst du eines mit nsh_base::input(), in TSX mit <Input> aus nsh/base.

el-input.tsx
el-input.tsxLive
import { type InputRef, type JSX, mount, signal, Text, View } from "nsh";
import { Input } from "nsh/base";
import { button, buttonText, field, Inspector, Panel, Stage, ui, wide } from "./palette";

function App() {
  const name = signal("");
  const password = signal("");
  const notes = signal("");
  const submitted = signal("");
  let passwordInput!: InputRef;
  const signIn = () => submitted.set(`Signed in as ${name() || "nobody"}`);
  const nameField = () => (
    <Input
      {...field()}
      value={name()}
      onChange={({ value }) => name.set(value)}
      placeholder="Ada Lovelace"
      autoCapitalize="words"
      maxLength={20}
      returnKeyType="next"
      onSubmit={() => passwordInput.focus()}
    />
  );
  const count = () => <Text style={{ fontSize: 12, color: ui().muted }}>{name().length} / 20</Text>;
  const passwordField = () => (
    <Input
      ref={passwordInput}
      {...field()}
      value={password()}
      onChange={({ value }) => password.set(value)}
      masked
      autoComplete="current-password"
      returnKeyType="done"
      onSubmit={signIn}
    />
  );
  const notesField = () => (
    <Input
      {...field({ style: { height: wide() ? 80 : 100 } })}
      multiline
      value={notes()}
      onChange={({ value }) => notes.set(value)}
      placeholder="Anything else?"
    />
  );
  const signInButton = () => (
    <View onPress={signIn} disabled={password().length === 0} {...button("primary", { style: { marginTop: 4 } })}>
      <Text style={buttonText("primary")}>Sign in</Text>
    </View>
  );
  /** A label above its field, and what describes it below. */
  const group = (label: string, input: JSX.Element, after?: JSX.Element) => (
    <View style={{ gap: 6 }}>
      <Text style={{ fontSize: 13, color: ui().muted }}>{label}</Text>
      {input}
      {after}
    </View>
  );

  return (
    <View style={{ flex: 1, backgroundColor: ui().page }}>
      {() =>
        wide() ? (
          <View style={{ flex: 1, padding: 20, paddingTop: 24, gap: 14 }}>
            <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>input</Text>
            <Text style={{ color: ui().muted }}>Tab moves between the fields; Return in the name goes to the password</Text>
            <View style={{ flex: 1, flexDirection: "row", gap: 16 }}>
              <Stage width={340} gap={16}>
                {group("Name (controlled, maxLength 20)", nameField(), count())}
                {group("Password", passwordField())}
                {group("Notes (multiline)", notesField())}
                {signInButton()}
              </Stage>
              <Inspector>
                <Panel title="value">
                  <Text style={{ fontSize: 13, color: ui().text }}>name {name() ? `“${name()}”` : "—"}</Text>
                  <Text style={{ fontSize: 13, color: ui().text, fontVariant: ["tabular-nums"] }}>password {password().length} characters</Text>
                  <Text style={{ fontSize: 13, color: ui().text, fontVariant: ["tabular-nums"] }}>notes {notes().length} characters</Text>
                </Panel>
                <Panel title="onSubmit" grow>
                  <Text style={{ fontSize: 13, color: ui().up }}>{submitted()}</Text>
                </Panel>
              </Inspector>
            </View>
          </View>
        ) : (
          <View style={{ flex: 1, width: "100%", maxWidth: 480, alignSelf: "center", padding: 20, paddingTop: 24, gap: 10 }}>
            <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>input</Text>

            <Text style={{ color: ui().muted }}>Name (controlled, maxLength 20)</Text>
            {nameField()}
            {count()}

            <Text style={{ color: ui().muted }}>Password</Text>
            {passwordField()}

            <Text style={{ color: ui().muted }}>Notes (multiline)</Text>
            {notesField()}

            {signInButton()}
            <Text style={{ color: ui().up }}>{submitted()}</Text>
          </View>
        )
      }
    </View>
  );
}

mount(App);
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))
}

#Kontrolliert und unkontrolliert

  • Kontrolliert: Übergib value und aktualisiere es in onChange. Die App kann umschreiben, was der Benutzer getippt hat (kürzen, formatieren): Der Text zeigt die Änderung sofort an und wird korrigiert, sobald das neue value eintrifft. Jede native Änderung trägt einen Zähler, und ein value der App wird angewendet, wenn es auf der letzten Änderung basiert, sodass neuere Eingaben immer Vorrang vor einer langsamen Aktualisierung haben.
  • Unkontrolliert: Übergib defaultValue (.default_value(..) oder nichts) und lies den Text aus onChange oder onSubmit.

Regeln, die während der Eingabe des Benutzers gelten müssen, sind Props und werden nativ umgesetzt: maxLength begrenzt die Länge und submitBehavior legt fest, was die Eingabetaste bewirkt. Siehe Formulare und Tastatur.

#Props

Zusätzlich zu den gemeinsamen Props (mit style als TextStyle typisiert: Schriftart, Farbe, Ausrichtung und die View-Styles werden angewendet):

#Text

Prop Rust Typ Standard Beschreibung
value .value(..) string Der Text, von der App kontrolliert.
defaultValue .default_value(..) string Der Anfangstext.
placeholder .placeholder(..) string Wird angezeigt, solange das Eingabefeld leer ist.
multiline .multiline(..) boolean false Mehrere Zeilen; die Eingabetaste fügt einen Zeilenumbruch ein, sofern submitBehavior nichts anderes festlegt.
scrollbar .scrollbar(..) boolean | ScrollbarTheme true Die Bildlaufleiste eines mehrzeiligen Eingabefelds: der Indikator der Plattform oder die integrierte Leiste der App, sobald diese eine festgelegt hat (Scrollbar); ein Objekt ist ein eigenes Theme, false bedeutet keine.
maxLength .max_length(..) number Die maximale Anzahl an Zeichen, die der Benutzer eingeben kann.
readonly .readonly(..) boolean false Der Benutzer kann den Text auswählen und kopieren, aber nicht ändern.
masked .masked(..) boolean false Der Text wird verborgen (Passwörter).
allowFontScaling boolean true Folgt der System-Textgröße.
maxFontSizeMultiplier number 0 Die Obergrenze der Systemskalierung (0 bedeutet keine).

#Tastatur

Prop Rust Typ Standard Beschreibung
keyboardType .keyboard_type(KeyboardType::Email) "default" | "number-pad" | "decimal-pad" | "numeric" | "numbers-and-punctuation" | "email-address" | "phone-pad" | "url" "default" Das Tastaturlayout (die Werte; numbers-and-punctuation hat auf iOS ein Minuszeichen).
returnKeyType .return_key_type(ReturnKeyType::Next) "default" | "done" | "go" | "next" | "search" | "send" "default" Die Beschriftung der Eingabetaste.
autoCapitalize .auto_capitalize(AutoCapitalize::None) "none" | "sentences" | "words" | "characters" "sentences" Automatische Großschreibung.
autoCorrect .auto_correct(..) boolean true Die Korrekturen des Systems.
autoComplete .auto_fill(AutoFill::OneTimeCode) AutoComplete | TextContentType Was das Eingabefeld enthält, für Autofill auf iOS, Android und im Web: "username", "current-password", "new-password", "one-time-code", "email", "tel", "postal-code", "cc-number", "off", …
pasteBehavior .paste_behavior(PasteBehavior::Report) "insert" | "report" "insert" Was beim Einfügen geschieht: "insert" fügt den eingefügten Text ein, "report" fügt nichts ein, damit die App das behandeln kann, was onPaste meldet. Bilder und Dateien werden nie eingefügt.
submitBehavior .submit_behavior(SubmitBehavior::Submit) "submit" | "blurAndSubmit" | "newline" "blurAndSubmit" (einzeilig), "newline" (mehrzeilig) Was die Eingabetaste bewirkt: "submit" meldet onSubmit und behält den Fokus, "blurAndSubmit" entzieht zusätzlich den Fokus, "newline" fügt einen Zeilenumbruch ein (mehrzeilig; ein einzeiliges Feld sendet ab). Ein mehrzeiliges Eingabefeld, das absendet, nimmt auf Hardware-Tastaturen weiterhin Zeilenumbrüche über Shift+Return oder Option / Alt+Return entgegen. "submit" lässt die Tastatur eingeblendet (z. B. um mehrere Nachrichten zu senden).
autoFocus .auto_focus(..) boolean false Erhält den Fokus, sobald es erscheint.

#Caret und Auswahl

Prop Rust Typ Beschreibung
caret .caret(..) boolean Das Caret wird gezeichnet (Standard true; false blendet es aus).
selectedRange .selected_range((start, end)) { start: number; end?: number } Die kontrollierte Auswahl, in UTF-16-Einheiten; end ist standardmäßig start (ein Caret).

Das Caret blinkt, solange sein Fenster angezeigt und aktiv ist. In GPUI-Fenstern unter Linux bleibt es nach zehn Sekunden ohne Eingabe oder Bewegung sichtbar, wie in GTK-Apps, sodass ein fokussiertes Feld, das sich selbst überlassen bleibt, keine Frames kostet.

#Farben

Die Farben von Platzhalter, Caret und Auswahlhervorhebung sind Style-Keys mit ihren Tailwind-Klassen und Methoden in Rust (UIKit färbt Caret und Auswahl mit einer Farbe ein, und zwar mit der des Carets, wenn sie gesetzt ist):

Style-Key Rust Klasse Beschreibung
placeholderColor .placeholder_color(..) placeholder:text-* Die Farbe des Platzhalters.
caretColor .caret_color(..) caret-* Die Farbe des Carets.
selectionColor .selection_color(..) selection:text-* Die Farbe der Auswahlhervorhebung.
input()
    .placeholder("Search")
    .text_color(rgb(0x18181b))
    .placeholder_color(rgb(0x9ca3af))
    .caret_color(rgb(0x2563eb))
    .selection_color(rgb(0x93c5fd))

#iOS

ios-Key Rust Typ Beschreibung
textContentType .auto_fill(..) AutoComplete | TextContentType Die Namen von iOS für autoComplete ("oneTimeCode", "newPassword", …).
inputAccessoryViewID .input_accessory(id) string Das nativeID einer InputAccessoryView (InputAccessoryView), die über der Tastatur angezeigt wird, solange das Eingabefeld den Fokus hat.
keyboardAppearance .keyboard_appearance(KeyboardAppearance::Dark) "default" | "light" | "dark" Das Erscheinungsbild der Tastatur; "default" folgt dem Erscheinungsbild des Systems. Die Tastaturen von Android folgen dem Theme des Systems.
use nsh_core::element::AutoFill;

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

#Events

Event Rust Payload Wann
onChange .on_change(|text: &str| ..) { value } Der Benutzer hat den Text geändert: value ist der aktuelle Text.
onSubmit .on_submit(|text: &str| ..) { text } Die Eingabetaste wurde in einem Eingabefeld gedrückt, das absendet (einzeilig oder submitBehavior "submit" / "blurAndSubmit").
onFocus / onBlur .on_focus / .on_blur keiner Das Eingabefeld hat den Fokus erhalten / verloren.
onSelectionChange .on_selection_change(|s: &Selection| ..) { start, end } Das Caret oder die Auswahl hat sich bewegt (UTF-16-Einheiten).
onPaste .on_paste(|e: &PasteEvent| ..) { text, html, files, uris } Der Benutzer hat etwas eingefügt: den Text, seine Formatierung als HTML (Linux, Web), Bilder (ein Screenshot) als Files, file:-URLs lokaler Dateien. Vor dem onChange des Textes, den es einfügt (Formulare und Tastatur).
onCaretMove .on_caret_move(|e: &CaretEvent| ..) { bounds, pageBounds } Das Caret hat sich bewegt, mit der Auswahl oder weil der Text gescrollt oder umgebrochen wurde: bounds ist seine Position im Eingabefeld, pageBounds im Fenster, das anchor eines Overlay (Formulare und Tastatur).
onKeyPress .on_key_press(|key: &str| ..) { key } Bevor sich der Text ändert: key ist der getippte Text, "Backspace" oder "Enter".
onContentSizeChange .on_content_size_change(|size: &Size<Points>| ..) { size } Die Größe des Textes hat sich geändert (bei mehrzeiligen Feldern in der Breite des Eingabefelds), während er getippt wird; ein leeres Eingabefeld meldet eine leere Zeile, unabhängig von seinem Platzhalter.
onCompositionChange .on_composition_change(|e: &Composition| ..) { composing, text } Die Komposition einer Eingabemethode hat begonnen, sich geändert oder geendet: text ist das, was gerade komponiert wird (Pinyin, bevor ein Zeichen gewählt ist, Kana vor der Konvertierung), "", sobald sie endet. Der übernommene Text erreicht onChange vor dem Ende.

Ein mehrzeiliges Eingabefeld, das mit seinem Text wächst:

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()

#Ref-Methoden

Methode Rust Beschreibung
focus() focus() Setzt den Fokus auf das Eingabefeld und zeigt die Tastatur an.
blur() blur() Entzieht ihm den Fokus.
setSelection(start, end?) set_selection(start, end) Wählt start..end aus (UTF-16-Einheiten); ein Caret, wenn beide gleich sind.

Den Fokus bei Return auf das nächste Feld verschieben:

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))

Keyboard.dismiss() aus nsh (dismiss_keyboard() in Rust) blendet die Tastatur aus, unabhängig davon, welches Eingabefeld sie hat.

#Hinweise zu Plattformen

  • Auf macOS teilen sich einzeiliger Text, Platzhalter und Carets die Schriftmetriken des nativen Felds und werden gemeinsam innerhalb der Content-Box zentriert. lineHeight fließt in das Layout ein; mehrzeilige Editoren verwenden es zusätzlich für den Absatzabstand.
  • Eingabefelder in einer Scroll View werden nativ von der Tastatur freigehalten. Verwende andernorts ein unteres Padding oder Margin von env(keyboard-inset-height).
  • disabled deaktiviert das Eingabefeld (kein Fokus) und wendet zusätzlich die Disabled-Styles an; ein readonly-Eingabefeld behält seinen Fokus und seine Auswahl.
  • inputAccessoryViewID gilt auf iOS, wo die Tastatur eine Zubehörleiste hat.