114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
import { computed, shallowRef, watch, h, type VNode, ref } from 'vue'
|
|
import { useRoute, useRouter, loadRouteLocation, type RouteLocationNormalizedLoaded, type RouteLocationRaw } from 'vue-router'
|
|
import router from '@/router'
|
|
|
|
// this is adapted from vue-router
|
|
// https://github.com/vuejs/vue-router-next/blob/798cab0d1e21f9b4d45a2bd12b840d2c7415f38a/src/RouterView.ts#L125
|
|
function getRouteProps(route: RouteLocationNormalizedLoaded) {
|
|
const routePropsOption = route.matched[0]?.props.default
|
|
return routePropsOption
|
|
? routePropsOption === true
|
|
? route.params
|
|
: typeof routePropsOption === 'function'
|
|
? routePropsOption(route)
|
|
: routePropsOption
|
|
: {}
|
|
}
|
|
|
|
function resolveAndLoadRoute(route: RouteLocationRaw) {
|
|
return loadRouteLocation(router.resolve(route))
|
|
}
|
|
|
|
export function useRouteWithModal() {
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
|
|
const historyStateBackdropRoutePath = computed<RouteLocationRaw | undefined>(() => {
|
|
// every time the fullPath changes we check the history state
|
|
// this happens also initially
|
|
return route.fullPath
|
|
? window.history.state?.backdropRoutePath
|
|
: undefined
|
|
})
|
|
|
|
const routerIsReady = ref(false)
|
|
router.isReady().then(() => {
|
|
routerIsReady.value = true
|
|
})
|
|
|
|
const baseRoute = shallowRef<RouteLocationNormalizedLoaded>()
|
|
watch(
|
|
[routerIsReady, historyStateBackdropRoutePath],
|
|
async () => {
|
|
if (routerIsReady.value === false || !route.fullPath) {
|
|
// wait until we can work with routes
|
|
return
|
|
}
|
|
|
|
if (historyStateBackdropRoutePath.value === undefined) {
|
|
if (route.meta?.showAsModal !== true) {
|
|
baseRoute.value = route
|
|
return
|
|
}
|
|
// TODO: maybe load parent route here in the future,
|
|
// via: route.matched[route.matched.length - 2]
|
|
// see: https://router.vuejs.org/guide/migration/#removal-of-parent-from-route-locations
|
|
baseRoute.value = await resolveAndLoadRoute({ name: 'home' })
|
|
return
|
|
}
|
|
|
|
// we get the resolved route from the fullpath
|
|
// and wait for the route component to be loaded before we assign it
|
|
baseRoute.value = await resolveAndLoadRoute(historyStateBackdropRoutePath.value)
|
|
},
|
|
{immediate: true},
|
|
)
|
|
|
|
const backdropRoute = computed(() => route.fullPath !== baseRoute.value?.fullPath ? baseRoute.value : undefined)
|
|
|
|
const modalRoute = shallowRef<VNode>()
|
|
watch(
|
|
[backdropRoute, route],
|
|
() => {
|
|
if (routerIsReady.value === false || !route.fullPath || !backdropRoute.value) {
|
|
modalRoute.value = undefined
|
|
return
|
|
}
|
|
|
|
const props = getRouteProps(route)
|
|
|
|
props.isModal = true
|
|
props.backdropRoutePath = backdropRoute.value.fullPath
|
|
props.onClose = closeModal
|
|
|
|
const component = route.matched[0]?.components?.default
|
|
|
|
if (!component) {
|
|
modalRoute.value = undefined
|
|
return
|
|
}
|
|
modalRoute.value = h(component, props)
|
|
},
|
|
{immediate: true},
|
|
)
|
|
|
|
async function closeModal() {
|
|
await router.isReady()
|
|
if (backdropRoute.value !== undefined) {
|
|
// TODO: Dialog modals might want to replace the route here via router.replace()
|
|
return router.push(backdropRoute.value)
|
|
}
|
|
if (window.history.state === undefined) {
|
|
return router.push({ name: 'home' })
|
|
} else {
|
|
router.back()
|
|
throw new Error('')
|
|
}
|
|
}
|
|
|
|
return {
|
|
baseRoute,
|
|
modalRoute,
|
|
closeModal,
|
|
}
|
|
} |