WIP: feat: route modals everywhere #2735
|
@ -1,5 +1,5 @@
|
|||
import { computed, shallowRef, watch, h, type VNode, ref } from 'vue'
|
||||
import { useRoute, useRouter, loadRouteLocation, type RouteLocationNormalizedLoaded, type RouteLocationRaw } from 'vue-router'
|
||||
import {computed, shallowRef, watch, h, type VNode, ref} from 'vue'
|
||||
import {useRoute, useRouter, loadRouteLocation, type RouteLocationNormalizedLoaded, type RouteLocationRaw, START_LOCATION, type RouteLocationNormalized} from 'vue-router'
|
||||
import router from '@/router'
|
||||
|
||||
// this is adapted from vue-router
|
||||
|
@ -15,8 +15,8 @@ function getRouteProps(route: RouteLocationNormalizedLoaded) {
|
|||
: {}
|
||||
}
|
||||
|
||||
function resolveAndLoadRoute(route: RouteLocationRaw) {
|
||||
return loadRouteLocation(router.resolve(route))
|
||||
function resolveAndLoadRoute(route: RouteLocationRaw, currentLocation?: RouteLocationNormalizedLoaded) {
|
||||
return loadRouteLocation(router.resolve(route, currentLocation))
|
||||
}
|
||||
|
||||
export function useRouteWithModal() {
|
||||
|
@ -27,9 +27,39 @@ export function useRouteWithModal() {
|
|||
// every time the fullPath changes we check the history state
|
||||
// this happens also initially
|
||||
return route.fullPath
|
||||
? window.history.state?.backdropRoutePath
|
||||
? history.state?.backdropRoutePath
|
||||
: undefined
|
||||
})
|
||||
|
||||
router.afterEach((to, from) => {
|
||||
const resolvedRoute = router.resolve(to)
|
||||
// if (!resolvedRoute.meta.forceModal) {
|
||||
if (!resolvedRoute.meta.modal) {
|
||||
return
|
||||
}
|
||||
|
||||
let routeNormalized: RouteLocationNormalized
|
||||
if (typeof resolvedRoute.meta?.modal === 'boolean' || resolvedRoute.meta.modal?.force !== true) {
|
||||
// TODO: also check route history here
|
||||
routeNormalized = from
|
||||
} else if (resolvedRoute.meta.modal?.defaultBackdropRoute) {
|
||||
const {defaultBackdropRoute} = resolvedRoute.meta.modal
|
||||
const routeRaw = typeof defaultBackdropRoute === 'function'
|
||||
|
||||
? defaultBackdropRoute(resolvedRoute)
|
||||
: defaultBackdropRoute
|
||||
routeNormalized = router.resolve(routeRaw)
|
||||
} else {
|
||||
// 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
|
||||
routeNormalized = router.resolve({ name: 'home' })
|
||||
}
|
||||
|
||||
history.replaceState({
|
||||
...history.state,
|
||||
foobar: routeNormalized.fullPath,
|
||||
}, '')
|
||||
})
|
||||
|
||||
const routerIsReady = ref(false)
|
||||
router.isReady().then(() => {
|
||||
|
@ -48,23 +78,34 @@ export function useRouteWithModal() {
|
|||
}
|
||||
|
||||
if (historyStateBackdropRoutePath.value === undefined) {
|
||||
if (route.meta?.showAsModal !== true) {
|
||||
if (typeof route.meta?.modal === 'boolean' || route.meta.modal?.force !== true) {
|
||||
hasModal.value = false
|
||||
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
|
||||
// the route forces to be shown as a modal
|
||||
hasModal.value = true
|
||||
baseRoute.value = await resolveAndLoadRoute({ name: 'home' })
|
||||
let routeRaw: RouteLocationRaw
|
||||
if (route.meta.modal?.defaultBackdropRoute) {
|
||||
const {defaultBackdropRoute} = route.meta.modal
|
||||
routeRaw = typeof defaultBackdropRoute === 'function'
|
||||
? defaultBackdropRoute(route)
|
||||
: defaultBackdropRoute
|
||||
} else {
|
||||
// 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
|
||||
routeRaw = { name: 'home' }
|
||||
}
|
||||
|
||||
baseRoute.value = await resolveAndLoadRoute(routeRaw, baseRoute.value)
|
||||
return
|
||||
}
|
||||
|
||||
// we get the resolved route from the fullpath
|
||||
// and wait for the route component to be loaded before we assign it
|
||||
hasModal.value = true
|
||||
baseRoute.value = await resolveAndLoadRoute(historyStateBackdropRoutePath.value)
|
||||
baseRoute.value = await resolveAndLoadRoute(historyStateBackdropRoutePath.value, baseRoute.value)
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
@ -103,7 +144,7 @@ export function useRouteWithModal() {
|
|||
// TODO: Dialog modals might want to replace the route here via router.replace()
|
||||
return router.push(backdropRoute.value)
|
||||
}
|
||||
if (window.history.state === undefined) {
|
||||
if (history.state === undefined) {
|
||||
return router.push({ name: 'home' })
|
||||
} else {
|
||||
router.back()
|
||||
|
@ -115,6 +156,5 @@ export function useRouteWithModal() {
|
|||
return {
|
||||
baseRoute,
|
||||
modalRoute,
|
||||
closeModal,
|
||||
}
|
||||
}
|
|
@ -83,7 +83,12 @@ const NewTeamComponent = () => import('@/views/teams/NewTeam.vue')
|
|||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
title?: string
|
||||
showAsModal?: boolean
|
||||
modal?: boolean | {
|
||||
/** Forces the view to be always shown as modal */
|
||||
force?: boolean,
|
||||
/** Show this route as backdrop if there is no route history */
|
||||
defaultBackdropRoute?: RouteLocationRaw | ((currentRoute: RouteLocation) => RouteLocationRaw),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,7 +225,10 @@ const router = createRouter({
|
|||
name: 'namespace.create',
|
||||
component: NewNamespaceComponent,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: {
|
||||
force: true,
|
||||
defaultBackdropRoute: {name: 'namespaces.index'},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -228,7 +236,7 @@ const router = createRouter({
|
|||
name: 'namespace.settings.edit',
|
||||
component: NamespaceSettingEdit,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
props: route => ({ namespaceId: Number(route.params.id as string) }),
|
||||
},
|
||||
|
@ -237,7 +245,7 @@ const router = createRouter({
|
|||
name: 'namespace.settings.share',
|
||||
component: NamespaceSettingShare,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -245,7 +253,7 @@ const router = createRouter({
|
|||
name: 'namespace.settings.archive',
|
||||
component: NamespaceSettingArchive,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
props: route => ({ namespaceId: Number(route.params.id as string) }),
|
||||
},
|
||||
|
@ -254,7 +262,7 @@ const router = createRouter({
|
|||
name: 'namespace.settings.delete',
|
||||
component: NamespaceSettingDelete,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
props: route => ({ namespaceId: Number(route.params.id as string) }),
|
||||
},
|
||||
|
@ -263,6 +271,12 @@ const router = createRouter({
|
|||
name: 'task.detail',
|
||||
component: TaskDetailView,
|
||||
props: route => ({ taskId: Number(route.params.id as string) }),
|
||||
meta: {
|
||||
modal: {
|
||||
force: true,
|
||||
defaultBackdropRoute: {name: 'home'},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/tasks/by/upcoming',
|
||||
|
@ -280,7 +294,7 @@ const router = createRouter({
|
|||
name: 'list.create',
|
||||
component: NewListComponent,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -289,7 +303,7 @@ const router = createRouter({
|
|||
component: ListSettingEdit,
|
||||
props: route => ({ listId: Number(route.params.listId as string) }),
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -297,7 +311,7 @@ const router = createRouter({
|
|||
name: 'list.settings.background',
|
||||
component: ListSettingBackground,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -305,7 +319,7 @@ const router = createRouter({
|
|||
name: 'list.settings.duplicate',
|
||||
component: ListSettingDuplicate,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -313,7 +327,7 @@ const router = createRouter({
|
|||
name: 'list.settings.share',
|
||||
component: ListSettingShare,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -321,7 +335,7 @@ const router = createRouter({
|
|||
name: 'list.settings.delete',
|
||||
component: ListSettingDelete,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -329,7 +343,7 @@ const router = createRouter({
|
|||
name: 'list.settings.archive',
|
||||
component: ListSettingArchive,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -337,7 +351,7 @@ const router = createRouter({
|
|||
name: 'filter.settings.edit',
|
||||
component: FilterEdit,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
props: route => ({ listId: Number(route.params.listId as string) }),
|
||||
},
|
||||
|
@ -346,7 +360,7 @@ const router = createRouter({
|
|||
name: 'filter.settings.delete',
|
||||
component: FilterDelete,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
props: route => ({ listId: Number(route.params.listId as string) }),
|
||||
},
|
||||
|
@ -355,7 +369,7 @@ const router = createRouter({
|
|||
name: 'list.info',
|
||||
component: ListInfo,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
props: route => ({ listId: Number(route.params.listId as string) }),
|
||||
},
|
||||
|
@ -422,7 +436,7 @@ const router = createRouter({
|
|||
name: 'teams.create',
|
||||
component: NewTeamComponent,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -440,7 +454,7 @@ const router = createRouter({
|
|||
name: 'labels.create',
|
||||
component: NewLabelComponent,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -462,7 +476,7 @@ const router = createRouter({
|
|||
name: 'filters.create',
|
||||
component: FilterNew,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -475,7 +489,7 @@ const router = createRouter({
|
|||
name: 'about',
|
||||
component: About,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
modal: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
Reference in New Issue
Something was broken here: since we checked for
historyState.value
in theif
condition it could never have a value in theelse
cause.