first commit
This commit is contained in:
47
src/components/Notification/Notification.vue
Normal file
47
src/components/Notification/Notification.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import store from './store';
|
||||
import type { Notification } from './interfaces';
|
||||
import { ErrorIcon, InfoIcon, WarningIcon, SuccessIcon, CloseIcon } from './icons';
|
||||
|
||||
defineProps<{
|
||||
notification: Notification,
|
||||
}>();
|
||||
|
||||
const closeNotification = (id: string) => {
|
||||
store.actions.removeNotification(id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-white shadow-lg rounded-lg pointer-events-auto">
|
||||
<div class="rounded-lg shadow-xl overflow-hidden">
|
||||
<div class="p-4">
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0" v-if="notification.type">
|
||||
<SuccessIcon v-if="notification.type === 'success'" />
|
||||
<InfoIcon v-else-if="notification.type === 'info'" />
|
||||
<WarningIcon v-else-if="notification.type === 'warning'" />
|
||||
<ErrorIcon v-else-if="notification.type === 'error'" />
|
||||
</div>
|
||||
<div class="ml-3 w-0 flex-1 pt-0.5">
|
||||
<p class="text-sm leading-5 font-medium text-gray-900" v-if="notification.title">
|
||||
{{ notification.title }}
|
||||
</p>
|
||||
<p :class="`${notification.title ? 'mt-1' : ''} text-sm leading-5 text-gray-500`">
|
||||
{{ notification.content }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="ml-4 flex-shrink-0 flex">
|
||||
<button
|
||||
@click="() => closeNotification(notification.id)"
|
||||
class="inline-flex text-gray-400 focus:outline-none focus:text-gray-500 transition-all ease-in-out duration-150"
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
26
src/components/Notification/NotificationProvider.vue
Normal file
26
src/components/Notification/NotificationProvider.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import Notification from './Notification.vue';
|
||||
import store from './store';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex items-start justify-end px-4 py-6 pointer-events-none sm:p-6">
|
||||
<div class="w-full max-w-sm">
|
||||
<TransitionGroup appear tag="div"
|
||||
:enter-active-class="store.getters.getNotificationsCount() > 1 ? 'transform ease-out delay-300 duration-300 transition' : 'transform ease-out duration-300 transition'"
|
||||
enter-from-class="translate-x-4 opacity-0" enter-to-class="translate-x-0 opacity-100"
|
||||
leave-active-class="transition duration-100 ease-in" leave-from-class="opacity-100" leave-to-class="opacity-0"
|
||||
move-class="transition duration-500 ease-in-out">
|
||||
<Notification :key="notification.id" :notification="notification" :class="idx > 0 ? 'mt-4' : ''"
|
||||
v-for="(notification, idx) in store.getters.getNotifications()" />
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.delay-300 {
|
||||
transition-delay: 300ms;
|
||||
}
|
||||
</style>
|
5
src/components/Notification/icons/CloseIcon.vue
Normal file
5
src/components/Notification/icons/CloseIcon.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" class="w-5 h-5">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</template>
|
5
src/components/Notification/icons/ErrorIcon.vue
Normal file
5
src/components/Notification/icons/ErrorIcon.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-red-400">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
||||
</svg>
|
||||
</template>
|
5
src/components/Notification/icons/InfoIcon.vue
Normal file
5
src/components/Notification/icons/InfoIcon.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-blue-400">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
||||
</svg>
|
||||
</template>
|
6
src/components/Notification/icons/SuccessIcon.vue
Normal file
6
src/components/Notification/icons/SuccessIcon.vue
Normal file
@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" class="h-6 w-6 text-green-400">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</template>
|
||||
|
5
src/components/Notification/icons/WarningIcon.vue
Normal file
5
src/components/Notification/icons/WarningIcon.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-orange-400">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
|
||||
</svg>
|
||||
</template>
|
13
src/components/Notification/icons/index.ts
Normal file
13
src/components/Notification/icons/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import ErrorIcon from "./ErrorIcon.vue";
|
||||
import WarningIcon from "./WarningIcon.vue";
|
||||
import InfoIcon from "./InfoIcon.vue";
|
||||
import SuccessIcon from "./SuccessIcon.vue";
|
||||
import CloseIcon from "./CloseIcon.vue";
|
||||
|
||||
export {
|
||||
ErrorIcon,
|
||||
WarningIcon,
|
||||
InfoIcon,
|
||||
SuccessIcon,
|
||||
CloseIcon
|
||||
}
|
9
src/components/Notification/index.ts
Normal file
9
src/components/Notification/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import NotificationProvider from "./NotificationProvider.vue";
|
||||
import store from "./store";
|
||||
|
||||
const { dispatchNotification } = store.actions;
|
||||
|
||||
export {
|
||||
NotificationProvider,
|
||||
dispatchNotification
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
export type NotificationType = 'success' | 'info' | 'warning' | 'error';
|
||||
|
||||
export interface Notification {
|
||||
id: string;
|
||||
title?: string;
|
||||
content: string;
|
||||
type?: NotificationType;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import type { NotificationType } from "./Notification.interface";
|
||||
|
||||
export interface NotificationConfig {
|
||||
title?: string;
|
||||
content: string;
|
||||
duration?: number;
|
||||
autoClose?: boolean;
|
||||
type?: NotificationType;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import type { Notification } from './Notification.interface';
|
||||
|
||||
export interface NotificationsState {
|
||||
notifications: Notification[];
|
||||
}
|
5
src/components/Notification/interfaces/index.ts
Normal file
5
src/components/Notification/interfaces/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import type { Notification, NotificationType } from './Notification.interface'
|
||||
import type { NotificationsState } from './NotificationsState.interface'
|
||||
import type { NotificationConfig } from './NotificationConfig.interface'
|
||||
|
||||
export type { Notification, NotificationsState, NotificationConfig, NotificationType };
|
50
src/components/Notification/store/index.ts
Normal file
50
src/components/Notification/store/index.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import type { NotificationsState, NotificationConfig } from '../interfaces';
|
||||
import { reactive, readonly } from 'vue';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const state = reactive<NotificationsState>({
|
||||
notifications: [],
|
||||
});
|
||||
|
||||
const actions = {
|
||||
removeNotification(id: string) {
|
||||
state.notifications = state.notifications.filter(notification => notification.id !== id);
|
||||
},
|
||||
dispatchNotification({
|
||||
title,
|
||||
content,
|
||||
type,
|
||||
autoClose = true,
|
||||
duration = 3000,
|
||||
}: NotificationConfig) {
|
||||
const id = uuidv4();
|
||||
const notifications = [{
|
||||
id,
|
||||
title,
|
||||
content,
|
||||
type,
|
||||
}, ...state.notifications];
|
||||
state.notifications = notifications;
|
||||
|
||||
if (autoClose) {
|
||||
setTimeout(() => {
|
||||
actions.removeNotification(id);
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getters = {
|
||||
getNotifications() {
|
||||
return [...state.notifications].slice(0,4);
|
||||
},
|
||||
getNotificationsCount() {
|
||||
return state.notifications.length;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state: readonly(state),
|
||||
actions,
|
||||
getters,
|
||||
}
|
Reference in New Issue
Block a user