185
src/components/FolderSelectionTreeNode.vue
Normal file
185
src/components/FolderSelectionTreeNode.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useCollectionsStore } from '@MailManager/stores/collectionsStore'
|
||||
import type { CollectionObject } from '@MailManager/models/collection'
|
||||
import type { ServiceObject } from '@MailManager/models'
|
||||
|
||||
interface Props {
|
||||
folder: CollectionObject
|
||||
service: ServiceObject
|
||||
selectedFolderKey: string | null
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const collectionsStore = useCollectionsStore()
|
||||
|
||||
const emit = defineEmits<{
|
||||
select: [folder: CollectionObject]
|
||||
}>()
|
||||
|
||||
const expanded = ref(false)
|
||||
|
||||
const folderKeyFor = (folder: CollectionObject): string => {
|
||||
return `${folder.provider}:${String(folder.service)}:${String(folder.identifier)}`
|
||||
}
|
||||
|
||||
const folderLabelFor = (folder: CollectionObject): string => {
|
||||
return folder.properties.label || String(folder.identifier)
|
||||
}
|
||||
|
||||
const folderIconFor = (folder: CollectionObject): string => {
|
||||
switch (folder.properties.role) {
|
||||
case 'inbox':
|
||||
return 'mdi-inbox'
|
||||
case 'sent':
|
||||
return 'mdi-send'
|
||||
case 'drafts':
|
||||
return 'mdi-file-document'
|
||||
case 'trash':
|
||||
return 'mdi-delete'
|
||||
case 'junk':
|
||||
return 'mdi-alert-octagon'
|
||||
case 'archive':
|
||||
return 'mdi-archive'
|
||||
case 'outbox':
|
||||
return 'mdi-tray-arrow-up'
|
||||
default:
|
||||
return 'mdi-folder'
|
||||
}
|
||||
}
|
||||
|
||||
const folderColorFor = (folder: CollectionObject): string | undefined => {
|
||||
switch (folder.properties.role) {
|
||||
case 'inbox':
|
||||
return 'primary'
|
||||
case 'sent':
|
||||
return 'success'
|
||||
case 'drafts':
|
||||
return 'warning'
|
||||
case 'trash':
|
||||
return 'error'
|
||||
case 'junk':
|
||||
return 'orange'
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
const key = computed(() => folderKeyFor(props.folder))
|
||||
|
||||
const childFolders = computed(() => {
|
||||
const serviceIdentifier = props.service.identifier
|
||||
|
||||
if (serviceIdentifier === null) {
|
||||
return []
|
||||
}
|
||||
|
||||
return collectionsStore.collectionsInCollection(props.service.provider, serviceIdentifier, props.folder.identifier)
|
||||
})
|
||||
|
||||
const hasChildren = computed(() => {
|
||||
const serviceIdentifier = props.service.identifier
|
||||
|
||||
if (serviceIdentifier === null) {
|
||||
return false
|
||||
}
|
||||
|
||||
return collectionsStore.hasChildrenInCollection(props.service.provider, serviceIdentifier, props.folder.identifier)
|
||||
})
|
||||
const isSelected = computed(() => props.selectedFolderKey === key.value)
|
||||
|
||||
const onSelect = () => {
|
||||
emit('select', props.folder)
|
||||
}
|
||||
|
||||
const toggleExpanded = () => {
|
||||
expanded.value = !expanded.value
|
||||
}
|
||||
|
||||
const onGroupDoubleClick = () => {
|
||||
toggleExpanded()
|
||||
onSelect()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="hasChildren"
|
||||
class="folder-node-group"
|
||||
>
|
||||
<v-list-item
|
||||
class="folder-node"
|
||||
:title="folderLabelFor(folder)"
|
||||
:active="isSelected"
|
||||
@click="onSelect"
|
||||
@dblclick.stop="onGroupDoubleClick"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
:icon="folderIconFor(folder)"
|
||||
:color="folderColorFor(folder)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #append>
|
||||
<v-btn
|
||||
variant="text"
|
||||
density="compact"
|
||||
size="x-small"
|
||||
:icon="expanded ? 'mdi-chevron-down' : 'mdi-chevron-right'"
|
||||
@click.stop="toggleExpanded"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<div
|
||||
v-if="expanded"
|
||||
class="folder-node-children"
|
||||
>
|
||||
<FolderSelectionTreeNode
|
||||
v-for="childFolder in childFolders"
|
||||
:key="folderKeyFor(childFolder)"
|
||||
:folder="childFolder"
|
||||
:service="service"
|
||||
:selected-folder-key="selectedFolderKey"
|
||||
@select="emit('select', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-list-item
|
||||
v-else
|
||||
class="folder-node"
|
||||
:title="folderLabelFor(folder)"
|
||||
:active="isSelected"
|
||||
@click="onSelect"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
:icon="folderIconFor(folder)"
|
||||
:color="folderColorFor(folder)"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.folder-node {
|
||||
--v-list-item-prepend-size: 22px;
|
||||
}
|
||||
|
||||
.folder-node.v-list-item--active {
|
||||
background-color: rgba(var(--v-theme-primary), 0.12);
|
||||
}
|
||||
|
||||
.folder-node :deep(.v-list-item__prepend) {
|
||||
padding-inline-start: 4px;
|
||||
margin-inline-end: 2px;
|
||||
}
|
||||
|
||||
.folder-node-children {
|
||||
padding-left: 12px;
|
||||
margin-left: 8px;
|
||||
border-left: 2px solid rgba(var(--v-border-color), 0.3);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user