This repository has been archived on 2024-02-08. You can view files and clone it, but cannot push or open issues or pull requests.
frontend/src/components/home/ProjectsNavigation.vue

110 lines
3.2 KiB
Vue
Raw Normal View History

<template>
<draggable
v-model="availableProjects"
animation="100"
2024-02-07 11:18:19 +00:00
ghost-class="ghost"
2023-03-27 11:34:32 +00:00
group="projects"
handle=".handle"
2023-04-01 20:02:08 +00:00
tag="menu"
item-key="id"
2023-03-28 13:33:34 +00:00
:disabled="!canEditOrder"
filter=".drag-disabled"
:component-data="{
type: 'transition-group',
name: !drag ? 'flip-list' : null,
class: [
'menu-list can-be-hidden',
2023-03-28 13:33:34 +00:00
{ 'dragging-disabled': !canEditOrder }
],
}"
2024-02-07 11:18:19 +00:00
@start="() => drag = true"
@end="saveProjectPosition"
>
2023-04-01 19:31:03 +00:00
<template #item="{element: project}">
<ProjectsNavigationItem
:class="{'drag-disabled': project.id < 0}"
:project="project"
:is-loading="projectUpdating[project.id]"
:can-collapse="canCollapse"
2023-04-03 11:53:28 +00:00
:level="level"
2023-04-10 18:49:39 +00:00
:data-project-id="project.id"
/>
</template>
</draggable>
</template>
<script lang="ts" setup>
import {ref, watch} from 'vue'
import draggable from 'zhyswan-vuedraggable'
import type {SortableEvent} from 'sortablejs'
import ProjectsNavigationItem from '@/components/home/ProjectsNavigationItem.vue'
import {calculateItemPosition} from '@/helpers/calculateItemPosition'
import type {IProject} from '@/modelTypes/IProject'
import {useProjectStore} from '@/stores/projects'
const props = defineProps<{
modelValue?: IProject[],
2023-03-28 13:33:34 +00:00
canEditOrder: boolean,
canCollapse?: boolean,
2023-04-03 11:53:28 +00:00
level?: number,
}>()
2023-04-10 18:43:35 +00:00
const emit = defineEmits<{
(e: 'update:modelValue', projects: IProject[]): void
}>()
const drag = ref(false)
const projectStore = useProjectStore()
// Vue draggable will modify the projects list as it changes their position which will not work on a prop.
// Hence, we'll clone the prop and work on the clone.
const availableProjects = ref<IProject[]>([])
watch(
() => props.modelValue,
projects => {
availableProjects.value = projects || []
},
{immediate: true},
)
const projectUpdating = ref<{ [id: IProject['id']]: boolean }>({})
async function saveProjectPosition(e: SortableEvent) {
if (!e.newIndex && e.newIndex !== 0) return
const projectsActive = availableProjects.value
// If the project was dragged to the last position, Safari will report e.newIndex as the size of the projectsActive
// array instead of using the position. Because the index is wrong in that case, dragging the project will fail.
// To work around that we're explicitly checking that case here and decrease the index.
const newIndex = e.newIndex === projectsActive.length ? e.newIndex - 1 : e.newIndex
const projectId = parseInt(e.item.dataset.projectId)
const project = projectStore.projects[projectId]
const parentProjectId = e.to.parentNode.dataset.projectId ? parseInt(e.to.parentNode.dataset.projectId) : 0
const projectBefore = projectsActive[newIndex - 1] ?? null
const projectAfter = projectsActive[newIndex + 1] ?? null
projectUpdating.value[project.id] = true
const position = calculateItemPosition(
projectBefore !== null ? projectBefore.position : null,
projectAfter !== null ? projectAfter.position : null,
)
try {
// create a copy of the project in order to not violate pinia manipulation
await projectStore.updateProject({
...project,
position,
parentProjectId,
})
emit('update:modelValue', availableProjects.value)
} finally {
projectUpdating.value[project.id] = false
}
}
</script>