This repository has been archived on 2024-02-08. You can view files and clone it, but cannot push or open issues or pull requests.
frontend/src/composables/useRouteWithModal.ts

113 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.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,
}
}