Skip to content

Instantly share code, notes, and snippets.

@teramotodaiki
Last active January 29, 2020 12:28
Show Gist options
  • Save teramotodaiki/f6f68a99543245c6e338474732d349bf to your computer and use it in GitHub Desktop.
Save teramotodaiki/f6f68a99543245c6e338474732d349bf to your computer and use it in GitHub Desktop.
import { Button, Dialog, DialogActions, DialogTitle } from '@material-ui/core';
import * as React from 'react';
import { Subject } from 'rxjs';
export interface IConfirm {
title: string;
ok: string;
cancel: string;
resolve: (ok: boolean) => void;
}
/**
* 現在表示している、あるいは保留されている内容
*/
const confirm$ = new Subject<IConfirm>();
/**
* ユーザーに簡単な確認を求めるためのダイアログを表示する
* OK なら Promise<true>, そうでなければ Promise<false> を返す
*/
export function useConfirm() {
return React.useCallback((title: string, ok: string, cancel: string) => {
return new Promise<boolean>(resolve => {
confirm$.next({
title,
ok,
cancel,
resolve
});
});
}, []);
}
/**
* ユーザーに確認を求めるダイアログ
* このコンポーネントはアプリのなかで1つだけ存在する
*/
export function ConfirmManager() {
// 現在表示すべき内容
const [showing, setShowing] = React.useState<IConfirm>();
const close = React.useCallback(() => setShowing(undefined), []);
// 新しい内容が追加されたら更新する
const stackRef = React.useRef<(IConfirm | undefined)[]>([]);
const update = React.useCallback(() => {
// スタックの最も上にある1件を表示する。なければダイアログを閉じる
setShowing(stackRef.current[0]);
}, []);
React.useEffect(() => {
return confirm$.subscribe(
value => {
stackRef.current.push(value);
update();
},
error => {
console.error(error);
close();
},
close
).unsubscribe;
}, []);
// 決定またはキャンセル
const ok = React.useCallback(() => {
stackRef.current[0]?.resolve(true); // Promise.resolve(true)
stackRef.current.shift(); // 今表示している内容を取り除く
update(); // 次があれば表示
}, []);
const cancel = React.useCallback(() => {
stackRef.current[0]?.resolve(false); // Promise.resolve(false)
stackRef.current.shift(); // 今表示している内容を取り除く
update(); // 次があれば表示
}, []);
return (
<Dialog open={Boolean(showing)} onClose={close}>
<DialogTitle>{showing?.title}</DialogTitle>
<DialogActions>
<Button variant="contained" color="primary" onClick={ok}>
{showing?.ok}
</Button>
<Button variant="text" onClick={cancel}>
{showing?.cancel}
</Button>
</DialogActions>
</Dialog>
);
}
import { Button } from '@material-ui/core';
import * as React from 'react';
import { useConfirm, ConfirmManager } from './ConfirmManager.tsx';
export interface Example() {
const confirm = useConfirm();
const dangerCallback = React.useCallback(async () => {
const yes = await confirm('本当にいいですか?', 'はい', 'いいえ');
if (yes) {
// GO!!!
}
}, [])
return (
<>
{/* これは React アプリケーション内に1つだけあれば良い */}
<ConfirmManager />
{/* 確認を要する処理を実行するボタン */}
<Button onClick={dangerCallback}>実行</Button>
</>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment