確認ダイアログ
:LiTarget: 用途
危険な操作の前に確認するダイアログパターン。
:LiSparkle: 特徴
- カスタムメッセージ
- タイプ入力確認
- Promise方式
:LiCode: コード(コピペ用)
'use client';
import { useState, useCallback, useRef } from 'react';
// Promise ベース確認ダイアログ
type Resolver = (ok: boolean) => void;
let resolverRef: Resolver | null = null;
let setterRef: ((opt: { msg: string; danger?: boolean } | null) => void) | null = null;
export function confirm(msg: string, opt: { danger?: boolean } = {}): Promise<boolean> {
return new Promise((resolve) => {
resolverRef = resolve;
setterRef?.({ msg, danger: opt.danger });
});
}
export function ConfirmHost() {
const [opt, setOpt] = useState<{ msg: string; danger?: boolean } | null>(null);
setterRef = setOpt;
if (!opt) return null;
const close = (ok: boolean) => { resolverRef?.(ok); setOpt(null); };
return (
<div className="fixed inset-0 z-50 bg-black/60 flex items-center justify-center p-4" onClick={() => close(false)}>
<div className="bg-zinc-900 border border-zinc-800 rounded-2xl p-6 max-w-md" onClick={(e) => e.stopPropagation()}>
<p className="text-zinc-100 mb-5">{opt.msg}</p>
<div className="flex justify-end gap-2">
<button onClick={() => close(false)} className="px-4 py-2 rounded-lg bg-zinc-800 text-zinc-300">キャンセル</button>
<button onClick={() => close(true)} className={`px-4 py-2 rounded-lg text-white ${opt.danger ? 'bg-rose-500' : 'bg-indigo-500'}`}>
{opt.danger ? '削除する' : '実行する'}
</button>
</div>
</div>
</div>
);
}
// 使い方:
// if (await confirm('本当に削除しますか?', { danger: true })) await api.delete();
:LiHandPointer: 使い方
対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。
:LiAlertCircle: 注意事項
- 依存パッケージを忘れず追加