117 lines
2.9 KiB
TypeScript
117 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useCallback, useRef } from "react";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogContent,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogCancel,
|
|
AlertDialogAction,
|
|
} from "@/components/ui/alert-dialog";
|
|
|
|
interface UseUnsavedChangesGuardOptions {
|
|
hasChanges: () => boolean;
|
|
onClose: () => void;
|
|
title?: string;
|
|
description?: string;
|
|
}
|
|
|
|
interface UnsavedChangesGuard {
|
|
handleOpenChange: (open: boolean) => void;
|
|
tryClose: () => void;
|
|
doClose: () => void;
|
|
showDialog: boolean;
|
|
confirmClose: () => void;
|
|
cancelClose: () => void;
|
|
title: string;
|
|
description: string;
|
|
}
|
|
|
|
export function useUnsavedChangesGuard({
|
|
hasChanges,
|
|
onClose,
|
|
title = "변경사항이 있습니다",
|
|
description = "저장하지 않은 변경사항이 사라집니다. 정말 닫으시겠습니까?",
|
|
}: UseUnsavedChangesGuardOptions): UnsavedChangesGuard {
|
|
const [showDialog, setShowDialog] = useState(false);
|
|
const hasChangesRef = useRef(hasChanges);
|
|
hasChangesRef.current = hasChanges;
|
|
|
|
const attemptClose = useCallback(() => {
|
|
if (hasChangesRef.current()) {
|
|
setShowDialog(true);
|
|
} else {
|
|
onClose();
|
|
}
|
|
}, [onClose]);
|
|
|
|
const handleOpenChange = useCallback(
|
|
(open: boolean) => {
|
|
if (!open) {
|
|
attemptClose();
|
|
}
|
|
},
|
|
[attemptClose],
|
|
);
|
|
|
|
const confirmClose = useCallback(() => {
|
|
setShowDialog(false);
|
|
onClose();
|
|
}, [onClose]);
|
|
|
|
const cancelClose = useCallback(() => {
|
|
setShowDialog(false);
|
|
}, []);
|
|
|
|
const doClose = useCallback(() => {
|
|
setShowDialog(false);
|
|
onClose();
|
|
}, [onClose]);
|
|
|
|
return {
|
|
handleOpenChange,
|
|
tryClose: attemptClose,
|
|
doClose,
|
|
showDialog,
|
|
confirmClose,
|
|
cancelClose,
|
|
title,
|
|
description,
|
|
};
|
|
}
|
|
|
|
interface UnsavedChangesDialogProps {
|
|
guard: UnsavedChangesGuard;
|
|
}
|
|
|
|
export function UnsavedChangesDialog({ guard }: UnsavedChangesDialogProps) {
|
|
return (
|
|
<AlertDialog open={guard.showDialog} onOpenChange={(open) => !open && guard.cancelClose()}>
|
|
<AlertDialogContent className="max-w-[95vw] sm:max-w-[420px]">
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle className="text-base sm:text-lg">
|
|
{guard.title}
|
|
</AlertDialogTitle>
|
|
<AlertDialogDescription className="text-xs sm:text-sm">
|
|
{guard.description}
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter className="gap-2 sm:gap-0">
|
|
<AlertDialogCancel className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm">
|
|
취소
|
|
</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
onClick={guard.confirmClose}
|
|
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
>
|
|
닫기
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
);
|
|
}
|