Éléments
Tooltip
Un tooltip est une courte indication placée à côté de l’élément qu’elle concerne : ce que fait un bouton à icône, le texte complet d’un libellé tronqué.
Sur les ordinateurs de bureau et dans les navigateurs de bureau, il s’affiche une fois que le pointeur est resté sur son déclencheur pendant delay (500 ms), et
se masque quand le pointeur le quitte ou appuie sur le déclencheur. Les téléphones et les tablettes n’affichent un tooltip que lorsque l’application l’ouvre
(open). Il ne capte jamais le pointeur ni les appuis, de sorte que le déclencheur fonctionne exactement comme sans lui.
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 transmet via la prop tooltip que possède chaque élément
(Props communes), comme le .tooltip(..) de GPUI : l’élément est ce sur quoi le pointeur s’arrête et ce contre quoi
le tooltip est placé. Le tooltip lui-même est la bulle : stylez-la avec class / style, et ses enfants constituent ce qu’elle dit.
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())),
)
}import { Text, View } from "nsh";
import { Tooltip } from "nsh/base";
function SaveButton(props: { onSave: () => void }) {
return (
<View
onPress={props.onSave}
accessibilityLabel="Save"
class="p-2"
tooltip={
<Tooltip placement="bottom" class="rounded-md bg-gray-900 px-2 py-1">
<Text class="text-white">Save the document</Text>
</Tooltip>
}
>
<Text>💾</Text>
</View>
);
}Par défaut, il se place au-dessus de son déclencheur (placement), de l’autre côté s’il n’y a pas assez de place, et reste dans la zone
de sécurité. Avec open, c’est l’application qui décide quand il s’affiche : il suit onOpenChange, qui signale que le pointeur s’est arrêté sur le
déclencheur ou l’a quitté, et il peut s’afficher sur n’importe quel appareil.
Il apparaît et disparaît avec vos transitions : startingClass est son apparence dans sa première image et endingClass son apparence
une fois masqué, qu’il conserve pendant l’exécution des classes transition-* de son class ; onClose suit.
<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 | Type | Défaut | Description |
|---|---|---|---|---|
children | .child(..) | JSX.Element | aucun | Ce qu’il dit. |
class, style | méthodes .app_class(..), Styled | string, StyleProp<ViewStyle> | aucun | L’apparence de la bulle : le tooltip lui-même. |
startingClass, startingStyle | .starting_class(..), .starting_style(..) | string, StyleProp<ViewStyle> | aucun | Son apparence dans sa première image : ses transitions partent de là. |
endingClass, endingStyle | .ending_class(..), .ending_style(..) | string, StyleProp<ViewStyle> | aucun | Son apparence une fois masqué : il la conserve pendant l’exécution de ses transitions. |
open | .open(..) | boolean | aucun | Affiché, contrôlé par l’application. |
defaultOpen | .default_open(..) | boolean | false | Affiché au départ, sans open. |
delay | .delay(..) | number | 500 | Millisecondes pendant lesquelles le pointeur reste sur le déclencheur avant l’affichage ; Infinity : jamais de lui-même, uniquement quand l’application l’ouvre. |
disabled | .disabled(..) | boolean | false | Il ne s’affiche jamais. |
placement | .placement(..) | "top" | "bottom" | "left" | "right" | "top" | Le côté du déclencheur où il se place. |
align | .align(..) | "start" | "center" | "end" | "center" | Son alignement sur le déclencheur le long de ce côté. |
offset | .offset(..) | number | 4 | L’écart entre le déclencheur et le tooltip. |
margin | .margin(..) | number | 4 | La distance qu’il conserve par rapport aux bords de la zone de sécurité. |
#Événements
| Événement | Rust | Description |
|---|---|---|
onOpenChange | .on_open_change(|e| ..) | Le pointeur s’est arrêté sur le déclencheur, l’a quitté ou a appuyé dessus (reason "hover"). |
onOpen | .on_open(|| ..) | Il est affiché. |
onClose | .on_close(|| ..) | Il a disparu, après sa sortie ; également lorsque l’application l’a masqué. |
#Accessibilité
Un tooltip répète aux lecteurs d’écran ce que dit son déclencheur : donnez aussi un accessibilityLabel au déclencheur, afin que
l’indication soit disponible pour tout le monde.