Files
sori.studio/composables/useAdminUnsavedChangesGuard.js

101 lines
2.5 KiB
JavaScript

/**
* 관리자 편집 화면의 미저장 변경 이탈을 막는다.
* @param {import('vue').Ref<boolean> | import('vue').ComputedRef<boolean>} isDirty - 변경 여부
* @param {{ onLeaveConfirmed?: () => void | Promise<void> }} options - 이탈 승인 옵션
* @returns {{
* isUnsavedModalOpen: import('vue').Ref<boolean>,
* stayOnUnsavedPage: () => void,
* leaveUnsavedPage: () => Promise<void>,
* allowNextRouteLeave: () => void
* }} 이탈 확인 상태와 동작
*/
export const useAdminUnsavedChangesGuard = (isDirty, options = {}) => {
const isUnsavedModalOpen = ref(false)
const pendingRoute = ref(null)
const isNextRouteAllowed = ref(false)
/**
* 현재 이탈을 막아야 하는지 확인한다.
* @returns {boolean} 이탈 차단 여부
*/
const shouldBlockLeave = () => Boolean(unref(isDirty)) && !isNextRouteAllowed.value
/**
* 다음 라우트 이동을 한 번 허용한다.
* @returns {void}
*/
const allowNextRouteLeave = () => {
isNextRouteAllowed.value = true
}
/**
* 현재 페이지에 머문다.
* @returns {void}
*/
const stayOnUnsavedPage = () => {
pendingRoute.value = null
isUnsavedModalOpen.value = false
}
/**
* 미저장 변경을 버리고 이동한다.
* @returns {Promise<void>}
*/
const leaveUnsavedPage = async () => {
const route = pendingRoute.value
pendingRoute.value = null
isUnsavedModalOpen.value = false
isNextRouteAllowed.value = true
await options.onLeaveConfirmed?.()
if (route?.fullPath) {
await navigateTo(route.fullPath)
}
}
onBeforeRouteLeave((to) => {
if (isNextRouteAllowed.value) {
isNextRouteAllowed.value = false
return true
}
if (!shouldBlockLeave()) {
return true
}
pendingRoute.value = to
isUnsavedModalOpen.value = true
return false
})
/**
* 브라우저 탭 닫기와 새로고침을 기본 확인창으로 막는다.
* @param {BeforeUnloadEvent} event - 브라우저 이탈 이벤트
* @returns {void}
*/
const handleBeforeUnload = (event) => {
if (!shouldBlockLeave()) {
return
}
event.preventDefault()
event.returnValue = ''
}
onMounted(() => {
window.addEventListener('beforeunload', handleBeforeUnload)
})
onBeforeUnmount(() => {
window.removeEventListener('beforeunload', handleBeforeUnload)
})
return {
isUnsavedModalOpen,
stayOnUnsavedPage,
leaveUnsavedPage,
allowNextRouteLeave
}
}