短い処理の表示ActionProgressExperimental

保存や送信のような短い処理のあいだ、画面を塞いで「処理中」を伝える小さなダイアログです。中身は、上端の流れるバー、回る印(Spinner)、題と説明です。既定では 350ms 待ってから出すので、すぐ終わる処理では何も出ません。

プレビュー

0.2秒で終わる
出ない
0.45秒で終わる
2秒で終わる
0350ms2s
処理中ダイアログが出ている350ms より前に終われば出ず、出たら 500ms は消えません。

状態とバリエーション

待つ時間の直後に終わるとき

0.45秒で終わる保存です。350ms で出たダイアログは、処理が終わっても 500ms は消えません。

フォームの送信

FormActionProgress は、置かれた form の送信中だけ出ます。pending の状態を自分で持たなくて済みます(React 19 の useFormStatus)。

動きを減らす設定

バーと回る印が止まります。

プロパティ

表は横にスクロールできます
プロパティ型初期値説明
openboolean-処理中か。delayMs を過ぎても true のときだけダイアログが出ます。
minVisibleMsnumber500一度出たら、open が false になってもこの時間は出しておきます。
titleReactNode-何をしているか。DialogTitle になります。
descriptionReactNode-題の下の一文。
iconReactNode-既定の Spinner の代わりに置く絵。
delayMsnumber350出すまで待つ時間(ミリ秒)。
portalContainerHTMLElement | null-描く先の要素。渡すと、その中に並べて描きます。
classNamestring-DialogContent に足すクラス。

FormActionProgress は、ActionProgress の props のうち open 以外をすべて受け取ります。

使い方

"use client";

import * as React from "react";
import { ActionProgress, Button } from "@gunjo/ui";

export function SaveSettings() {
  const [container, setContainer] = React.useState<HTMLDivElement | null>(null);
  const [pending, setPending] = React.useState(false);
  const [saved, setSaved] = React.useState(0);

  // 本物のアプリでは、保存の要求が終わったところで pending(open に渡す値)を false にします
  const save = (ms: number) => {
    if (pending) return;
    setPending(true);
    window.setTimeout(() => {
      setPending(false);
      setSaved((n) => n + 1);
    }, ms);
  };

  return (
    // 開いたときの高さ(実測 157px)を 4px の倍数に切り上げて取ります
    <div ref={setContainer} className="relative grid min-h-40 w-full place-items-center [&>*]:[grid-area:1/1]">
      <div className="flex flex-col items-center gap-4">
      <div className="flex flex-wrap justify-center gap-2">
        <Button onClick={() => save(2000)}>保存する(2秒)</Button>
        <Button variant="outline" onClick={() => save(200)}>
          保存する(0.2秒)
        </Button>
      </div>
      <p className="text-sm text-muted-foreground" role="status">
        {pending ? "保存しています" : saved > 0 ? `保存しました(${saved} 回)` : ""}
      </p>
      </div>
      <ActionProgress
        portalContainer={container}
        open={pending}
        title="保存しています"
        description="終わるとこの画面に戻ります。"
      />
    </div>
  );
}

設計の判断

  • 既定では 350ms 待って出し、出たら 500ms は消しません。一瞬だけ出て消えると、画面がちらつくためです。
  • %は出しません。短い処理は途中の割合を返しません。流れるバーは「動いている」だけを伝えます。
  • open を false にしたときだけ閉じます。Esc や外側のクリックで閉じられると、処理中なのに送信のボタンをもう一度押せてしまい、二重に送られます。

使用コンポーネント

  • ProgressDialog

    数十秒以上かかる処理向け。絵・補助の枠・キャンセルを持つ。

  • Button

    loading はボタンの中だけ。画面は塞がない。

  • RouteProgress

    ページの移動中に上端へ出す細いバー。

  • Spinner

    回る印だけ。待つ時間もダイアログも持たない。

いつ・なぜ使うか(UIXHERO)

「いつ・なぜ使うか」の判断は、姉妹サイト UIXHERO の記事で解説しています。