एलिमेंट
Tooltip
Tooltip एक छोटा संकेत (hint) है जो उस चीज़ के बगल में दिखता है जिसके बारे में वह है: जैसे किसी आइकन बटन का काम, या कटे हुए (truncated) लेबल का पूरा टेक्स्ट।
डेस्कटॉप और डेस्कटॉप ब्राउज़र पर यह तब दिखता है जब पॉइंटर अपने ट्रिगर पर delay (500 ms) तक टिका रहे, और
पॉइंटर के हटने या ट्रिगर को दबाने पर छिप जाता है। फ़ोन और टैबलेट पर Tooltip तभी दिखता है जब ऐप उसे खोले
(open)। यह कभी पॉइंटर या प्रेस को नहीं रोकता, इसलिए ट्रिगर ठीक वैसे ही काम करता है जैसे इसके बिना करता।
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);nsh/base से Tooltip को tooltip prop के रूप में दिया जाता है, जो हर element में होता है
(Common props), जैसे GPUI का .tooltip(..): यह element वही है जिस पर पॉइंटर टिकता है और जिसके सापेक्ष
Tooltip रखा जाता है। Tooltip स्वयं बबल है: इसे class / style से स्टाइल करें, और इसके children वही हैं जो यह कहता है।
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>
);
}यह डिफ़ॉल्ट रूप से अपने ट्रिगर के ऊपर आता है (placement), जगह न होने पर दूसरी ओर आता है, और सेफ़ एरिया के भीतर रहता है। open के साथ ऐप तय करता है कि यह कब दिखे: यह onOpenChange का अनुसरण करता है, जो ट्रिगर पर पॉइंटर के टिकने या उससे हटने की सूचना देता है, और इसे किसी भी डिवाइस पर दिखा सकता है।
यह आपके transitions के साथ आता और जाता है: startingClass इसका पहले फ़्रेम में रूप है और endingClass छिपने के बाद का रूप, जिसे यह तब तक बनाए रखता है जब तक इसके class की transition-* क्लासेज़ चलती हैं; 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 | Type | Default | विवरण |
|---|---|---|---|---|
children | .child(..) | JSX.Element | none | यह क्या कहता है। |
class, style | .app_class(..), Styled methods | string, StyleProp<ViewStyle> | none | बबल का रूप: यानी स्वयं Tooltip। |
startingClass, startingStyle | .starting_class(..), .starting_style(..) | string, StyleProp<ViewStyle> | none | पहले फ़्रेम में इसका रूप: इसके transitions यहीं से शुरू होते हैं। |
endingClass, endingStyle | .ending_class(..), .ending_style(..) | string, StyleProp<ViewStyle> | none | छिपने के बाद इसका रूप: transitions चलने तक यह बना रहता है। |
open | .open(..) | boolean | none | दिखाया गया, ऐप द्वारा नियंत्रित। |
defaultOpen | .default_open(..) | boolean | false | शुरू में दिखाया गया, open के बिना। |
delay | .delay(..) | number | 500 | दिखने से पहले पॉइंटर ट्रिगर पर कितने मिलीसेकंड टिकता है; Infinity: अपने आप कभी नहीं, केवल तब जब ऐप इसे खोले। |
disabled | .disabled(..) | boolean | false | यह कभी नहीं दिखता। |
placement | .placement(..) | "top" | "bottom" | "left" | "right" | "top" | ट्रिगर का वह किनारा जिस ओर यह आता है। |
align | .align(..) | "start" | "center" | "end" | "center" | उस किनारे के साथ यह ट्रिगर से कैसे संरेखित होता है। |
offset | .offset(..) | number | 4 | ट्रिगर और Tooltip के बीच की दूरी। |
margin | .margin(..) | number | 4 | सेफ़ एरिया के किनारों से यह कितनी दूरी बनाए रखता है। |
#Events
| Event | Rust | विवरण |
|---|---|---|
onOpenChange | .on_open_change(|e| ..) | पॉइंटर ट्रिगर पर टिका, या उससे हटा या उसे दबाया (reason "hover")। |
onOpen | .on_open(|| ..) | यह दिखाया जा चुका है। |
onClose | .on_close(|| ..) | यह अपने exit के बाद हट चुका है; ऐप द्वारा छिपाए जाने पर भी। |
#Accessibility
Tooltip स्क्रीन रीडर को वही दोहराता है जो इसका ट्रिगर कहता है: ट्रिगर को भी accessibilityLabel दें, ताकि
संकेत सभी के लिए उपलब्ध रहे।