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
15 changed files with 152 additions and 284 deletions
Showing only changes of commit 5a0c0eff9f - Show all commits

View File

@ -20,8 +20,9 @@
<quick-actions/> <quick-actions/>
<router-view/> <router-view :route="routeWithModal"/>
<!-- TODO: is this still used? -->
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).
<router-view name="popup" v-slot="{ Component }"> <router-view name="popup" v-slot="{ Component }">
<transition name="modal"> <transition name="modal">
<component :is="Component" /> <component :is="Component" />
@ -50,6 +51,24 @@ import {CURRENT_LIST, KEYBOARD_SHORTCUTS_ACTIVE, MENU_ACTIVE} from '@/store/muta
import Navigation from '@/components/home/navigation.vue' import Navigation from '@/components/home/navigation.vue'
import QuickActions from '@/components/quick-actions/quick-actions.vue' import QuickActions from '@/components/quick-actions/quick-actions.vue'
function useRouteWithModal() {
const router = useRouter()
const route = useRoute()
const historyState = computed(() => route.fullPath && window.history.state)
const routeWithModal = computed(() => {
if (historyState.value.backgroundView) {
return router.resolve(historyState.value.backgroundView)
} else {
return route
}
})
return { routeWithModal }
}
useRouteWithModal()
const store = useStore() const store = useStore()
const background = computed(() => store.state.background) const background = computed(() => store.state.background)

View File

@ -29,9 +29,10 @@
<script> <script>
import Filters from '@/components/list/partials/filters' import Filters from '@/components/list/partials/filters'
import {getDefaultParams} from '@/components/tasks/mixins/taskList'
import Popup from '@/components/misc/popup' import Popup from '@/components/misc/popup'
import {getDefaultParams} from '@/composables/taskList'
export default { export default {
name: 'filter-popup', name: 'filter-popup',
components: { components: {

View File

@ -6,7 +6,9 @@
<message> <message>
{{ {{
s.available($route) ? $t('keyboardShortcuts.currentPageOnly') : $t('keyboardShortcuts.allPages') s?.available($route)
? $t('keyboardShortcuts.currentPageOnly')
: $t('keyboardShortcuts.allPages')
}} }}
</message> </message>
@ -17,7 +19,8 @@
class="shortcut-keys" class="shortcut-keys"
is="dd" is="dd"
:keys="sc.keys" :keys="sc.keys"
:combination="typeof sc.combination !== 'undefined' ? $t(`keyboardShortcuts.${sc.combination}`) : null"/> :combination="typeof sc.combination !== 'undefined' ? $t(`keyboardShortcuts.${sc.combination}`) : null"
/>
</template> </template>
</dl> </dl>
</template> </template>
@ -25,28 +28,17 @@
</modal> </modal>
</template> </template>
<script> <script lang="ts" setup>
import {KEYBOARD_SHORTCUTS_ACTIVE} from '@/store/mutation-types' import {store} from '@/store'
import Shortcut from '@/components/misc/shortcut.vue'
import Message from '@/components/misc/message'
import {KEYBOARD_SHORTCUTS} from './shortcuts'
export default { import Shortcut from '@/components/misc/shortcut.vue'
name: 'keyboard-shortcuts', import Message from '@/components/misc/message.vue'
components: {
Message, import {KEYBOARD_SHORTCUTS_ACTIVE} from '@/store/mutation-types'
Shortcut, import {KEYBOARD_SHORTCUTS as shortcuts} from './shortcuts'
},
data() { function close() {
return { store.commit(KEYBOARD_SHORTCUTS_ACTIVE, false)
shortcuts: KEYBOARD_SHORTCUTS,
}
},
methods: {
close() {
this.$store.commit(KEYBOARD_SHORTCUTS_ACTIVE, false)
},
},
} }
</script> </script>

View File

@ -5,7 +5,6 @@ const ctrl = isAppleDevice() ? '⌘' : 'ctrl'
export const KEYBOARD_SHORTCUTS = [ export const KEYBOARD_SHORTCUTS = [
{ {
title: 'keyboardShortcuts.general', title: 'keyboardShortcuts.general',
available: () => null,
shortcuts: [ shortcuts: [
{ {
title: 'keyboardShortcuts.toggleMenu', title: 'keyboardShortcuts.toggleMenu',
@ -55,13 +54,7 @@ export const KEYBOARD_SHORTCUTS = [
}, },
{ {
title: 'keyboardShortcuts.task.title', title: 'keyboardShortcuts.task.title',
available: (route) => [ available: (route) => route.name === 'task.detail',
'task.detail',
'task.list.detail',
'task.gantt.detail',
'task.kanban.detail',
'task.detail',
].includes(route.name),
shortcuts: [ shortcuts: [
{ {
title: 'keyboardShortcuts.task.assign', title: 'keyboardShortcuts.task.assign',

View File

@ -67,7 +67,7 @@
<router-link <router-link
class="mt-2 has-text-centered is-block" class="mt-2 has-text-centered is-block"
:to="{name: 'task.detail', params: {id: taskEditTask.id}}" :to="taskDetailRoute"
> >
{{ $t('task.openDetail') }} {{ $t('task.openDetail') }}
</router-link> </router-link>
@ -102,6 +102,15 @@ export default {
taskEditTask: TaskModel, taskEditTask: TaskModel,
} }
}, },
computed: {
taskDetailRoute() {
return {
name: 'task.detail',
params: { id: this.taskEditTask.id },
state: { backgroundView: this.$router.currentRoute.value.fullPath },
dpschen marked this conversation as resolved Outdated

I'm not sure about the naming of this because there's views with a background. Maybe that can get confusing?

I'm not sure about the naming of this because there's views with a background. Maybe that can get confusing?

That's true. Do you have a suggestion?

Maybe something like backdropView in the sense of https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter

That's true. Do you have a suggestion? Maybe something like backdropView in the sense of https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter

I like that idea.

I like that idea.

Done

Done
}
},
},
components: { components: {
ColorPicker, ColorPicker,
Reminders, Reminders,

View File

@ -7,8 +7,8 @@
'has-light-text': !colorIsDark(task.hexColor) && task.hexColor !== `#${task.defaultColor}` && task.hexColor !== task.defaultColor, 'has-light-text': !colorIsDark(task.hexColor) && task.hexColor !== `#${task.defaultColor}` && task.hexColor !== task.defaultColor,
}" }"
:style="{'background-color': task.hexColor !== '#' && task.hexColor !== `#${task.defaultColor}` ? task.hexColor : false}" :style="{'background-color': task.hexColor !== '#' && task.hexColor !== `#${task.defaultColor}` ? task.hexColor : false}"
@click.ctrl="() => toggleTaskDone(task)"
@click.exact="() => $router.push({ name: 'task.kanban.detail', params: { id: task.id } })" @click.exact="() => $router.push({ name: 'task.kanban.detail', params: { id: task.id } })"
@click.ctrl="() => toggleTaskDone(task)"
@click.meta="() => toggleTaskDone(task)" @click.meta="() => toggleTaskDone(task)"
> >
<span class="task-id"> <span class="task-id">
@ -112,6 +112,13 @@ export default {
this.loadingInternal = false this.loadingInternal = false
} }
}, },
openTaskDetail() {
this.$router.push({
name: 'task.detail',
params: { id: this.task.id },
state: { backgroundView: this.$router.currentRoute.value.fullPath },
})
},
}, },
} }
</script> </script>

View File

@ -8,7 +8,7 @@
> >
</span> </span>
<router-link <router-link
:to="{ name: taskDetailRoute, params: { id: task.id } }" :to="taskDetailRoute"
:class="{ 'done': task.done}" :class="{ 'done': task.done}"
class="tasktext"> class="tasktext">
<span> <span>
@ -126,10 +126,6 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
taskDetailRoute: {
type: String,
default: 'task.list.detail',
},
showList: { showList: {
type: Boolean, type: Boolean,
default: false, default: false,
@ -167,6 +163,13 @@ export default {
title: '', title: '',
} : this.$store.state.currentList } : this.$store.state.currentList
}, },
taskDetailRoute() {
return {
name: 'task.detail',
params: { id: this.task.id },
state: { backgroundView: this.$router.currentRoute.value.fullPath },
}
},
}, },
methods: { methods: {
async markAsDone(checked) { async markAsDone(checked) {

View File

@ -13,7 +13,6 @@ import DataExportDownload from '../views/user/DataExportDownload'
// Tasks // Tasks
import ShowTasksInRangeComponent from '../views/tasks/ShowTasksInRange' import ShowTasksInRangeComponent from '../views/tasks/ShowTasksInRange'
import LinkShareAuthComponent from '../views/sharing/LinkSharingAuth' import LinkShareAuthComponent from '../views/sharing/LinkSharingAuth'
import TaskDetailViewModal from '../views/tasks/TaskDetailViewModal'
import TaskDetailView from '../views/tasks/TaskDetailView' import TaskDetailView from '../views/tasks/TaskDetailView'
import ListNamespaces from '../views/namespaces/ListNamespaces' import ListNamespaces from '../views/namespaces/ListNamespaces'
// Team Handling // Team Handling
@ -315,204 +314,21 @@ const router = createRouter({
path: '/lists/:listId/list', path: '/lists/:listId/list',
name: 'list.list', name: 'list.list',
component: List, component: List,
children: [
{
path: '/tasks/:id',
name: 'task.list.detail',
component: TaskDetailViewModal,
},
{
path: '/lists/:listId/settings/edit',
name: 'list.list.settings.edit',
component: ListSettingEdit,
},
{
path: '/lists/:listId/settings/background',
name: 'list.list.settings.background',
component: ListSettingBackground,
},
{
path: '/lists/:listId/settings/duplicate',
name: 'list.list.settings.duplicate',
component: ListSettingDuplicate,
},
{
path: '/lists/:listId/settings/share',
name: 'list.list.settings.share',
component: ListSettingShare,
},
{
path: '/lists/:listId/settings/delete',
name: 'list.list.settings.delete',
component: ListSettingDelete,
},
{
path: '/lists/:listId/settings/archive',
name: 'list.list.settings.archive',
component: ListSettingArchive,
},
{
path: '/lists/:listId/settings/edit',
name: 'filter.list.settings.edit',
component: FilterEdit,
},
{
path: '/lists/:listId/settings/delete',
name: 'filter.list.settings.delete',
component: FilterDelete,
},
],
}, },
{ {
path: '/lists/:listId/gantt', path: '/lists/:listId/gantt',
name: 'list.gantt', name: 'list.gantt',
component: Gantt, component: Gantt,
children: [
{
path: '/tasks/:id',
name: 'task.gantt.detail',
component: TaskDetailViewModal,
},
{
path: '/lists/:listId/settings/edit',
name: 'list.gantt.settings.edit',
component: ListSettingEdit,
},
{
path: '/lists/:listId/settings/background',
name: 'list.gantt.settings.background',
component: ListSettingBackground,
},
{
path: '/lists/:listId/settings/duplicate',
name: 'list.gantt.settings.duplicate',
component: ListSettingDuplicate,
},
{
path: '/lists/:listId/settings/share',
name: 'list.gantt.settings.share',
component: ListSettingShare,
},
{
path: '/lists/:listId/settings/delete',
name: 'list.gantt.settings.delete',
component: ListSettingDelete,
},
{
path: '/lists/:listId/settings/archive',
name: 'list.gantt.settings.archive',
component: ListSettingArchive,
},
{
path: '/lists/:listId/settings/edit',
name: 'filter.gantt.settings.edit',
component: FilterEdit,
},
{
path: '/lists/:listId/settings/delete',
name: 'filter.gantt.settings.delete',
component: FilterDelete,
},
],
}, },
{ {
path: '/lists/:listId/table', path: '/lists/:listId/table',
name: 'list.table', name: 'list.table',
component: Table, component: Table,
children: [
{
path: '/lists/:listId/settings/edit',
name: 'list.table.settings.edit',
component: ListSettingEdit,
},
{
path: '/lists/:listId/settings/background',
name: 'list.table.settings.background',
component: ListSettingBackground,
},
{
path: '/lists/:listId/settings/duplicate',
name: 'list.table.settings.duplicate',
component: ListSettingDuplicate,
},
{
path: '/lists/:listId/settings/share',
name: 'list.table.settings.share',
component: ListSettingShare,
},
{
path: '/lists/:listId/settings/delete',
name: 'list.table.settings.delete',
component: ListSettingDelete,
},
{
path: '/lists/:listId/settings/archive',
name: 'list.table.settings.archive',
component: ListSettingArchive,
},
{
path: '/lists/:listId/settings/edit',
name: 'filter.table.settings.edit',
component: FilterEdit,
},
{
path: '/lists/:listId/settings/delete',
name: 'filter.table.settings.delete',
component: FilterDelete,
},
],
}, },
{ {
path: '/lists/:listId/kanban', path: '/lists/:listId/kanban',
name: 'list.kanban', name: 'list.kanban',
component: Kanban, component: Kanban,
children: [
{
path: '/tasks/:id',
name: 'task.kanban.detail',
component: TaskDetailViewModal,
},
{
path: '/lists/:listId/settings/edit',
name: 'list.kanban.settings.edit',
component: ListSettingEdit,
},
{
path: '/lists/:listId/settings/background',
name: 'list.kanban.settings.background',
component: ListSettingBackground,
},
{
path: '/lists/:listId/settings/duplicate',
name: 'list.kanban.settings.duplicate',
component: ListSettingDuplicate,
},
{
path: '/lists/:listId/settings/share',
name: 'list.kanban.settings.share',
component: ListSettingShare,
},
{
path: '/lists/:listId/settings/delete',
name: 'list.kanban.settings.delete',
component: ListSettingDelete,
},
{
path: '/lists/:listId/settings/archive',
name: 'list.kanban.settings.archive',
component: ListSettingArchive,
},
{
path: '/lists/:listId/settings/edit',
name: 'filter.kanban.settings.edit',
component: FilterEdit,
},
{
path: '/lists/:listId/settings/delete',
name: 'filter.kanban.settings.delete',
component: FilterDelete,
},
],
}, },
], ],
}, },

View File

@ -51,6 +51,10 @@
</div> </div>
</div> </div>
<ShowTasks class="mt-4" :show-all="true" v-if="hasLists" :key="showTasksKey"/> <ShowTasks class="mt-4" :show-all="true" v-if="hasLists" :key="showTasksKey"/>
<transition name="modal">
<task-detail-view-modal v-if="showTaskDetail" />
</transition>
</div> </div>
</template> </template>
@ -67,6 +71,9 @@ import {getHistory} from '@/modules/listHistory'
import {parseDateOrNull} from '@/helpers/parseDateOrNull' import {parseDateOrNull} from '@/helpers/parseDateOrNull'
import {formatDateShort, formatDateSince} from '@/helpers/time/formatDate' import {formatDateShort, formatDateSince} from '@/helpers/time/formatDate'
import {useDateTimeSalutation} from '@/composables/useDateTimeSalutation' import {useDateTimeSalutation} from '@/composables/useDateTimeSalutation'
import TaskDetailViewModal, { useShowModal } from '@/views/tasks/TaskDetailViewModal.vue'
const showTaskDetail = useShowModal()
const welcome = useDateTimeSalutation() const welcome = useDateTimeSalutation()

View File

@ -8,28 +8,28 @@
<router-link <router-link
v-shortcut="'g l'" v-shortcut="'g l'"
:title="$t('keyboardShortcuts.list.switchToListView')" :title="$t('keyboardShortcuts.list.switchToListView')"
:class="{'is-active': $route.name.includes('list.list')}" :class="{'is-active': currentListType === 'list'}"
:to="{ name: 'list.list', params: { listId: listId } }"> :to="{ name: 'list.list', params: { listId: listId } }">
{{ $t('list.list.title') }} {{ $t('list.list.title') }}
</router-link> </router-link>
<router-link <router-link
v-shortcut="'g g'" v-shortcut="'g g'"
:title="$t('keyboardShortcuts.list.switchToGanttView')" :title="$t('keyboardShortcuts.list.switchToGanttView')"
:class="{'is-active': $route.name.includes('list.gantt')}" :class="{'is-active': currentListType === 'gantt'}"
:to="{ name: 'list.gantt', params: { listId: listId } }"> :to="{ name: 'list.gantt', params: { listId: listId } }">
{{ $t('list.gantt.title') }} {{ $t('list.gantt.title') }}
</router-link> </router-link>
<router-link <router-link
v-shortcut="'g t'" v-shortcut="'g t'"
:title="$t('keyboardShortcuts.list.switchToTableView')" :title="$t('keyboardShortcuts.list.switchToTableView')"
:class="{'is-active': $route.name.includes('list.table')}" :class="{'is-active': currentListType === 'table'}"
:to="{ name: 'list.table', params: { listId: listId } }"> :to="{ name: 'list.table', params: { listId: listId } }">
{{ $t('list.table.title') }} {{ $t('list.table.title') }}
</router-link> </router-link>
<router-link <router-link
v-shortcut="'g k'" v-shortcut="'g k'"
:title="$t('keyboardShortcuts.list.switchToKanbanView')" :title="$t('keyboardShortcuts.list.switchToKanbanView')"
:class="{'is-active': $route.name.includes('list.kanban')}" :class="{'is-active': currentListType === 'kanban'}"
:to="{ name: 'list.kanban', params: { listId: listId } }"> :to="{ name: 'list.kanban', params: { listId: listId } }">
{{ $t('list.kanban.title') }} {{ $t('list.kanban.title') }}
</router-link> </router-link>
@ -69,6 +69,11 @@ export default {
}, },
}, },
computed: { computed: {
currentListType() {
// default: 'list',
return ''
},
// Computed property to let "listId" always have a value // Computed property to let "listId" always have a value
listId() { listId() {
return typeof this.$route.params.listId === 'undefined' ? 0 : this.$route.params.listId return typeof this.$route.params.listId === 'undefined' ? 0 : this.$route.params.listId
@ -113,11 +118,11 @@ export default {
this.$store.commit('kanban/setListId', 0) this.$store.commit('kanban/setListId', 0)
} }
// When clicking again on a list in the menu, there would be no list view selected which means no list // // When clicking again on a list in the menu, there would be no list view selected which means no list
// at all. Users will then have to click on the list view menu again which is quite confusing. // // at all. Users will then have to click on the list view menu again which is quite confusing.
if (this.$route.name === 'list.index') { // if (this.$route.name === 'list.index') {
return this.replaceListView() // return this.replaceListView()
} // }
// Don't load the list if we either already loaded it or aren't dealing with a list at all currently and // Don't load the list if we either already loaded it or aren't dealing with a list at all currently and
// the currently loaded list has the right set. // the currently loaded list has the right set.

View File

@ -52,13 +52,9 @@
:show-taskswithout-dates="showTaskswithoutDates" :show-taskswithout-dates="showTaskswithoutDates"
/> />
<!-- This router view is used to show the task popup while keeping the gantt chart itself --> <transition name="modal">
<router-view v-slot="{ Component }"> <task-detail-view-modal v-if="showTaskDetail" />
<transition name="modal"> </transition>
<component :is="Component" />
</transition>
</router-view>
</card> </card>
</div> </div>
</template> </template>
@ -69,12 +65,20 @@ import flatPickr from 'vue-flatpickr-component'
import Fancycheckbox from '../../../components/input/fancycheckbox' import Fancycheckbox from '../../../components/input/fancycheckbox'
import {saveListView} from '@/helpers/saveListView' import {saveListView} from '@/helpers/saveListView'
import TaskDetailViewModal, { useShowModal } from '@/views/tasks/TaskDetailViewModal.vue'
export default { export default {
name: 'Gantt', name: 'Gantt',
components: { components: {
Fancycheckbox, Fancycheckbox,
flatPickr, flatPickr,
GanttChart, GanttChart,
TaskDetailViewModal,
},
setup() {
return {
showTaskDetail: useShowModal(),
}
}, },
created() { created() {
// Save the current list view to local storage // Save the current list view to local storage

View File

@ -204,18 +204,12 @@
</div> </div>
</div> </div>
<!-- This router view is used to show the task popup while keeping the kanban board itself -->
<router-view v-slot="{ Component }">
<transition name="modal">
<component :is="Component"/>
</transition>
</router-view>
<transition name="modal"> <transition name="modal">
<task-detail-view-modal v-if="showTaskDetail" />
<modal <modal
v-else-if="showBucketDeleteModal"
@close="showBucketDeleteModal = false" @close="showBucketDeleteModal = false"
@submit="deleteBucket()" @submit="deleteBucket()"
v-if="showBucketDeleteModal"
> >
<template #header><span>{{ $t('list.kanban.deleteHeaderBucket') }}</span></template> <template #header><span>{{ $t('list.kanban.deleteHeaderBucket') }}</span></template>
@ -242,6 +236,7 @@ import Dropdown from '@/components/misc/dropdown.vue'
import {getCollapsedBucketState, saveCollapsedBucketState} from '@/helpers/saveCollapsedBucketState' import {getCollapsedBucketState, saveCollapsedBucketState} from '@/helpers/saveCollapsedBucketState'
import {calculateItemPosition} from '../../../helpers/calculateItemPosition' import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
import KanbanCard from '@/components/tasks/partials/kanban-card' import KanbanCard from '@/components/tasks/partials/kanban-card'
import TaskDetailViewModal, { useShowModal } from '@/views/tasks/TaskDetailViewModal.vue'
const DRAG_OPTIONS = { const DRAG_OPTIONS = {
// sortable options // sortable options
@ -261,6 +256,7 @@ export default {
Dropdown, Dropdown,
FilterPopup, FilterPopup,
draggable, draggable,
TaskDetailViewModal,
}, },
data() { data() {
return { return {
@ -296,6 +292,13 @@ export default {
}, },
} }
}, },
setup() {
return {
showTaskDetail: useShowModal(),
}
},
created() { created() {
// Save the current list view to local storage // Save the current list view to local storage
// We use local storage and not vuex here to make it persistent across reloads. // We use local storage and not vuex here to make it persistent across reloads.

View File

@ -90,7 +90,6 @@
:disabled="!canWrite" :disabled="!canWrite"
:the-task="t" :the-task="t"
@taskUpdated="updateTasks" @taskUpdated="updateTasks"
task-detail-route="task.detail"
> >
<template v-if="canWrite"> <template v-if="canWrite">
<span class="icon handle"> <span class="icon handle">
@ -124,12 +123,9 @@
/> />
</card> </card>
<!-- This router view is used to show the task popup while keeping the kanban board itself --> <transition name="modal">
<router-view v-slot="{ Component }"> <task-detail-view-modal v-if="showTaskDetail" />
<transition name="modal"> </transition>
<component :is="Component"/>
</transition>
</router-view>
</div> </div>
</template> </template>
@ -147,8 +143,8 @@ import FilterPopup from '@/components/list/partials/filter-popup.vue'
import {HAS_TASKS} from '@/store/mutation-types' import {HAS_TASKS} from '@/store/mutation-types'
import Nothing from '@/components/misc/nothing.vue' import Nothing from '@/components/misc/nothing.vue'
import Pagination from '@/components/misc/pagination.vue' import Pagination from '@/components/misc/pagination.vue'
import Popup from '@/components/misc/popup' import {ALPHABETICAL_SORT} from '@/components/list/partials/filters.vue'
import { ALPHABETICAL_SORT } from '@/components/list/partials/filters' import TaskDetailViewModal, { useShowModal } from '@/views/tasks/TaskDetailViewModal.vue'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import {calculateItemPosition} from '../../../helpers/calculateItemPosition' import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
@ -192,7 +188,6 @@ export default {
taskList, taskList,
], ],
components: { components: {
Popup,
Nothing, Nothing,
FilterPopup, FilterPopup,
SingleTaskInList, SingleTaskInList,
@ -200,7 +195,15 @@ export default {
AddTask, AddTask,
draggable, draggable,
Pagination, Pagination,
TaskDetailViewModal,
}, },
setup() {
return {
showTaskDetail: useShowModal(),
}
},
created() { created() {
// Save the current list view to local storage // Save the current list view to local storage
// We use local storage and not vuex here to make it persistent across reloads. // We use local storage and not vuex here to make it persistent across reloads.

View File

@ -120,7 +120,7 @@
<tbody> <tbody>
<tr :key="t.id" v-for="t in tasks"> <tr :key="t.id" v-for="t in tasks">
<td v-if="activeColumns.id"> <td v-if="activeColumns.id">
<router-link :to="{name: 'task.detail', params: { id: t.id }}"> <router-link :to="taskDetailRoutes[t.id]">
<template v-if="t.identifier === ''"> <template v-if="t.identifier === ''">
#{{ t.index }} #{{ t.index }}
</template> </template>
@ -133,7 +133,7 @@
<Done :is-done="t.done" variant="small" /> <Done :is-done="t.done" variant="small" />
</td> </td>
<td v-if="activeColumns.title"> <td v-if="activeColumns.title">
<router-link :to="{name: 'task.detail', params: { id: t.id }}">{{ t.title }}</router-link> <router-link :to="taskDetailRoutes[t.id]">{{ t.title }}</router-link>
</td> </td>
<td v-if="activeColumns.priority"> <td v-if="activeColumns.priority">
<priority-label :priority="t.priority" :done="t.done" :show-all="true"/> <priority-label :priority="t.priority" :done="t.done" :show-all="true"/>
@ -185,6 +185,8 @@
</template> </template>
<script> <script>
import {useRoute} from 'vue-router'
import taskList from '@/components/tasks/mixins/taskList' import taskList from '@/components/tasks/mixins/taskList'
import Done from '@/components/misc/Done.vue' import Done from '@/components/misc/Done.vue'
import User from '@/components/misc/user' import User from '@/components/misc/user'
@ -237,6 +239,19 @@ export default {
}, },
} }
}, },
computed: {
taskDetailRoutes() {
const taskDetailRoutes = {}
this.tasks.forEach(({id}) => {
taskDetailRoutes[id] = {
name: 'task.detail',
params: { id },
state: { backgroundView: this.$router.currentRoute.value.fullPath },
}
})
return taskDetailRoutes
},
},
created() { created() {
const savedShowColumns = localStorage.getItem('tableViewColumns') const savedShowColumns = localStorage.getItem('tableViewColumns')
if (savedShowColumns !== null) { if (savedShowColumns !== null) {
@ -253,9 +268,13 @@ export default {
this.initTasks(1) this.initTasks(1)
},
setup() {
// Save the current list view to local storage // Save the current list view to local storage
// We use local storage and not vuex here to make it persistent across reloads. // We use local storage and not vuex here to make it persistent across reloads.
saveListView(this.$route.params.listId, this.$route.name) const route = useRoute()
console.log(route.value)
saveListView(route.value.params.listId, route.value.name)
}, },
methods: { methods: {
initTasks(page, search = '') { initTasks(page, search = '') {

View File

@ -13,37 +13,24 @@
<script> <script>
import TaskDetailView from './TaskDetailView' import TaskDetailView from './TaskDetailView'
import {computed} from 'vue'
import {useRoute} from 'vue-router'
export function useShowModal() {
const route = useRoute()
const historyState = computed(() => route.fullPath && window.history.state)
const show = computed(() => historyState.value.backgroundView)
return show
}
export default { export default {
name: 'TaskDetailViewModal', name: 'TaskDetailViewModal',
components: { components: {
TaskDetailView, TaskDetailView,
}, },
data() {
return {
lastRoute: null,
}
},
beforeRouteEnter(to, from, next) {
next(vm => {
vm.lastRoute = from
})
},
beforeRouteLeave(to, from, next) {
if (from.name === 'task.kanban.detail' && to.name === 'task.detail') {
this.$router.replace({name: 'task.kanban.detail', params: to.params})
return
}
next()
},
methods: { methods: {
close() { close() {
if (this.lastRoute === null) { this.$router.back()
this.$router.back()
} else {
this.$router.push(this.lastRoute)
}
}, },
}, },
} }