feat: implement modals with vue router 4 #816

Merged
konrad merged 62 commits from dpschen/frontend:feature/vue3-modals-with-router-4 into main 2022-02-05 16:49:04 +00:00
5 changed files with 32 additions and 30 deletions
Showing only changes of commit de626eab31 - Show all commits

View File

@ -1,8 +1,12 @@
<template>
<div>
<a @click="$store.commit('menuActive', false)" class="menu-hide-button" v-if="menuActive">
<BaseButton
v-if="menuActive"
@click="$store.commit('menuActive', false)"
class="menu-hide-button"
>
<icon icon="times" />
</a>
</BaseButton>
<div
:class="{'has-background': background}"
:style="{'background-image': background && `url(${background})`}"
@ -16,7 +20,11 @@
]"
class="app-content"
>
<a @click="$store.commit('menuActive', false)" class="mobile-overlay" v-if="menuActive"></a>
<BaseButton
v-if="menuActive"
@click="$store.commit('menuActive', false)"
dpschen marked this conversation as resolved Outdated

I don't think it is, we should make sure the modal views keep their transition though. Might make sense to include that in the modal component itself?

I don't think it is, we should make sure the modal views keep their transition though. Might make sense to include that in the modal component itself?

Either that or put the transition inside something like a provider component. In that we could use the new teleport component. I was always using portal-vue in vue 2 for this kind of stuff.

Either that or put the transition inside something like a provider component. In that we could use the [new teleport component](https://v3.vuejs.org/api/built-in-components.html#teleport). I was always using [portal-vue](https://github.com/LinusBorg/portal-vue) in vue 2 for this kind of stuff.

I think using the teleport component allows for a cleaner solution since there are situations where you want a transition handled by the route and others where you want to have it handled by the outer component (like delete modals).

I think using the teleport component allows for a cleaner solution since there are situations where you want a transition handled by the route and others where you want to have it handled by the outer component (like delete modals).
class="mobile-overlay"
/>
<quick-actions/>
@ -28,7 +36,9 @@
</router-view>
<transition name="modal">
<component v-if="currentModal" :is="currentModal" />
<TaskDetailViewModal v-if="currentModal" >
dpschen marked this conversation as resolved Outdated

I suppose this is used because we don't have a modal wrapper? Using "TaskDetailView" in the general content with component feels a little out of place IMHO 😅

I suppose this is used because we don't have a modal wrapper? Using "TaskDetailView" in the general content with component feels a little out of place IMHO 😅

See it like the router-view conponent of vue router: it's also not a view itself. regardless I just wanted to make the most simple change. I guess it should be renamed to something like modal-view and maybe even merged with modal 🤔 - but that can also happen later.

See it like the router-view conponent of vue router: it's also not a view itself. regardless I just wanted to make the most simple change. I guess it should be renamed to something like modal-view and maybe even merged with modal 🤔 - but that can also happen later.

I removed the component task-detail-view-modal in 6827390b77 and was able to merge it with the modal itself.

As a result this is obsolete now =)

I removed the component `task-detail-view-modal` in https://kolaente.dev/vikunja/frontend/commit/6827390b77ae6e186e7b0163651c19ca9a247d2f and was able to merge it with the modal itself. As a result this is obsolete now =)
<component :is="currentModal" />
</TaskDetailViewModal>
</transition>
<a
@ -52,25 +62,23 @@ import {useEventListener} from '@vueuse/core'
import {CURRENT_LIST, KEYBOARD_SHORTCUTS_ACTIVE, MENU_ACTIVE} from '@/store/mutation-types'
import Navigation from '@/components/home/navigation.vue'
import QuickActions from '@/components/quick-actions/quick-actions.vue'
import TaskDetailViewModal from '@/views/tasks/TaskDetailViewModal.vue'
import BaseButton from '@/components/base/BaseButton.vue'
function useRouteWithModal() {
const router = useRouter()
const route = useRoute()
const historyState = computed(() => route.fullPath && window.history.state)
const backdropView = computed(() => route.fullPath && window.history.state.backdropView)
const routeWithModal = computed(() => {
if (historyState.value.backdropView) {
return router.resolve(historyState.value.backdropView)
} else {
return route
}
return backdropView.value
? router.resolve(backdropView.value)
: route
})
const currentModal = shallowRef<VNode>()
watchEffect(() => {
const hasBackdropView = historyState.value.backdropView
if (!hasBackdropView) {
if (!backdropView.value) {
currentModal.value = undefined
return
}

View File

@ -170,7 +170,8 @@ export default {
return {
name: 'task.detail',
params: { id: this.task.id },
state: { backdropView: this.$router.currentRoute.value.fullPath },
// TODO: re-enable opening task detail in modal
// state: { backdropView: this.$router.currentRoute.value.fullPath },
}
},
},

View File

@ -16,7 +16,7 @@ import DataExportDownload from '../views/user/DataExportDownload.vue'
import ShowTasksInRangeComponent from '../views/tasks/ShowTasksInRange.vue'
import LinkShareAuthComponent from '../views/sharing/LinkSharingAuth.vue'
import ListNamespaces from '../views/namespaces/ListNamespaces.vue'
import TaskDetailViewModal from '../views/tasks/TaskDetailViewModal.vue'
import TaskDetailView from '../views/tasks/TaskDetailView.vue'
// Team Handling
import ListTeamsComponent from '../views/teams/ListTeams.vue'
// Label Handling
@ -242,7 +242,7 @@ const router = createRouter({
{
path: '/tasks/:id',
name: 'task.detail',
component: TaskDetailViewModal,
component: TaskDetailView,
props: route => ({ taskId: parseInt(route.params.id as string) }),
},
{

View File

@ -181,7 +181,6 @@
<script setup lang="ts">
import { toRef, computed, Ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStorage } from '@vueuse/core'
@ -273,14 +272,15 @@ function sort(property : keyof SortBy) {
}
}
const router = useRouter()
// TODO: re-enable opening task detail in modal
// const router = useRouter()
const taskDetailRoutes = computed(() => Object.fromEntries(
tasks.value.map(({id}) => ([
id,
{
name: 'task.detail',
params: { id },
state: { backdropView: router.currentRoute.value.fullPath },
// state: { backdropView: router.currentRoute.value.fullPath },
},
])),
))

View File

@ -4,10 +4,10 @@
variant="scrolling"
class="task-detail-view-modal"
>
<a @click="close()" class="close">
<BaseButton @click="close()" class="close">
<icon icon="times"/>
</a>
<task-detail-view :task-id="props.taskId"/>
</BaseButton>
<slot />
</modal>
</template>
@ -15,14 +15,7 @@
import {computed} from 'vue'
import {useRouter, useRoute} from 'vue-router'
import TaskDetailView from './TaskDetailView.vue'
const props = defineProps({
taskId: {
type: Number,
required: true,
},
})
import BaseButton from '@/components/base/BaseButton.vue'
const route = useRoute()
const historyState = computed(() => route.fullPath && window.history.state)