このページは機械翻訳のため、誤りを含む場合があります。 英語の原文を読む

要素

AlertDialog

AlertDialog は、プラットフォーム標準のアラートまたはアクションシートを表示します。iOS と Mac Catalyst では UIAlertController、Android では AlertDialog(アクションシートの場合はリストダイアログ)、その他のプラットフォームではプラットフォームのダイアログの外観に合わせたアラートが使われます。AlertDialog.show(Rust では alert_dialog() の .show())は、ユーザーが選択したアクションの id である { action } で解決される promise(Rust では future)を返します。 ラベルはすべてアプリ側から渡されるため、アラートにはアプリが指定した内容が、アプリの言語で表示されます。

el-alert-dialog.tsx
el-alert-dialog.tsxLive
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))
}

独自の外観を持つダイアログ(カード、シート、全画面フローなど)には、Dialog を使用してください。

#AlertDialog.show

AlertDialog.show(options: AlertDialogOptions): Promise<AlertDialogResult>
alert_dialog().title(..).description(..).action(..).cancel(..).destructive(..).show()
    -> impl Future<Output = Result<AlertDialogResult, String>>

結果は { action }(AlertDialogResult)です。選択されたアクションの id になります。アクションが選択されずにダイアログが閉じられた場合は null(None)になります。これには、ダイアログ外のタップ、Escape キーまたは戻るボタンによる終了が含まれ、closeOnBackdropPress と closeOnEscape で許可されている場合に限ります。アクションがない場合、promise は TypeError(Rust では Err)で reject されます。Rust では、nsh_base::alert_dialog() がオプションごと、アクションごとのメソッドでダイアログを構成します。アクションは表示される順に指定し、.show() で表示します。その結果はタスク(spawn)内で await してください。

#アクションシート

アクションシートは、"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.
        }
    });
}

#AlertDialogOptions

フィールドRust型デフォルト説明
title.title(..)string | nullなし説明文の上に表示されます。
description.description(..)string | nullなしタイトルの下に表示されます。
actions.action(..)、.cancel(..)、.destructive(..)(アクションごとに 1 つ)AlertDialogAction[]必須アクションを表示順に指定します(1 つ以上必要。Android のアラートでは最大 3 つ)。
presentation.action_sheet()"alert" | "actionSheet""alert"中央に表示されるアラート、または下から表示されるアクションのシート(Android ではリストダイアログ)。
closeOnEscape.close_on_escape(..)boolean | nullアラートでは false、アクションシートでは trueEscape キーと Android の戻るボタンで、アクションなしでダイアログを閉じます。
closeOnBackdropPress.close_on_backdrop_press(..)boolean | nullアラートでは false、アクションシートでは trueダイアログ外のタップで、アクションなしでダイアログを閉じます。

#AlertDialogResult

フィールドRust型説明
actionactionstring | null(Option<SharedString>)選択されたアクションの id。アクションが選択されずにダイアログが閉じられた場合は null になります。

#AlertDialogAction

フィールドRust型デフォルト説明
idメソッドの第 1 引数string必須アクションが選択されたときに show が action として返す値です。アプリ側で付ける名前であり、ラベルの言語が変わっても同じ値のままです。
labelメソッドの第 2 引数string必須アクションに表示される文言。
role.action(..)、.cancel(..)、.destructive(..)"default" | "cancel" | "destructive""default"アクションの意味です。プラットフォームがロールに応じて配置と描画を行います(iOS では破壊的アクションは赤で表示されます)。

#プラットフォーム別の注意事項

  • iOS: UIAlertController がアクションを配置します。"cancel" ロールのアクションは、iOS がキャンセルアクションを配置する位置に置かれ、アクションシートでは下部に分離して表示されます。iPad と Mac Catalyst では、アクションシートはタイトルなしのキャンセルアクションを持つポップオーバーになります。ポップオーバー外をタップすると閉じ、action は null になります。
  • Android: AlertDialog には最大 3 つのアクションが表示され、位置によって割り当てられます。最後のアクションがポジティブボタン、その前のアクションがネガティブボタン、さらにその前のアクションがニュートラルボタンになります。アクションシートはダイアログ内のアクションのリストで、キャンセルアクションの代わりにダイアログ外のタップまたは戻るボタンで閉じます(このとき action は null になります)。リストダイアログには title が表示され、シートにタイトルがない場合は description が表示されます。
  • Windows、Linux、GPUI、Web: NSH は、プラットフォームのダイアログの外観でアラートまたはアクションシートを描画します。