Skip to content

Instantly share code, notes, and snippets.

@innocenzi
Created August 19, 2022 14:58
Show Gist options
  • Save innocenzi/fe1bed6735e465a6448f96d158af4bd5 to your computer and use it in GitHub Desktop.
Save innocenzi/fe1bed6735e465a6448f96d158af4bd5 to your computer and use it in GitHub Desktop.
<script setup lang="ts">
// .....
import { setupWithInertia, NotificationRegion } from '~/ts/notifications/notifications'
setupWithInertia()
</script>
<template>
<slot />
<!-- ...... -->
<!-- Notifications -->
<NotificationRegion />
</template>
import { random } from '../utils/string'
import type { Notification, NotificationOptions } from './types'
import { useProperty } from '@/composables/useProperty'
const notifications = ref<Notification[]>([])
/**
* Pushes notifications from the server, reactively.
*/
export function setupWithInertia() {
const notifications = useProperty('notifications')
watch(notifications, () => {
const { push } = useNotifications()
for (const [type, content] of Object.entries(notifications.value)) {
if (content) {
push({
type: type as Notification['type'],
content: content as Notification['content'],
})
}
}
}, { immediate: true })
}
export function createNotification(notification: Omit<Notification, 'state'>, options: NotificationOptions = {}) {
return {
state: {
id: random(),
closing: false,
decayTime: options?.decay || 300,
},
...notification,
}
}
/** Pushes a notification. */
export function pushNotification(notification: Omit<Notification, 'state'>, options: NotificationOptions = {}) {
const actual = createNotification(notification, options)
notifications.value.push(actual)
if (options?.decay !== false) {
setTimeout(() => dismissNotification(actual), options?.decay ?? 8000)
}
return {
dismiss: () => dismissNotification(actual),
}
}
/** Dismisses a notification. */
export function dismissNotification(notification: Notification) {
const index = notifications.value.findIndex(({ state }) => state.id === notification.state.id)
if (index === -1) {
return
}
notifications.value.splice(index, 1)
}
export function useNotifications() {
return {
notifications: readonly(notifications),
push: pushNotification,
dismiss: dismissNotification,
}
}
export { default as NotificationRegion } from '../../vue/components/Notification/Region.vue'
<script setup lang="ts">
import { useNotifications } from '~/ts/notifications/notifications'
const { notifications, dismiss } = useNotifications()
</script>
<template>
<div aria-live="assertive" class="flex fixed inset-0 z-40 items-end py-6 px-4 pointer-events-none sm:items-start sm:p-6">
<div class="flex flex-col items-center space-y-4 w-full sm:items-end">
<TransitionGroup
tag="ul"
class="space-y-2 w-full max-w-sm"
enter-active-class="transform ease-out duration-300 transition"
enter-from-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
leave-active-class="transition ease-in duration-500"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<li v-for="(notification) in notifications" :key="notification.state.id">
<NotificationItem :notification="notification" @dismiss="dismiss(notification)" />
</li>
</TransitionGroup>
</div>
</div>
</template>
export interface Notification {
type: 'success' | 'error' | 'warning' | 'info'
closeable?: boolean
title?: string
content: string
state: {
id: string
}
}
export interface NotificationOptions {
decay?: number | false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment