2023-04-01 19:47:31 +00:00
|
|
|
<template>
|
2023-04-02 17:51:44 +00:00
|
|
|
<li
|
|
|
|
class="list-menu loader-container is-loading-small"
|
|
|
|
:class="{'is-loading': isLoading}"
|
|
|
|
>
|
2023-04-10 18:48:19 +00:00
|
|
|
<div>
|
2023-04-02 17:51:44 +00:00
|
|
|
<BaseButton
|
2023-04-03 09:18:10 +00:00
|
|
|
v-if="canCollapse && childProjects?.length > 0"
|
2023-04-02 17:51:44 +00:00
|
|
|
class="collapse-project-button"
|
2024-02-07 11:18:19 +00:00
|
|
|
@click="childProjectsOpen = !childProjectsOpen"
|
2023-04-02 17:51:44 +00:00
|
|
|
>
|
2024-02-07 11:18:19 +00:00
|
|
|
<icon
|
|
|
|
icon="chevron-down"
|
|
|
|
:class="{ 'project-is-collapsed': !childProjectsOpen }"
|
|
|
|
/>
|
2023-04-02 17:51:44 +00:00
|
|
|
</BaseButton>
|
|
|
|
<BaseButton
|
|
|
|
:to="{ name: 'project.index', params: { projectId: project.id} }"
|
|
|
|
class="list-menu-link"
|
2023-04-14 15:41:11 +00:00
|
|
|
:class="{'router-link-exact-active': currentProject?.id === project.id}"
|
2023-04-02 17:51:44 +00:00
|
|
|
>
|
2023-04-14 15:06:20 +00:00
|
|
|
<span
|
|
|
|
v-if="!canCollapse || childProjects?.length === 0"
|
|
|
|
class="collapse-project-button-placeholder"
|
2024-02-07 11:18:19 +00:00
|
|
|
/>
|
|
|
|
<div
|
|
|
|
class="color-bubble-handle-wrapper"
|
|
|
|
:class="{'is-draggable': project.id > 0}"
|
|
|
|
>
|
2023-04-14 15:06:20 +00:00
|
|
|
<ColorBubble
|
|
|
|
v-if="project.hexColor !== ''"
|
|
|
|
:color="project.hexColor"
|
|
|
|
/>
|
2024-02-07 11:18:19 +00:00
|
|
|
<span
|
|
|
|
v-else-if="project.id < -1"
|
|
|
|
class="saved-filter-icon icon menu-item-icon"
|
|
|
|
>
|
|
|
|
<icon icon="filter" />
|
2023-06-07 18:58:43 +00:00
|
|
|
</span>
|
2023-04-14 15:06:20 +00:00
|
|
|
<span
|
2023-06-07 18:58:43 +00:00
|
|
|
v-if="project.id > 0"
|
2024-02-06 17:29:17 +00:00
|
|
|
class="icon menu-item-icon handle"
|
2023-04-14 15:06:20 +00:00
|
|
|
:class="{'has-color-bubble': project.hexColor !== ''}"
|
|
|
|
>
|
2024-02-07 11:18:19 +00:00
|
|
|
<icon icon="grip-lines" />
|
2023-04-14 15:06:20 +00:00
|
|
|
</span>
|
|
|
|
</div>
|
2023-06-06 15:33:55 +00:00
|
|
|
<span class="project-menu-title">{{ getProjectTitle(project) }}</span>
|
2023-04-02 17:51:44 +00:00
|
|
|
</BaseButton>
|
|
|
|
<BaseButton
|
|
|
|
v-if="project.id > 0"
|
|
|
|
class="favorite"
|
|
|
|
:class="{'is-favorite': project.isFavorite}"
|
|
|
|
@click="projectStore.toggleProjectFavorite(project)"
|
|
|
|
>
|
2024-02-07 11:18:19 +00:00
|
|
|
<icon :icon="project.isFavorite ? 'star' : ['far', 'star']" />
|
2023-04-02 17:51:44 +00:00
|
|
|
</BaseButton>
|
2023-04-14 16:18:03 +00:00
|
|
|
<ProjectSettingsDropdown
|
|
|
|
class="menu-list-dropdown"
|
|
|
|
:project="project"
|
|
|
|
>
|
2023-04-02 17:51:44 +00:00
|
|
|
<template #trigger="{toggleOpen}">
|
2024-02-07 11:18:19 +00:00
|
|
|
<BaseButton
|
|
|
|
class="menu-list-dropdown-trigger"
|
|
|
|
@click="toggleOpen"
|
|
|
|
>
|
|
|
|
<icon
|
|
|
|
icon="ellipsis-h"
|
|
|
|
class="icon"
|
|
|
|
/>
|
2023-04-02 17:51:44 +00:00
|
|
|
</BaseButton>
|
|
|
|
</template>
|
|
|
|
</ProjectSettingsDropdown>
|
2023-04-10 18:48:19 +00:00
|
|
|
</div>
|
2023-04-02 17:51:44 +00:00
|
|
|
<ProjectsNavigation
|
2024-02-09 13:26:42 +00:00
|
|
|
v-if="childProjectsOpen && canCollapse"
|
2023-04-12 08:49:15 +00:00
|
|
|
:model-value="childProjects"
|
2023-04-02 17:51:44 +00:00
|
|
|
:can-edit-order="true"
|
2023-04-03 09:29:11 +00:00
|
|
|
:can-collapse="canCollapse"
|
2023-04-02 17:51:44 +00:00
|
|
|
/>
|
|
|
|
</li>
|
2023-04-01 19:47:31 +00:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2024-02-12 21:22:55 +00:00
|
|
|
import {computed} from 'vue'
|
2023-04-01 19:47:31 +00:00
|
|
|
import {useProjectStore} from '@/stores/projects'
|
|
|
|
import {useBaseStore} from '@/stores/base'
|
2024-02-12 21:22:55 +00:00
|
|
|
import {useStorage} from '@vueuse/core'
|
2023-04-01 19:47:31 +00:00
|
|
|
|
|
|
|
import type {IProject} from '@/modelTypes/IProject'
|
|
|
|
|
|
|
|
import BaseButton from '@/components/base/BaseButton.vue'
|
|
|
|
import ProjectSettingsDropdown from '@/components/project/project-settings-dropdown.vue'
|
|
|
|
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
|
|
|
import ColorBubble from '@/components/misc/colorBubble.vue'
|
2023-04-02 17:51:44 +00:00
|
|
|
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
|
2023-04-01 19:47:31 +00:00
|
|
|
|
2023-06-20 12:39:59 +00:00
|
|
|
const {
|
|
|
|
project,
|
|
|
|
isLoading,
|
|
|
|
canCollapse,
|
|
|
|
} = defineProps<{
|
2023-04-01 19:47:31 +00:00
|
|
|
project: IProject,
|
2023-04-02 17:51:44 +00:00
|
|
|
isLoading?: boolean,
|
2023-04-03 09:18:10 +00:00
|
|
|
canCollapse?: boolean,
|
2023-06-20 12:39:59 +00:00
|
|
|
}>()
|
2023-04-01 19:47:31 +00:00
|
|
|
|
|
|
|
const projectStore = useProjectStore()
|
|
|
|
const baseStore = useBaseStore()
|
|
|
|
const currentProject = computed(() => baseStore.currentProject)
|
2023-04-02 17:51:44 +00:00
|
|
|
|
2024-02-12 21:22:55 +00:00
|
|
|
// Persist open state across browser reloads. Using a seperate ref for the state
|
|
|
|
// allows us to use only one entry in local storage instead of one for every project id.
|
|
|
|
type openState = { [key: number]: boolean }
|
|
|
|
const childProjectsOpenState = useStorage<openState>('navigation-child-projects-open', {})
|
|
|
|
const childProjectsOpen = computed({
|
|
|
|
get() {
|
|
|
|
return childProjectsOpenState.value[project.id] ?? true
|
|
|
|
},
|
|
|
|
set(open) {
|
|
|
|
childProjectsOpenState.value[project.id] = open
|
|
|
|
},
|
|
|
|
})
|
2023-04-02 17:51:44 +00:00
|
|
|
|
|
|
|
const childProjects = computed(() => {
|
2023-06-20 12:39:59 +00:00
|
|
|
return projectStore.getChildProjects(project.id)
|
2023-06-06 15:29:08 +00:00
|
|
|
.filter(p => !p.isArchived)
|
2023-04-02 17:51:44 +00:00
|
|
|
.sort((a, b) => a.position - b.position)
|
|
|
|
})
|
2023-04-01 19:47:31 +00:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.list-setting-spacer {
|
|
|
|
width: 5rem;
|
|
|
|
flex-shrink: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
.project-is-collapsed {
|
|
|
|
transform: rotate(-90deg);
|
|
|
|
}
|
|
|
|
|
|
|
|
.favorite {
|
|
|
|
transition: opacity $transition, color $transition;
|
|
|
|
opacity: 0;
|
|
|
|
|
|
|
|
&:hover,
|
|
|
|
&.is-favorite {
|
|
|
|
opacity: 1;
|
|
|
|
color: var(--warning);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 18:48:19 +00:00
|
|
|
.list-menu:hover > div > .favorite {
|
2023-04-01 19:47:31 +00:00
|
|
|
opacity: 1;
|
|
|
|
}
|
2023-04-14 15:06:20 +00:00
|
|
|
|
2023-06-07 18:58:43 +00:00
|
|
|
.list-menu:hover > div > a > .color-bubble-handle-wrapper.is-draggable > {
|
|
|
|
.saved-filter-icon,
|
|
|
|
.color-bubble {
|
|
|
|
opacity: 0;
|
|
|
|
}
|
2023-04-14 15:06:20 +00:00
|
|
|
}
|
|
|
|
|
2023-09-07 14:36:11 +00:00
|
|
|
.is-touch .color-bubble {
|
|
|
|
opacity: 1 !important;
|
|
|
|
}
|
|
|
|
|
2023-04-14 15:06:20 +00:00
|
|
|
.color-bubble-handle-wrapper {
|
|
|
|
position: relative;
|
|
|
|
width: 1rem;
|
|
|
|
height: 1rem;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: flex-start;
|
|
|
|
margin-right: .25rem;
|
2023-06-06 15:32:18 +00:00
|
|
|
flex-shrink: 0;
|
2023-04-14 15:06:20 +00:00
|
|
|
|
|
|
|
.color-bubble, .icon {
|
|
|
|
transition: all $transition;
|
|
|
|
position: absolute;
|
|
|
|
width: 12px;
|
|
|
|
margin: 0 !important;
|
|
|
|
padding: 0 !important;
|
|
|
|
}
|
|
|
|
}
|
2023-06-06 15:33:55 +00:00
|
|
|
|
|
|
|
.project-menu-title {
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
}
|
2023-06-07 18:58:43 +00:00
|
|
|
|
|
|
|
.saved-filter-icon {
|
|
|
|
color: var(--grey-300) !important;
|
|
|
|
font-size: .75rem;
|
|
|
|
}
|
2023-09-07 14:36:11 +00:00
|
|
|
|
|
|
|
.is-touch .handle.has-color-bubble {
|
|
|
|
display: none !important;
|
|
|
|
}
|
2023-04-01 19:47:31 +00:00
|
|
|
</style>
|