Elemente
AlertDialog
AlertDialog zeigt den plattformeigenen Alert bzw. Action Sheet an: UIAlertController unter iOS und Mac Catalyst, einen
AlertDialog (oder einen Listendialog für ein Action Sheet) unter Android und einen Alert im Dialogstil der Plattform auf den
anderen Plattformen. AlertDialog.show (.show() von alert_dialog() in Rust) gibt ein Promise zurück (in Rust ein Future), das mit { action } aufgelöst wird: dem id der Aktion, die der Benutzer gewählt hat.
Jedes Label stammt aus der App, ein Alert sagt also genau das, was Ihre App sagt, in deren Sprache.
import { mount, signal, Text, View } from "nsh";
import { AlertDialog, Button } from "nsh/base";
import { button, buttonText, desktop, focusRing, hoverFill, Inspector, Panel, size, Stage, ui, wide } from "./palette";
type Options = Parameters<typeof AlertDialog.show>[0];
// What each button shows, and what it is about.
const alerts: { label: string; detail: string; danger?: boolean; options: Options }[] = [
{
label: "Delete…",
detail: "cancel + destructive roles",
danger: true,
options: {
title: "Delete the photo?",
description: "This cannot be undone.",
actions: [
{ label: "Cancel", role: "cancel", id: "cancel" },
{ label: "Delete", role: "destructive", id: "delete" },
],
},
},
{
label: "Save changes…",
detail: "three actions, closeOnEscape",
options: {
title: "Save changes?",
description: "Your edits to “Trip notes” are not saved yet.",
actions: [
{ label: "Don't save", role: "destructive", id: "discard" },
{ label: "Cancel", role: "cancel", id: "cancel" },
{ label: "Save", id: "save" },
],
closeOnEscape: true,
},
},
{
label: "Share…",
detail: 'presentation "actionSheet"',
options: {
title: "Share the photo",
presentation: "actionSheet",
actions: [
{ label: "Copy link", id: "copy" },
{ label: "Save", id: "save" },
{ label: "Delete", role: "destructive", id: "delete" },
{ label: "Cancel", role: "cancel", id: "cancel" },
],
},
},
];
function App() {
const answer = signal("–");
const shown = signal(0);
const show = async (options: Options) => {
shown.update((n) => n + 1);
const { action } = await AlertDialog.show(options);
answer.set(action === null ? "{ action: null } (closed without an action)" : `{ action: ${JSON.stringify(action)} }`);
};
const row = () => ({ ...ui().row, borderRadius: 12, padding: 14, gap: 2 });
const hint = desktop ? "Escape or a click outside the action sheet closes it with { action: null }." : "A tap outside the action sheet closes it with { action: null }.";
return (
<View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 12 }}>
<Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>alert-dialog</Text>
<Text style={{ color: ui().muted }}>The platform's own alert; every label comes from the app.</Text>
{() =>
wide() ? (
<View style={{ flex: 1, flexDirection: "row", gap: 16 }}>
{/* A row per alert: what it shows, and a button as wide as its label at the end. */}
<Stage width={400}>
<View style={{ ...ui().card, borderRadius: 14, overflow: "hidden" }}>
<Each of={alerts}>
{(a, i) => (
<>
{i() > 0 ? <View style={{ height: 1, marginLeft: 12, backgroundColor: ui().line }} /> : null}
<View style={{ flexDirection: "row", alignItems: "center", gap: 12, height: size.row + 4, paddingHorizontal: 12 }}>
<Text style={{ flex: 1, fontSize: 13, color: ui().muted }}>{a().detail}</Text>
<Button onPress={() => show(a().options)} {...button()}>
<Text style={{ ...buttonText(), color: a().danger ? ui().down : ui().text }}>{a().label}</Text>
</Button>
</View>
</>
)}
</Each>
</View>
</Stage>
<Inspector>
<Panel title={`AlertDialog.show resolved with (${shown()} shown)`}>
<Text style={{ color: ui().accent, fontSize: 14, fontWeight: "600" }}>{answer()}</Text>
</Panel>
<Text style={{ color: ui().faint, fontSize: 12 }}>{hint}</Text>
</Inspector>
</View>
) : (
<View style={{ gap: 10 }}>
<Each of={alerts}>
{(a) => (
<Button onPress={() => show(a().options)} style={row()} activeStyle={{ opacity: 0.7 }} hoverStyle={hoverFill()} focusStyle={focusRing()}>
<Text style={{ color: a().danger ? ui().down : ui().text, fontSize: 16, fontWeight: "600" }}>{a().label}</Text>
<Text style={{ color: ui().muted, fontSize: 13 }}>{a().detail}</Text>
</Button>
)}
</Each>
<View style={{ ...ui().inset, borderRadius: 12, padding: 14, gap: 4, marginTop: 4 }}>
<Text style={{ color: ui().muted, fontSize: 13 }}>AlertDialog.show resolved with ({shown()} shown)</Text>
<Text style={{ color: ui().accent, fontSize: 16, fontWeight: "600" }}>{answer()}</Text>
</View>
<Text style={{ color: ui().faint, fontSize: 13 }}>{hint}</Text>
</View>
)
}
</View>
);
}
mount(App);use nsh_base::{alert_dialog, button};
use nsh_core::prelude::*;
fn app() -> impl IntoElement {
let answer = signal(SharedString::from("–"));
let confirm = move || {
spawn(async move {
let choice = alert_dialog()
.title("Delete the photo?")
.description("This cannot be undone.")
.cancel("cancel", "Cancel")
.destructive("delete", "Delete")
.show()
.await;
let deleted = matches!(choice, Ok(r) if r.action.as_deref() == Some("delete"));
answer.set(if deleted { "deleted" } else { "kept" }.into());
});
};
view()
.flex_1()
.p_4()
.pt(px(48.0))
.gap_3()
.child(
button()
.on_press(confirm)
.rounded_lg()
.bg(rgb(0xdc2626))
.px_4()
.py_3()
.child(text("Delete…").text_color(white()).font_weight(FontWeight::SEMIBOLD)),
)
.child(text(answer))
}import { mount, signal, Text, View } from "nsh";
import { AlertDialog, Button } from "nsh/base";
function App() {
const answer = signal("–");
const confirm = async () => {
const { action } = await AlertDialog.show({
title: "Delete the photo?",
description: "This cannot be undone.",
actions: [
{ label: "Cancel", role: "cancel", id: "cancel" },
{ label: "Delete", role: "destructive", id: "delete" },
],
});
answer.set(action === "delete" ? "deleted" : "kept");
};
return (
<View style={{ flex: 1, padding: 16, paddingTop: 48, gap: 12 }}>
<Button onPress={confirm} class="rounded-lg bg-red-600 px-4 py-3 active:bg-red-700">
<Text class="text-center font-semibold text-white">Delete…</Text>
</Button>
<Text>{answer()}</Text>
</View>
);
}
mount(App);Für Dialoge mit eigenem Aussehen (eine Karte, ein Sheet, ein Vollbild-Ablauf) verwenden Sie Dialog.
#AlertDialog.show
AlertDialog.show(options: AlertDialogOptions): Promise<AlertDialogResult>
alert_dialog().title(..).description(..).action(..).cancel(..).destructive(..).show()
-> impl Future<Output = Result<AlertDialogResult, String>>
Das Ergebnis ist { action } (AlertDialogResult): das id der gewählten Aktion oder null (None), wenn der Dialog ohne Aktion geschlossen wurde:
durch einen Tipp außerhalb, Escape oder die Zurück-Taste, sofern closeOnBackdropPress und closeOnEscape dies erlauben. Ohne Aktionen wird das Promise mit einem TypeError abgelehnt (in Rust mit einem Err). In Rust richtet nsh_base::alert_dialog() den Dialog mit einer Methode pro Option und pro Aktion ein, in der Reihenfolge,
in der die Aktionen angezeigt werden, und .show() zeigt ihn an; warten Sie in einem Task auf die Antwort (spawn).
#Action sheets
Ein Action Sheet übernimmt dieselben Optionen mit der Präsentation "actionSheet":
use nsh_base::alert_dialog;
use nsh_core::prelude::*;
fn share() {
spawn(async move {
let choice = alert_dialog()
.title("Share the photo")
.action_sheet()
.action("copy", "Copy link")
.action("save", "Save")
.destructive("delete", "Delete")
.cancel("cancel", "Cancel")
.show()
.await;
match choice.ok().and_then(|r| r.action).as_deref() {
Some("copy") => { /* … */ }
Some("save") => { /* … */ }
Some("delete") => { /* … */ }
_ => {} // "cancel", or closed without an action.
}
});
}const { action } = await AlertDialog.show({
title: "Share the photo",
presentation: "actionSheet",
actions: [
{ label: "Copy link", id: "copy" },
{ label: "Save", id: "save" },
{ label: "Delete", role: "destructive", id: "delete" },
{ label: "Cancel", role: "cancel", id: "cancel" },
],
});#AlertDialogOptions
| Feld | Rust | Typ | Standard | Beschreibung |
|---|---|---|---|---|
title |
.title(..) |
string | null |
keiner | Über der Beschreibung. |
description |
.description(..) |
string | null |
keiner | Unter dem Titel. |
actions |
.action(..), .cancel(..), .destructive(..), eine pro Aktion |
AlertDialogAction[] |
erforderlich | Die Aktionen in ihrer Reihenfolge (mindestens eine; bei einem Android-Alert höchstens drei). |
presentation |
.action_sheet() |
"alert" | "actionSheet" |
"alert" |
Ein Alert in der Mitte oder ein Sheet mit Aktionen vom unteren Rand (unter Android ein Listendialog). |
closeOnEscape |
.close_on_escape(..) |
boolean | null |
false bei einem Alert, true bei einem Action Sheet |
Escape und die Android-Zurück-Taste schließen den Dialog ohne Aktion. |
closeOnBackdropPress |
.close_on_backdrop_press(..) |
boolean | null |
false bei einem Alert, true bei einem Action Sheet |
Ein Tipp außerhalb schließt den Dialog ohne Aktion. |
#AlertDialogResult
| Feld | Rust | Typ | Beschreibung |
|---|---|---|---|
action |
action |
string | null (Option<SharedString>) |
Das id der gewählten Aktion oder null, wenn der Dialog ohne Aktion geschlossen wurde. |
#AlertDialogAction
| Feld | Rust | Typ | Standard | Beschreibung |
|---|---|---|---|---|
id |
das erste Argument der Methode | string |
erforderlich | Was show bei Auswahl als action meldet: der Name der App dafür, der unabhängig von der Sprache der Labels gleich bleibt. |
label |
das zweite Argument der Methode | string |
erforderlich | Was sie anzeigt. |
role |
.action(..), .cancel(..), .destructive(..) |
"default" | "cancel" | "destructive" |
"default" |
Was sie bedeutet: Die Plattform platziert und zeichnet sie nach ihrer Rolle (eine destruktive Aktion ist unter iOS rot). |
#Hinweise zu den Plattformen
- iOS:
UIAlertControllerordnet die Aktionen an. Eine Aktion mit der Rolle"cancel"wird dort platziert, wo iOS Abbrechen-Aktionen platziert, und ein Action Sheet zeigt sie getrennt an seinem unteren Rand. Auf dem iPad und unter Mac Catalyst ist das Action Sheet ein Popover mit einer Abbrechen-Aktion ohne Titel: Ein Tipp außerhalb schließt es, undactionistnull. - Android: Ein
AlertDialogzeigt bis zu drei Aktionen an, die nach Position zugeordnet werden: Die letzte Aktion ist der positive Button, die davor der negative Button und die davor der neutrale Button. Ein Action Sheet ist eine Liste der Aktionen in einem Dialog, wobei ein Tipp außerhalb oder die Zurück-Taste an die Stelle der Abbrechen-Aktion tritt (actionist dannnull). Der Listendialog zeigt dentitleoder diedescription, wenn das Sheet keinen Titel hat. - Windows, Linux, GPUI und das Web: NSH zeichnet den Alert bzw. das Action Sheet im Dialogstil der Plattform.