feat: implement modals with vue router 4
This is an implementation of the modals with the new possibilities of vue router 3. See: https://github.com/vuejs/vue-router/issues/703#issuecomment-865066913 for a better explanation and the linked example implementation: https://github.com/vuejs/vue-router-next/blob/master/e2e/modal/index.ts
This commit is contained in:
parent
29d8422e94
commit
5a0c0eff9f
15 changed files with 152 additions and 284 deletions
|
@ -20,8 +20,9 @@
|
||||||
|
|
||||||
<quick-actions/>
|
<quick-actions/>
|
||||||
|
|
||||||
<router-view/>
|
<router-view :route="routeWithModal"/>
|
||||||
|
|
||||||
|
<!-- TODO: is this still used? -->
|
||||||
<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)
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
ColorPicker,
|
ColorPicker,
|
||||||
Reminders,
|
Reminders,
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 = '') {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue