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

Elementos

Tooltip

Un tooltip es una pista breve junto a aquello a lo que se refiere: lo que hace un botón de icono, el texto completo de una etiqueta truncada. En escritorio y en navegadores de escritorio aparece cuando el puntero ha permanecido sobre su disparador durante delay (500 ms), y se oculta cuando el puntero sale o pulsa el disparador. Los teléfonos y las tabletas solo muestran un tooltip cuando la app lo abre (open). Nunca captura el puntero ni las pulsaciones, por lo que el disparador funciona exactamente igual que sin él.

el-tooltip.tsx
el-tooltip.tsxLive
import { mount, signal, Text, View } from "nsh";
import { type OpenChangeReason, Tooltip } from "nsh/base";
import { button, buttonText, createPresence, desktop, Inspector, Log, Panel, Stage, ui, wide } from "./palette";

/** An icon button with a hint of the app's look, above or below it. */
function Tool(props: {
  icon: string;
  label: string;
  hint: string;
  placement: "top" | "bottom";
  pinned: boolean;
  onPress: () => void;
  onOpenChange: (e: { open: boolean; reason: OpenChangeReason }) => void;
}) {
  const hovered = signal(false);
  // The bubble stays made between showings: it comes in from its side as it shows.
  const presence = createPresence(props.placement);
  return (
    <View style={{ alignItems: "center", gap: 6 }}>
      <View
        onPress={props.onPress}
        accessibilityLabel={props.label}
        {...button("secondary", { align: "center", style: { width: desktop ? 36 : 52, height: desktop ? 36 : 52, paddingHorizontal: 0 } })}
        tooltip={
          <Tooltip
            open={props.pinned || hovered()}
            placement={props.placement}
            offset={8}
            margin={8}
            delay={400}
            onOpenChange={(e) => {
              hovered.set(e.open);
              props.onOpenChange(e);
            }}
            onOpen={presence.show}
            onClose={presence.hide}
          >
            <View style={{ backgroundColor: ui().text, borderRadius: 7, paddingHorizontal: 10, paddingVertical: 6, ...presence.style }}>
              <Text style={{ fontSize: 13, color: ui().page }}>{props.hint}</Text>
            </View>
          </Tooltip>
        }
      >
        <Text style={{ fontSize: desktop ? 17 : 22, color: ui().text }}>{props.icon}</Text>
      </View>
      <Text style={{ fontSize: 11, color: ui().faint }}>{props.placement}</Text>
    </View>
  );
}

function App() {
  const events = signal<string[]>([]);
  const pressed = signal("—");
  const pinned = signal(false);
  const log = (name: string) => ({ open, reason }: { open: boolean; reason: OpenChangeReason }) =>
    events.set([`${name} ${open ? "shown" : "hidden"} · ${reason}`, ...events()].slice(0, 6));
  const tools = () => (
    <>
      <Tool icon="★" label="Star" hint="Add to favorites" placement="top" pinned={pinned()} onPress={() => pressed.set("Star")} onOpenChange={log("Star")} />
      <Tool icon="↗" label="Share" hint="Share with your team" placement="bottom" pinned={pinned()} onPress={() => pressed.set("Share")} onOpenChange={log("Share")} />
      <Tool icon="✕" label="Delete" hint="Move to the trash" placement="top" pinned={pinned()} onPress={() => pressed.set("Delete")} onOpenChange={log("Delete")} />
    </>
  );
  // The `tooltip` prop of any element: a `Tooltip` with the app's look.
  const pin = () => (
    <View
      tooltip={
        <Tooltip style={{ backgroundColor: ui().text, borderRadius: 7, paddingHorizontal: 10, paddingVertical: 6 }}>
          <Text style={{ fontSize: 13, color: ui().page }}>Shows every tooltip without a pointer</Text>
        </Tooltip>
      }
      onPress={() => pinned.set(!pinned())}
      {...button("secondary", { align: "center", style: wide() ? {} : { flex: 1 } })}
    >
      <Text style={buttonText()}>
        {wide() ? (pinned() ? "Unpin the tooltips" : "Pin the tooltips") : pinned() ? "Unpin" : "Pin"}
      </Text>
    </View>
  );
  const onPress = () => (
    <Panel title="onPress">
      <Text style={{ fontSize: 13, color: ui().text }}>{pressed()}</Text>
    </Panel>
  );
  const changes = () => <Log title="onOpenChange({ open, reason })" lines={events()} minHeight={130} />;

  return (
    <View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 14 }}>
      <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>tooltip</Text>
      <Text style={{ color: ui().muted }}>
        {desktop ? "Rest the pointer on a button; a click still presses it" : "Tooltips follow a pointer: pin them to see them here"}
      </Text>

      {() =>
        wide() ? (
          <View style={{ flex: 1, flexDirection: "row", gap: 16 }}>
            <Stage gap={40}>
              <View style={{ flexDirection: "row", justifyContent: "center", gap: 64 }}>{tools()}</View>
              {pin()}
            </Stage>
            <Inspector>
              {onPress()}
              {changes()}
            </Inspector>
          </View>
        ) : (
          <>
            <View style={{ flex: 1, minHeight: 200, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 28 }}>{tools()}</View>
            <View style={{ flexDirection: "row", gap: 8 }}>{pin()}</View>
            {onPress()}
            {changes()}
          </>
        )
      }
    </View>
  );
}

mount(App);

Tooltip de nsh/base se pasa como la prop tooltip que tiene todo elemento (Props comunes), como el .tooltip(..) de GPUI: el elemento es sobre el que descansa el puntero y aquel respecto al cual se coloca. El tooltip en sí es la burbuja: se estiliza con class / style, y sus hijos son lo que dice.

use nsh_base::tooltip;
use nsh_core::prelude::*;

fn save_button(on_save: impl Fn() + 'static) -> impl IntoElement {
    view()
        .on_press(on_save)
        .accessibility_label("Save")
        .p_2()
        .child(text("💾"))
        .tooltip(
            tooltip()
                .placement(Placement::Bottom)
                .rounded_md()
                .bg(rgb(0x111827))
                .px_2()
                .py_1()
                .child(text("Save the document").text_color(white())),
        )
}

Por defecto se coloca encima de su disparador (placement), en el lado opuesto cuando no hay espacio, y permanece dentro del área segura. Con open la app decide cuándo se muestra: sigue a onOpenChange, que informa de que el puntero descansa sobre el disparador o lo abandona, y puede mostrarse en cualquier dispositivo.

Aparece y desaparece con tus transiciones: startingClass es su aspecto en el primer fotograma y endingClass su aspecto una vez oculto, que conserva mientras se ejecutan las clases transition-* de su class; a continuación viene onClose.

<Tooltip
  class="rounded-md bg-gray-900 px-2 py-1 transition-opacity duration-100"
  startingClass="opacity-0"
  endingClass="opacity-0"
>
  <Text class="text-white">Save the document</Text>
</Tooltip>

#Props

Prop Rust Tipo Valor por defecto Descripción
children .child(..) JSX.Element ninguno Lo que dice.
class, style métodos .app_class(..), Styled string, StyleProp<ViewStyle> ninguno El aspecto de la burbuja: el propio tooltip.
startingClass, startingStyle .starting_class(..), .starting_style(..) string, StyleProp<ViewStyle> ninguno Su aspecto en el primer fotograma: sus transiciones parten de él.
endingClass, endingStyle .ending_class(..), .ending_style(..) string, StyleProp<ViewStyle> ninguno Su aspecto una vez oculto: se mantiene mientras se ejecutan sus transiciones.
open .open(..) boolean ninguno Mostrado, controlado por la app.
defaultOpen .default_open(..) boolean false Mostrado al principio, sin open.
delay .delay(..) number 500 Milisegundos que el puntero descansa sobre el disparador antes de que se muestre; Infinity: nunca por sí mismo, solo cuando la app lo abre.
disabled .disabled(..) boolean false Nunca se muestra.
placement .placement(..) "top" | "bottom" | "left" | "right" "top" El lado del disparador en el que se coloca.
align .align(..) "start" | "center" | "end" "center" Cómo se alinea con el disparador a lo largo de ese lado.
offset .offset(..) number 4 El espacio entre el disparador y el tooltip.
margin .margin(..) number 4 La distancia que mantiene respecto a los bordes del área segura.

#Eventos

Evento Rust Descripción
onOpenChange .on_open_change(|e| ..) El puntero descansó sobre el disparador, o lo abandonó o lo pulsó (reason "hover").
onOpen .on_open(|| ..) Se muestra.
onClose .on_close(|| ..) Ha desaparecido, tras su salida; también cuando la app lo ocultó.

#Accesibilidad

Un tooltip repite para los lectores de pantalla lo que dice su disparador: dale también un accessibilityLabel al disparador, de modo que la pista esté disponible para todos.