feat: implement modals with vue router 4 #816
|
@ -20,8 +20,9 @@
|
|||
|
||||
<quick-actions/>
|
||||
|
||||
<router-view/>
|
||||
<router-view :route="routeWithModal"/>
|
||||
|
||||
<!-- TODO: is this still used? -->
|
||||
dpschen marked this conversation as resolved
Outdated
|
||||
<router-view name="popup" v-slot="{ Component }">
|
||||
<transition name="modal">
|
||||
<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 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 background = computed(() => store.state.background)
|
||||
|
|
|
@ -29,9 +29,10 @@
|
|||
|
||||
<script>
|
||||
import Filters from '@/components/list/partials/filters'
|
||||
import {getDefaultParams} from '@/components/tasks/mixins/taskList'
|
||||
import Popup from '@/components/misc/popup'
|
||||
|
||||
import {getDefaultParams} from '@/composables/taskList'
|
||||
|
||||
export default {
|
||||
name: 'filter-popup',
|
||||
components: {
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
<message>
|
||||
{{
|
||||
s.available($route) ? $t('keyboardShortcuts.currentPageOnly') : $t('keyboardShortcuts.allPages')
|
||||
s?.available($route)
|
||||
? $t('keyboardShortcuts.currentPageOnly')
|
||||
: $t('keyboardShortcuts.allPages')
|
||||
}}
|
||||
</message>
|
||||
|
||||
|
@ -17,7 +19,8 @@
|
|||
class="shortcut-keys"
|
||||
is="dd"
|
||||
:keys="sc.keys"
|
||||
:combination="typeof sc.combination !== 'undefined' ? $t(`keyboardShortcuts.${sc.combination}`) : null"/>
|
||||
:combination="typeof sc.combination !== 'undefined' ? $t(`keyboardShortcuts.${sc.combination}`) : null"
|
||||
/>
|
||||
</template>
|
||||
</dl>
|
||||
</template>
|
||||
|
@ -25,28 +28,17 @@
|
|||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {KEYBOARD_SHORTCUTS_ACTIVE} from '@/store/mutation-types'
|
||||
import Shortcut from '@/components/misc/shortcut.vue'
|
||||
import Message from '@/components/misc/message'
|
||||
import {KEYBOARD_SHORTCUTS} from './shortcuts'
|
||||
<script lang="ts" setup>
|
||||
import {store} from '@/store'
|
||||
|
||||
export default {
|
||||
name: 'keyboard-shortcuts',
|
||||
components: {
|
||||
Message,
|
||||
Shortcut,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
shortcuts: KEYBOARD_SHORTCUTS,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$store.commit(KEYBOARD_SHORTCUTS_ACTIVE, false)
|
||||
},
|
||||
},
|
||||
import Shortcut from '@/components/misc/shortcut.vue'
|
||||
import Message from '@/components/misc/message.vue'
|
||||
|
||||
import {KEYBOARD_SHORTCUTS_ACTIVE} from '@/store/mutation-types'
|
||||
import {KEYBOARD_SHORTCUTS as shortcuts} from './shortcuts'
|
||||
|
||||
function close() {
|
||||
store.commit(KEYBOARD_SHORTCUTS_ACTIVE, false)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ const ctrl = isAppleDevice() ? '⌘' : 'ctrl'
|
|||
export const KEYBOARD_SHORTCUTS = [
|
||||
{
|
||||
title: 'keyboardShortcuts.general',
|
||||
available: () => null,
|
||||
shortcuts: [
|
||||
{
|
||||
title: 'keyboardShortcuts.toggleMenu',
|
||||
|
@ -55,13 +54,7 @@ export const KEYBOARD_SHORTCUTS = [
|
|||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.task.title',
|
||||
available: (route) => [
|
||||
'task.detail',
|
||||
'task.list.detail',
|
||||
'task.gantt.detail',
|
||||
'task.kanban.detail',
|
||||
'task.detail',
|
||||
].includes(route.name),
|
||||
available: (route) => route.name === 'task.detail',
|
||||
shortcuts: [
|
||||
{
|
||||
title: 'keyboardShortcuts.task.assign',
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
<router-link
|
||||
class="mt-2 has-text-centered is-block"
|
||||
:to="{name: 'task.detail', params: {id: taskEditTask.id}}"
|
||||
:to="taskDetailRoute"
|
||||
>
|
||||
{{ $t('task.openDetail') }}
|
||||
</router-link>
|
||||
|
@ -102,6 +102,15 @@ export default {
|
|||
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
konrad
commented
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?
dpschen
commented
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
konrad
commented
I like that idea. I like that idea.
dpschen
commented
Done Done
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
ColorPicker,
|
||||
Reminders,
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
'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}"
|
||||
@click.ctrl="() => toggleTaskDone(task)"
|
||||
@click.exact="() => $router.push({ name: 'task.kanban.detail', params: { id: task.id } })"
|
||||
@click.ctrl="() => toggleTaskDone(task)"
|
||||
@click.meta="() => toggleTaskDone(task)"
|
||||
>
|
||||
<span class="task-id">
|
||||
|
@ -112,6 +112,13 @@ export default {
|
|||
this.loadingInternal = false
|
||||
}
|
||||
},
|
||||
openTaskDetail() {
|
||||
this.$router.push({
|
||||
name: 'task.detail',
|
||||
params: { id: this.task.id },
|
||||
state: { backgroundView: this.$router.currentRoute.value.fullPath },
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
</span>
|
||||
<router-link
|
||||
:to="{ name: taskDetailRoute, params: { id: task.id } }"
|
||||
:to="taskDetailRoute"
|
||||
:class="{ 'done': task.done}"
|
||||
class="tasktext">
|
||||
<span>
|
||||
|
@ -126,10 +126,6 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
taskDetailRoute: {
|
||||
type: String,
|
||||
default: 'task.list.detail',
|
||||
},
|
||||
showList: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
@ -167,6 +163,13 @@ export default {
|
|||
title: '',
|
||||
} : this.$store.state.currentList
|
||||
},
|
||||
taskDetailRoute() {
|
||||
return {
|
||||
name: 'task.detail',
|
||||
params: { id: this.task.id },
|
||||
state: { backgroundView: this.$router.currentRoute.value.fullPath },
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async markAsDone(checked) {
|
||||
|
|
|
@ -13,7 +13,6 @@ import DataExportDownload from '../views/user/DataExportDownload'
|
|||
// Tasks
|
||||
import ShowTasksInRangeComponent from '../views/tasks/ShowTasksInRange'
|
||||
import LinkShareAuthComponent from '../views/sharing/LinkSharingAuth'
|
||||
import TaskDetailViewModal from '../views/tasks/TaskDetailViewModal'
|
||||
import TaskDetailView from '../views/tasks/TaskDetailView'
|
||||
import ListNamespaces from '../views/namespaces/ListNamespaces'
|
||||
// Team Handling
|
||||
|
@ -315,204 +314,21 @@ const router = createRouter({
|
|||
path: '/lists/:listId/list',
|
||||
name: 'list.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',
|
||||
name: 'list.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',
|
||||
name: 'list.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',
|
||||
name: 'list.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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -51,6 +51,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<ShowTasks class="mt-4" :show-all="true" v-if="hasLists" :key="showTasksKey"/>
|
||||
|
||||
<transition name="modal">
|
||||
<task-detail-view-modal v-if="showTaskDetail" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -67,6 +71,9 @@ import {getHistory} from '@/modules/listHistory'
|
|||
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
||||
import {formatDateShort, formatDateSince} from '@/helpers/time/formatDate'
|
||||
import {useDateTimeSalutation} from '@/composables/useDateTimeSalutation'
|
||||
import TaskDetailViewModal, { useShowModal } from '@/views/tasks/TaskDetailViewModal.vue'
|
||||
|
||||
const showTaskDetail = useShowModal()
|
||||
|
||||
const welcome = useDateTimeSalutation()
|
||||
|
||||
|
|
|
@ -8,28 +8,28 @@
|
|||
<router-link
|
||||
v-shortcut="'g l'"
|
||||
: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 } }">
|
||||
{{ $t('list.list.title') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
v-shortcut="'g g'"
|
||||
: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 } }">
|
||||
{{ $t('list.gantt.title') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
v-shortcut="'g t'"
|
||||
: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 } }">
|
||||
{{ $t('list.table.title') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
v-shortcut="'g k'"
|
||||
: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 } }">
|
||||
{{ $t('list.kanban.title') }}
|
||||
</router-link>
|
||||
|
@ -69,6 +69,11 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
currentListType() {
|
||||
// default: 'list',
|
||||
return ''
|
||||
},
|
||||
|
||||
// Computed property to let "listId" always have a value
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (this.$route.name === 'list.index') {
|
||||
return this.replaceListView()
|
||||
}
|
||||
// // 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.
|
||||
// if (this.$route.name === 'list.index') {
|
||||
// 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
|
||||
// the currently loaded list has the right set.
|
||||
|
|
|
@ -52,13 +52,9 @@
|
|||
:show-taskswithout-dates="showTaskswithoutDates"
|
||||
/>
|
||||
|
||||
<!-- This router view is used to show the task popup while keeping the gantt chart itself -->
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="modal">
|
||||
<component :is="Component" />
|
||||
<task-detail-view-modal v-if="showTaskDetail" />
|
||||
</transition>
|
||||
</router-view>
|
||||
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -69,12 +65,20 @@ import flatPickr from 'vue-flatpickr-component'
|
|||
import Fancycheckbox from '../../../components/input/fancycheckbox'
|
||||
import {saveListView} from '@/helpers/saveListView'
|
||||
|
||||
import TaskDetailViewModal, { useShowModal } from '@/views/tasks/TaskDetailViewModal.vue'
|
||||
|
||||
export default {
|
||||
name: 'Gantt',
|
||||
components: {
|
||||
Fancycheckbox,
|
||||
flatPickr,
|
||||
GanttChart,
|
||||
TaskDetailViewModal,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
showTaskDetail: useShowModal(),
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Save the current list view to local storage
|
||||
|
|
|
@ -204,18 +204,12 @@
|
|||
</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">
|
||||
<task-detail-view-modal v-if="showTaskDetail" />
|
||||
<modal
|
||||
v-else-if="showBucketDeleteModal"
|
||||
@close="showBucketDeleteModal = false"
|
||||
@submit="deleteBucket()"
|
||||
v-if="showBucketDeleteModal"
|
||||
>
|
||||
<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 {calculateItemPosition} from '../../../helpers/calculateItemPosition'
|
||||
import KanbanCard from '@/components/tasks/partials/kanban-card'
|
||||
import TaskDetailViewModal, { useShowModal } from '@/views/tasks/TaskDetailViewModal.vue'
|
||||
|
||||
const DRAG_OPTIONS = {
|
||||
// sortable options
|
||||
|
@ -261,6 +256,7 @@ export default {
|
|||
Dropdown,
|
||||
FilterPopup,
|
||||
draggable,
|
||||
TaskDetailViewModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -296,6 +292,13 @@ export default {
|
|||
},
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
showTaskDetail: useShowModal(),
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
// Save the current list view to local storage
|
||||
// We use local storage and not vuex here to make it persistent across reloads.
|
||||
|
|
|
@ -90,7 +90,6 @@
|
|||
:disabled="!canWrite"
|
||||
:the-task="t"
|
||||
@taskUpdated="updateTasks"
|
||||
task-detail-route="task.detail"
|
||||
>
|
||||
<template v-if="canWrite">
|
||||
<span class="icon handle">
|
||||
|
@ -124,12 +123,9 @@
|
|||
/>
|
||||
</card>
|
||||
|
||||
<!-- 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"/>
|
||||
<task-detail-view-modal v-if="showTaskDetail" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -147,8 +143,8 @@ import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
|||
import {HAS_TASKS} from '@/store/mutation-types'
|
||||
import Nothing from '@/components/misc/nothing.vue'
|
||||
import Pagination from '@/components/misc/pagination.vue'
|
||||
import Popup from '@/components/misc/popup'
|
||||
import { ALPHABETICAL_SORT } from '@/components/list/partials/filters'
|
||||
import {ALPHABETICAL_SORT} from '@/components/list/partials/filters.vue'
|
||||
import TaskDetailViewModal, { useShowModal } from '@/views/tasks/TaskDetailViewModal.vue'
|
||||
|
||||
import draggable from 'vuedraggable'
|
||||
import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
|
||||
|
@ -192,7 +188,6 @@ export default {
|
|||
taskList,
|
||||
],
|
||||
components: {
|
||||
Popup,
|
||||
Nothing,
|
||||
FilterPopup,
|
||||
SingleTaskInList,
|
||||
|
@ -200,7 +195,15 @@ export default {
|
|||
AddTask,
|
||||
draggable,
|
||||
Pagination,
|
||||
TaskDetailViewModal,
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
showTaskDetail: useShowModal(),
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
// Save the current list view to local storage
|
||||
// We use local storage and not vuex here to make it persistent across reloads.
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
<tbody>
|
||||
<tr :key="t.id" v-for="t in tasks">
|
||||
<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 === ''">
|
||||
#{{ t.index }}
|
||||
</template>
|
||||
|
@ -133,7 +133,7 @@
|
|||
<Done :is-done="t.done" variant="small" />
|
||||
</td>
|
||||
<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 v-if="activeColumns.priority">
|
||||
<priority-label :priority="t.priority" :done="t.done" :show-all="true"/>
|
||||
|
@ -185,6 +185,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {useRoute} from 'vue-router'
|
||||
|
||||
import taskList from '@/components/tasks/mixins/taskList'
|
||||
import Done from '@/components/misc/Done.vue'
|
||||
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() {
|
||||
const savedShowColumns = localStorage.getItem('tableViewColumns')
|
||||
if (savedShowColumns !== null) {
|
||||
|
@ -253,9 +268,13 @@ export default {
|
|||
|
||||
this.initTasks(1)
|
||||
|
||||
},
|
||||
setup() {
|
||||
// Save the current list view to local storage
|
||||
// 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: {
|
||||
initTasks(page, search = '') {
|
||||
|
|
|
@ -13,37 +13,24 @@
|
|||
|
||||
<script>
|
||||
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 {
|
||||
name: 'TaskDetailViewModal',
|
||||
components: {
|
||||
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: {
|
||||
close() {
|
||||
if (this.lastRoute === null) {
|
||||
this.$router.back()
|
||||
} else {
|
||||
this.$router.push(this.lastRoute)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
Reference in New Issue
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.
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).