Initial commit

This commit is contained in:
2026-02-10 19:39:08 -05:00
commit 2a251f9b3f
32 changed files with 6135 additions and 0 deletions

View File

@@ -0,0 +1,229 @@
<script setup lang="ts">
import type { CollectionObject } from '@MailManager/models/collection'
import type { ServiceInterface } from '@MailManager/types/service'
export interface FolderNode {
folder: CollectionObject
children: FolderNode[]
}
export interface Props {
node: FolderNode
service: ServiceInterface
selectedFolder?: CollectionObject | null
}
const props = defineProps<Props>()
const emit = defineEmits<{
select: [folder: CollectionObject]
createSubfolder: [service: ServiceInterface, parentFolder: CollectionObject]
}>()
// Get icon for folder based on role
const getFolderIcon = (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'
}
}
// Get color for folder based on role
const getFolderColor = (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
}
}
// Check if folder is selected
const isSelected = (folder: CollectionObject): boolean => {
if (!props.selectedFolder) return false
return (
folder.provider === props.selectedFolder.provider &&
String(folder.service) === String(props.selectedFolder.service) &&
String(folder.identifier) === String(props.selectedFolder.identifier)
)
}
</script>
<template>
<v-list-group v-if="node.children.length > 0" class="folder-tree-group">
<template v-slot:activator="{ props: activatorProps }">
<v-list-item
v-bind="activatorProps"
:title="node.folder.properties.label"
:active="isSelected(node.folder)"
@click.stop="emit('select', node.folder)"
>
<template v-slot:prepend>
<v-icon
:icon="getFolderIcon(node.folder)"
:color="getFolderColor(node.folder)"
/>
</template>
<template v-slot:append>
<v-badge
v-if="node.folder.properties.unread && node.folder.properties.unread > 0"
:content="node.folder.properties.unread"
color="primary"
inline
class="mr-2"
/>
<!-- Action menu -->
<v-menu>
<template v-slot:activator="{ props: menuProps }">
<v-btn
v-bind="menuProps"
icon="mdi-dots-vertical"
variant="text"
size="x-small"
density="compact"
@click.stop
>
<v-icon size="small">mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list density="compact">
<v-list-item
prepend-icon="mdi-folder-plus"
@click="emit('createSubfolder', service, node.folder)"
>
<v-list-item-title>New Subfolder</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-list-item>
</template>
<div class="folder-tree-children">
<FolderTreeNode
v-for="child in node.children"
:key="child.folder.identifier"
:node="child"
:service="service"
:selected-folder="selectedFolder"
@select="emit('select', $event)"
@create-subfolder="(service, parentFolder) => emit('createSubfolder', service, parentFolder)"
/>
</div>
</v-list-group>
<v-list-item
v-else
:title="node.folder.properties.label"
:active="isSelected(node.folder)"
@click="emit('select', node.folder)"
>
<template v-slot:prepend>
<v-icon
:icon="getFolderIcon(node.folder)"
:color="getFolderColor(node.folder)"
/>
</template>
<template v-slot:append>
<v-badge
v-if="node.folder.properties.unread && node.folder.properties.unread > 0"
:content="node.folder.properties.unread"
color="primary"
inline
class="mr-2"
/>
<!-- Action menu -->
<v-menu>
<template v-slot:activator="{ props: menuProps }">
<v-btn
v-bind="menuProps"
icon="mdi-dots-vertical"
variant="text"
size="x-small"
density="compact"
@click.stop
>
<v-icon size="small">mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list density="compact">
<v-list-item
prepend-icon="mdi-folder-plus"
@click="emit('createSubfolder', service, node.folder)"
>
<v-list-item-title>New Subfolder</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-list-item>
</template>
<script lang="ts">
export default {
name: 'FolderTreeNode'
}
</script>
<style scoped>
.v-list-item--active {
background-color: rgba(var(--v-theme-primary), 0.12);
}
/* Remove indentation for the folder-tree-group itself */
.folder-tree-group {
--indent-padding: 0 !important;
}
/* Reduce default indentation for nested items */
.folder-tree-group :deep(.v-list-group__items) {
--indent-padding: 0;
}
/* Add visual indicator for nested items */
.folder-tree-children {
position: relative;
padding-left: 12px;
border-left: 2px solid rgba(var(--v-border-color), 0.3);
margin-left: 8px;
}
/* Add connector line for nested items */
.folder-tree-children::before {
content: '';
position: absolute;
left: -2px;
top: 0;
bottom: 0;
width: 2px;
background: rgba(var(--v-border-color), 0.3);
}
</style>