Skip to content

Instantly share code, notes, and snippets.

@CamKem
Last active March 4, 2024 14:12
Show Gist options
  • Save CamKem/df599816138dcd26c0055f5a61eb908f to your computer and use it in GitHub Desktop.
Save CamKem/df599816138dcd26c0055f5a61eb908f to your computer and use it in GitHub Desktop.
Popup Flash Messages for VueJS
<template>
<teleport to="body">
<Transition
enter-active-class="transition duration-300 ease-in-out"
enter-from-class="translate-y-12 scale-75 opacity-0 ease-in-out"
enter-to-class="scale-100 opacity-100 ease-in-out"
leave-active-class="transition duration-300 ease-in-out"
leave-from-class="translate-y-0 scale-100 opacity-100 ease-in-out"
leave-to-class="translate-y-12 scale-75 opacity-0 ease-in-out"
>
<div
v-if="flash.show"
:id="`flash-message-${props.id}`"
:class="getFlashMessageClasses(flash.type)"
class="flash-message"
>
{{ flash.message }}
</div>
</Transition>
</teleport>
</template>
<script setup>
import {nextTick, onMounted} from "vue";
import useElementById from "../Composables/useElementById";
import {useFlashStore} from "../Stores/flashStore";
let props = defineProps({id: Number});
const store = useFlashStore();
const flash = store.findMessage(props.id);
onMounted(() => {
store.setHideTimer(props.id, 3000)
nextTick(() => {
const el = useElementById(`flash-message-${props.id}`);
el.value.style.marginBottom = `${(props.id * 80) + 16}px`;
});
});
const getFlashMessageClasses = (type) => {
return {
"text-sky-300 border-sky-300": type === "success",
"border-red-300 text-red-300": type === "error",
"border-amber-200 text-amber-200": type === "warning",
"border-cyan-300 text-cyan-300": type === "info",
};
};
</script>
<style scoped>
.flash-message {
@apply fixed right-0 bottom-0 z-50 m-4 rounded-lg border bg-gray-700 px-8 py-6 text-sm font-bold shadow-lg;
@apply hover:bg-gray-700 hover:bg-clip-padding hover:backdrop-filter hover:backdrop-blur-sm hover:bg-opacity-30;
}
</style>
import {defineStore} from 'pinia'
export const useFlashStore = defineStore('flash', {
state: () => ({
messages: [],
}),
getters: {
getMessages() {
return this.messages
}
},
actions: {
getNewId() {
const ids = this.messages.map(message => message.id)
const max = ids.length === 0 ? undefined : Math.max(...ids)
return max === undefined ? 0 : max + 1
},
findMessage(id) {
return this.messages.find(message => message.id === id)
},
removeMessage(id) {
this.messages = this.messages.filter(message => message.id !== id)
},
addMessage(message) {
this.messages.push({
id: this.getNewId(),
show: true,
...message
})
},
hideMessage(id) {
this.findMessage(id).show = false
setTimeout(() => {
this.removeMessage(id)
}, 300)
},
setHideTimer(id, timer) {
setTimeout(() => {
this.hideMessage(id)
}, timer)
}
},
})
<template>
<FlashMessage v-for="message in flash.getMessages" :key="message.id" :id="message.id" />
</template>
<script setup>
import axios from 'axios';
import {onMounted, watchEffect} from "vue";
import {useFlashStore} from "../../Stores/flashStore";
const flash = useFlashStore();
const props = defineProps({ flash: Object });
onMounted(() => {
if (props.flash) {
flash.addMessage(props.flash);
}
watchEffect(() => {
axios.interceptors.response.use(
function (response) {
if (response.data.flash) {
flash.addMessage(response.data.flash);
}
return response;
},
);
});
});
</script>
@CamKem
Copy link
Author

CamKem commented Mar 4, 2024

Pretty self explanatory, happy to answer questions .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment