125 lines
2.7 KiB
Vue
125 lines
2.7 KiB
Vue
<template>
|
|
<div class="notifications">
|
|
<NavbarTriggerButton
|
|
:pressed="modalIsOpen"
|
|
ref="toggleButton"
|
|
:aria-label="modalIsOpen ? $t('notification.hideNotifications') : $t('notification.showNotifications')"
|
|
@click="togglePopup"
|
|
>
|
|
<span v-if="hasUnreadNotifications" class="unread-indicator" />
|
|
<icon icon="bell"/>
|
|
</NavbarTriggerButton>
|
|
|
|
<!-- FIXME: create dedicated dropdown menu -->
|
|
<CustomTransition name="fade">
|
|
<div class="notifications-list" v-if="modalIsOpen" ref="popup">
|
|
<h3 class="head">{{ $t('notification.title') }}</h3>
|
|
<NotificationItem
|
|
v-for="n in notifications"
|
|
:key="n.id"
|
|
class="notification-item"
|
|
:notification="n"
|
|
@markNotificationAsRead="emit('markNotificationAsRead', n)"
|
|
/>
|
|
<p class="nothing" v-if="notifications.length === 0">
|
|
{{ $t('notification.none') }}<br/>
|
|
<span class="explainer">
|
|
{{ $t('notification.explainer') }}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</CustomTransition>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import {ref} from 'vue'
|
|
import {onClickOutside} from '@vueuse/core'
|
|
|
|
import type {INotification} from '@/modelTypes/INotification'
|
|
|
|
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
|
import NavbarTriggerButton from '@/components/home/NavbarTriggerButton.vue'
|
|
import NotificationItem from '@/components/notifications/NotificationItem.vue'
|
|
|
|
defineProps<{
|
|
notifications: INotification[],
|
|
hasUnreadNotifications: boolean,
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'markNotificationAsRead', notification: INotification): void
|
|
}>()
|
|
|
|
const modalIsOpen = ref(false)
|
|
const popup = ref(null)
|
|
const toggleButton = ref(null)
|
|
|
|
function togglePopup() {
|
|
modalIsOpen.value = !modalIsOpen.value
|
|
}
|
|
|
|
onClickOutside(
|
|
popup,
|
|
() => {
|
|
if (!modalIsOpen.value) {
|
|
return
|
|
}
|
|
modalIsOpen.value = false
|
|
},
|
|
{ignore: [toggleButton]},
|
|
)
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.unread-indicator {
|
|
position: absolute;
|
|
top: .75rem;
|
|
right: 1.15rem;
|
|
width: .75rem;
|
|
height: .75rem;
|
|
|
|
background: var(--primary);
|
|
border-radius: 100%;
|
|
border: 2px solid var(--white);
|
|
}
|
|
|
|
.notifications-list {
|
|
position: fixed;
|
|
right: 1rem;
|
|
margin-top: 1rem;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
|
|
background: var(--white);
|
|
width: 350px;
|
|
max-width: calc(100vw - 2rem);
|
|
padding: .75rem .25rem;
|
|
border-radius: $radius;
|
|
box-shadow: var(--shadow-sm);
|
|
font-size: .85rem;
|
|
|
|
@media screen and (max-width: $tablet) {
|
|
max-height: calc(100vh - 1rem - #{$navbar-height});
|
|
}
|
|
}
|
|
|
|
.head {
|
|
font-family: $vikunja-font;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.notification-item:last-child {
|
|
margin-bottom: .25rem;
|
|
}
|
|
|
|
.nothing {
|
|
text-align: center;
|
|
padding: 1rem 0;
|
|
color: var(--grey-500);
|
|
}
|
|
|
|
.explainer {
|
|
font-size: .75rem;
|
|
}
|
|
</style> |