Esta página se tradujo automáticamente y puede contener errores. Leer el original en inglés

Elementos

Text

Text dibuja un párrafo. Es el elemento que muestra cadenas de texto: una cadena hija de un View no dibuja nada. El párrafo se compone de forma nativa (CoreText en iOS y Mac Catalyst, StaticLayout en Android) y se mide en el hilo del motor, de modo que el hilo principal solo lo dibuja. En Rust se crea con text(content), en TSX con <Text> desde nsh.

el-text.tsx
el-text.tsxLive
import { mount, signal, Text, View } from "nsh";
import { button, buttonText, focusRing, ui } from "./palette";

const LONG =
  "Text is typeset natively (CoreText on iOS, StaticLayout on Android) and measured on the engine thread, so the main thread only draws it.";

function App() {
  const tapped = signal("nothing yet");
  const lines = signal(2);
  const link = () => ({ color: ui().accent, textDecorationLine: "underline" }) as const;
  return (
    <View style={{ flex: 1, backgroundColor: ui().page }}>
      <View style={{ flex: 1, width: "100%", maxWidth: 560, alignSelf: "center", padding: 20, paddingTop: 24, gap: 14 }}>
        <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>text</Text>

        <Text style={{ fontSize: 16, lineHeight: 24, color: ui().text }}>
          {"Nested text elements are spans: "}
          <Text style={{ fontWeight: "700" }}>bold</Text>
          {", "}
          <Text style={{ fontStyle: "italic", color: ui().violet }}>italic</Text>
          {", and "}
          <Text style={link()} onPress={() => tapped.set("the first link")} hoverStyle={{ opacity: 0.88 }} focusStyle={focusRing()} activeStyle={{ opacity: 0.7 }}>
            links
          </Text>
          {" that take presses of "}
          <Text style={link()} onPress={() => tapped.set("the second link")} hoverStyle={{ opacity: 0.88 }} focusStyle={focusRing()} activeStyle={{ opacity: 0.7 }}>
            their own
          </Text>
          .
        </Text>
        <Text style={{ fontSize: 13, color: ui().muted }}>Pressed: {tapped()}</Text>

        <View style={{ height: 1, backgroundColor: ui().line }} />

        <Text style={{ numberOfLines: lines(), fontSize: 15, color: ui().text }}>
          {LONG}
        </Text>
        <View style={{ flexDirection: "row", gap: 8 }}>
          <View
            onPress={() => lines.set(lines() === 2 ? 0 : 2)}
            {...button()}
          >
            <Text style={buttonText()}>{lines() === 2 ? "Show all" : "numberOfLines 2"}</Text>
          </View>
        </View>

        <Text style={{ numberOfLines: 1, textOverflow: "ellipsis-middle", fontSize: 15, color: ui().muted }}>
          {LONG}
        </Text>

        <Text style={{ fontSize: 17, color: ui().text }}>
          <Text style={{ textDecorationLine: "line-through", textDecorationColor: ui().down }}>struck</Text>{" "}
          <Text style={{ textTransform: "uppercase", letterSpacing: 2 }}>spaced</Text>{" "}
          <Text style={{ fontVariant: ["tabular-nums"] }}>0123456789</Text>
        </Text>

        <Text
          style={{
            fontSize: 28,
            fontWeight: "800",
            color: ui().onAccent,
            backgroundColor: "#c8f26d",
            paddingHorizontal: 10,
            borderRadius: 8,
            textShadow: "2px 2px 4px rgba(255,255,255,0.55)",
          }}
        >
          Text shadow
        </Text>
      </View>
    </View>
  );
}

mount(App);
use nsh_core::prelude::*;

text("Read the ")
    .text_base()
    .line_height(px(24.0))
    .text_color(rgb(0x1f2328))
    .child(text("terms").font_weight(FontWeight::BOLD))
    .child(" before you continue.")

text(..) acepta una cadena, un SharedString, una signal o una closure (text(move || format!("{} items", count.get()))): una closure o una signal actualiza el texto en su sitio cuando cambia lo que lee.

#Spans

Un texto dentro de un texto es un span del mismo párrafo, no una vista independiente. Los spans heredan los estilos de texto del párrafo (color, fontSize, fontWeight, …) y sobrescriben los que definen. Todo el bloque es una única vista nativa. En Rust, text(..) muestra su contenido y después sus hijos: .child(text(..)) añade un span, .child("…") un fragmento de texto plano.

text("")
    .text_size(px(17.0))
    .child(text("Light ").font_weight(FontWeight::LIGHT))
    .child(text("Bold ").font_weight(FontWeight::BOLD))
    .child(text("Italic").italic().text_color(rgb(0x8250df)))

#Fondos de los spans

Un span con fondo rellena la caja de sus caracteres, por detrás de ellos: tan alta como su fuente (el ascent por encima de la línea base y el descent por debajo, sea cual sea la altura de línea), y en cada línea a la que se ajusta, donde la parte de cada línea es una caja propia con el radio de esquina del span. Los spans que contiene comparten su fondo a menos que definan el suyo. Es solo dibujo: el texto se distribuye igual que sin él, y un texto seleccionable copia los caracteres del span junto con el resto. El código en línea es un span:

text("Run ")
    .text_size(px(15.0))
    .line_height(px(22.0))
    .select_text()
    .child(
        text(" cargo test ")
            .font_family("ui-monospace")
            .text_size(px(13.0))
            .bg(rgb(0xeff1f3))
            .rounded(px(4.0)),
    )
    .child(" before you commit.")

Las clases de Tailwind funcionan de la misma manera: <Text class="bg-gray-100 rounded">. El padding y los bordes son estilos de caja y no afectan a los spans; los espacios dentro del span dan holgura a la caja por sus lados.

#Enlaces

Un span con un manejador de pulsación es un enlace: una pulsación sobre sus caracteres va al span, y una pulsación en cualquier otro punto, al párrafo.

text("By continuing you agree to the ")
    .text_size(px(15.0))
    .on_press(move || log("the sentence"))
    .child(
        text("Terms of service")
            .text_color(rgb(0x0969da))
            .underline()
            .on_press(move || open("terms")),
    )
    .child(".")

#Vistas e imágenes en línea

Cualquier otro elemento dentro de un texto (una vista, una imagen, incluso dentro de spans) es un adjunto en línea: se distribuye con su propio tamaño, se sitúa sobre la línea base (o donde lo coloque su estilo verticalAlign: "text-top", "text-bottom", "middle", como en CSS; la línea crece para contenerlo) y se ajusta junto con las palabras. Recibe sus propias pulsaciones.

text("Made with ")
    .text_base()
    .line_height(px(26.0))
    .child(image(LOGO).size(px(18.0)).rounded(px(4.0)))
    .child(" NSH. Status ")
    .child(
        view()
            .bg(rgb(0x1a7f37))
            .rounded(px(9.0))
            .px(px(7.0))
            .h(px(18.0))
            .justify_center()
            .child(text("LIVE").text_color(white()).text_size(px(11.0)).font_weight(FontWeight::BOLD)),
    )

Los adjuntos recortados por numberOfLines o por unos puntos suspensivos quedan ocultos. Al copiar un texto seleccionable se omiten.

#Espacios en blanco

El texto JSX sigue las reglas de React: las líneas se recortan salvo en los extremos del texto, las líneas con solo espacios en blanco se descartan y las líneas restantes se unen con un espacio. Los espacios dentro de una misma línea se conservan ({a} {b} conserva su espacio). Escribe {" "} o una expresión de cadena cuando un span necesite un espacio al inicio o al final. Las cadenas de Rust se dibujan tal como se escriben.

#Props

Además de las props comunes (con style tipado como TextStyle):

Prop Rust Tipo Valor por defecto Descripción
numberOfLines (style) .number_of_lines(n), .truncate() number 0 El máximo de líneas que se muestran; 0 las muestra todas.
textOverflow (style) .text_overflow(EllipsizeMode::Middle) "ellipsis" | "clip" | "ellipsis-start" | "ellipsis-middle" "ellipsis" Cómo se corta el texto que no cabe en numberOfLines: con puntos suspensivos al final, al principio o en medio, o recortado.
allowFontScaling .allow_font_scaling(..) boolean true Sigue el ajuste del tamaño de texto del sistema.
maxFontSizeMultiplier .max_font_size_multiplier(..) number 0 El límite superior de la escala del sistema; 0 significa sin límite.
adjustsFontSizeToFit .shrink_to_fit(min_scale) boolean false Reduce la fuente hasta que el texto quepa en sus líneas (numberOfLines) y en su caja.
minimumFontScale .shrink_to_fit(min_scale) number Cuánto como máximo se reduce adjustsFontSizeToFit (0..1; al menos 0.1).
userSelect (style) .select_text(), .select_none() "auto" | "none" | "text" "auto" "text": el usuario puede seleccionar rangos del texto: una pulsación larga selecciona una palabra, los tiradores la amplían y el menú de edición la copia. "auto" toma el ajuste de las vistas que rodean al texto; si no hay ninguno definido, el texto no es seleccionable, salvo en los navegadores de escritorio, donde lo es a menos que esté dentro de un elemento interactivo; "none" lo mantiene no seleccionable también allí.
selectionColor (style) .selection_color(..) ColorValue el color de acento de la plataforma El resaltado y los tiradores de la selección.
onTextLayout (e: { lines: TextLayoutLine[] }) => void Las líneas distribuidas tras cada layout.

Las clases de Tailwind select-text, select-none y select-auto definen userSelect, line-clamp-* define numberOfLines, text-ellipsis y text-clip definen textOverflow, y truncate es una sola línea cortada con puntos suspensivos. El resaltado y los tiradores de la selección toman la clave de estilo selectionColor (selection:text-*), por defecto el color de acento de la plataforma.

#Estilos de texto

TextStyle añade a los estilos de vista: color, fontSize, fontWeight ("normal", "bold" o 100–900), fontStyle, fontFamily, lineHeight, letterSpacing, textAlign, textAlignVertical, textTransform, textDecorationLine, textDecorationColor, textDecorationStyle, textDecorationThickness, textUnderlineOffset, textShadow (el text-shadow de CSS), fontVariant, writingDirection, numberOfLines y textOverflow. En Rust son métodos de Styled: .text_color(..), .text_size(..) (o .text_sm(), .text_lg(), …), .font_weight(FontWeight::SEMIBOLD), .italic(), .font_family(..), .line_height(..), .letter_spacing(..), .text_center(), .uppercase(), .underline(), .line_through(), .tabular_nums(), .number_of_lines(..) y el resto. Consulta la referencia de estilos y text.

La familia tipográfica es una familia del sistema o un archivo de fuente de la app:

// assets/fonts/Inter-Regular.ttf, shipped by the registry (`registry.assets(assets!())`).
const INTER: FontAsset = asset!("fonts/Inter-Regular.ttf");

text("Inter").font_family(INTER.family).text_size(px(17.0))

La fuente se registra antes del primer frame. Consulta fuentes.

#Eventos

Evento Rust Payload Cuándo
onTextLayout { lines: TextLayoutLine[] } Después de cada layout del párrafo.
onPress, onLongPress, … .on_press, .on_long_press, … PressEvent (ninguno en Rust) Igual que en view; en un span, pulsaciones sobre sus caracteres.
onLayout .on_layout { bounds } El frame del bloque cambió.

TextLayoutLine es { text, x, y, width, height, ascender, descender, capHeight, xHeight }, dentro de la caja de contenido del texto:

const lines = signal(0);
<Text onTextLayout={(e) => lines.set(e.lines.length)}>{article}</Text>;

Rust mide una cadena antes de distribuirla con measure_text(..).

#Métodos de ref

Ninguno propio.

#Notas de plataforma

  • La selección permanece dentro de un bloque de texto (un texto y sus spans). iOS muestra su menú de edición (Copy, Select All, Look Up, Translate, Share), Android una barra de herramientas flotante (Copy, Select all, Share).
  • Las decoraciones de texto distintas de una línea simple sólida (double, dotted, dashed, wavy) las dibuja NSH en iOS.
  • Los textos de iOS dibujan fondos sólidos y sombras exteriores.
  • fontScale() de nsh (font_scale() en Rust) informa de la escala de texto actual del sistema.