Files
mail/src/components/composer/ComposerRecipients.vue
T
2026-06-22 22:54:02 -04:00

156 lines
4.2 KiB
Vue

<script setup lang="ts">
import { useRecipientLookupStore } from '@/stores/mailRecipientsStore'
import type { MessageAddressInterface } from '@MailManager/types/message'
interface Props {
to: MessageAddressInterface[]
cc: MessageAddressInterface[]
bcc: MessageAddressInterface[]
subject: string
showCc: boolean
showBcc: boolean
}
defineProps<Props>()
const emit = defineEmits<{
'update:to': [value: MessageAddressInterface[]]
'update:cc': [value: MessageAddressInterface[]]
'update:bcc': [value: MessageAddressInterface[]]
'update:subject': [value: string]
'toggle:cc': []
'toggle:bcc': []
}>()
const recipientLookup = useRecipientLookupStore()
/**
* Normalize combobox entries: typed strings become bare address objects;
* picked suggestions are already MessageAddressInterface and pass through.
*/
function normalize(entries: (MessageAddressInterface | string)[]): MessageAddressInterface[] {
return entries.map(entry =>
typeof entry === 'string' ? { address: entry } : entry,
)
}
function chipLabel(item: MessageAddressInterface): string {
return item.label ? `${item.label} <${item.address}>` : item.address
}
function itemTitle(item: MessageAddressInterface): string {
return item.label || item.address
}
</script>
<template>
<div class="composer-fields pa-4">
<v-combobox
:model-value="to"
label="To"
chips
multiple
closable-chips
return-object
no-filter
variant="outlined"
density="compact"
class="mb-2"
:items="recipientLookup.suggestions"
:loading="recipientLookup.searching"
:item-title="itemTitle"
@update:search="recipientLookup.search($event)"
@update:model-value="emit('update:to', normalize($event))"
>
<template #chip="{ item, props: chipProps }">
<v-chip v-bind="chipProps">{{ chipLabel(item) }}</v-chip>
</template>
<template #item="{ item, props: itemProps }">
<v-list-item v-bind="itemProps" :subtitle="item.label ? item.address : undefined" />
</template>
<template #append-inner>
<v-btn
size="x-small"
variant="text"
class="mr-1"
@click="$emit('toggle:cc')"
>
Cc
</v-btn>
<v-btn
size="x-small"
variant="text"
@click="$emit('toggle:bcc')"
>
Bcc
</v-btn>
</template>
</v-combobox>
<v-combobox
v-if="showCc"
:model-value="cc"
label="Cc"
chips
multiple
closable-chips
return-object
no-filter
variant="outlined"
density="compact"
class="mb-2"
:items="recipientLookup.suggestions"
:loading="recipientLookup.searching"
:item-title="itemTitle"
@update:search="recipientLookup.search($event)"
@update:model-value="emit('update:cc', normalize($event))"
>
<template #chip="{ item, props: chipProps }">
<v-chip v-bind="chipProps">{{ chipLabel(item) }}</v-chip>
</template>
<template #item="{ item, props: itemProps }">
<v-list-item v-bind="itemProps" :subtitle="item.label ? item.address : undefined" />
</template>
</v-combobox>
<v-combobox
v-if="showBcc"
:model-value="bcc"
label="Bcc"
chips
multiple
closable-chips
return-object
no-filter
variant="outlined"
density="compact"
class="mb-2"
:items="recipientLookup.suggestions"
:loading="recipientLookup.searching"
:item-title="itemTitle"
@update:search="recipientLookup.search($event)"
@update:model-value="emit('update:bcc', normalize($event))"
>
<template #chip="{ item, props: chipProps }">
<v-chip v-bind="chipProps">{{ chipLabel(item) }}</v-chip>
</template>
<template #item="{ item, props: itemProps }">
<v-list-item v-bind="itemProps" :subtitle="item.label ? item.address : undefined" />
</template>
</v-combobox>
<v-text-field
:model-value="subject"
label="Subject"
variant="outlined"
density="compact"
@update:model-value="$emit('update:subject', $event)"
/>
</div>
</template>
<style scoped lang="scss">
.composer-fields {
flex-shrink: 0;
}
</style>