138 lines
3.7 KiB
Vue
138 lines
3.7 KiB
Vue
<script setup lang="ts">
|
|
import { computed, getCurrentInstance, ref } from 'vue'
|
|
import type { EntityObject } from '@MailManager/models'
|
|
|
|
interface Props {
|
|
message: EntityObject
|
|
modelValue?: boolean
|
|
target?: [number, number] | undefined
|
|
showActivator?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
target: undefined,
|
|
showActivator: true,
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [value: boolean]
|
|
reply: [message: EntityObject]
|
|
forward: [message: EntityObject]
|
|
move: [message: EntityObject]
|
|
delete: [message: EntityObject]
|
|
flag: [message: EntityObject, flag: string, value: boolean]
|
|
}>()
|
|
|
|
const isRead = computed(() => props.message.properties.isRead === true)
|
|
const isFlagged = computed(() => props.message.properties.isFlagged === true)
|
|
const localIsOpen = ref(false)
|
|
const instance = getCurrentInstance()
|
|
const isControlled = computed(() => {
|
|
const vnodeProps = instance?.vnode.props ?? {}
|
|
|
|
return Object.prototype.hasOwnProperty.call(vnodeProps, 'modelValue')
|
|
|| Object.prototype.hasOwnProperty.call(vnodeProps, 'onUpdate:modelValue')
|
|
})
|
|
|
|
const isOpen = computed({
|
|
get: () => (isControlled.value ? props.modelValue ?? false : localIsOpen.value),
|
|
set: (value: boolean) => {
|
|
if (!isControlled.value) {
|
|
localIsOpen.value = value
|
|
}
|
|
|
|
emit('update:modelValue', value)
|
|
},
|
|
})
|
|
|
|
const handleFlag = (flag: string, value: boolean) => {
|
|
emit('flag', props.message, flag, value)
|
|
isOpen.value = false
|
|
}
|
|
|
|
const handleReply = () => {
|
|
emit('reply', props.message)
|
|
isOpen.value = false
|
|
}
|
|
|
|
const handleForward = () => {
|
|
emit('forward', props.message)
|
|
isOpen.value = false
|
|
}
|
|
|
|
const handleMove = () => {
|
|
emit('move', props.message)
|
|
isOpen.value = false
|
|
}
|
|
|
|
const handleDelete = () => {
|
|
emit('delete', props.message)
|
|
isOpen.value = false
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<v-menu
|
|
v-model="isOpen"
|
|
:target="target"
|
|
:absolute="target !== undefined"
|
|
:close-on-content-click="true"
|
|
location="bottom end"
|
|
content-class="message-item-menu-content"
|
|
>
|
|
<template v-if="showActivator" #activator="{ props: menuProps }">
|
|
<v-btn
|
|
v-bind="menuProps"
|
|
class="message-item-menu-trigger"
|
|
icon="mdi-dots-vertical"
|
|
size="small"
|
|
variant="text"
|
|
@click.stop
|
|
@mousedown.stop
|
|
>
|
|
<v-icon>mdi-dots-vertical</v-icon>
|
|
<v-tooltip activator="parent" location="bottom">More Actions</v-tooltip>
|
|
</v-btn>
|
|
</template>
|
|
|
|
<v-list class="message-item-menu" density="compact" min-width="220">
|
|
<v-list-item
|
|
:prepend-icon="isRead ? 'mdi-email-outline' : 'mdi-email-open-outline'"
|
|
@click="handleFlag('read', !isRead)"
|
|
>
|
|
<v-list-item-title>
|
|
{{ isRead ? 'Mark as unread' : 'Mark as read' }}
|
|
</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item
|
|
:prepend-icon="isFlagged ? 'mdi-star' : 'mdi-star-outline'"
|
|
@click="handleFlag('flagged', !isFlagged)"
|
|
>
|
|
<v-list-item-title>
|
|
{{ isFlagged ? 'Unstar' : 'Star' }}
|
|
</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-divider class="my-1" />
|
|
|
|
<v-list-item prepend-icon="mdi-folder-move-outline" @click="handleMove">
|
|
<v-list-item-title>Move to...</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item prepend-icon="mdi-reply" @click="handleReply">
|
|
<v-list-item-title>Reply</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item prepend-icon="mdi-share" @click="handleForward">
|
|
<v-list-item-title>Forward</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-divider class="my-1" />
|
|
|
|
<v-list-item prepend-icon="mdi-delete-outline" @click="handleDelete">
|
|
<v-list-item-title>Delete</v-list-item-title>
|
|
</v-list-item>
|
|
</v-list>
|
|
</v-menu>
|
|
</template> |