fix: Snackbar notifications hidden behind modal dialogs (#11616)

This commit is contained in:
Sivin Varghese
2025-05-29 12:49:38 +05:30
committed by GitHub
parent 23a804512a
commit 2ea10ae065
2 changed files with 61 additions and 51 deletions

View File

@@ -20,7 +20,7 @@ export default {
<template> <template>
<div> <div>
<div <div
class="shadow-sm bg-slate-800 dark:bg-slate-700 rounded-[4px] items-center gap-3 inline-flex mb-2 max-w-[25rem] min-h-[1.875rem] min-w-[15rem] px-6 py-3 text-left" class="shadow-sm bg-n-slate-12 dark:bg-n-slate-7 rounded-lg items-center gap-3 inline-flex mb-2 max-w-[25rem] min-h-[1.875rem] min-w-[15rem] px-6 py-3 text-left"
> >
<div class="text-sm font-medium text-white dark:text-white"> <div class="text-sm font-medium text-white dark:text-white">
{{ message }} {{ message }}
@@ -29,7 +29,7 @@ export default {
<router-link <router-link
v-if="action.type == 'link'" v-if="action.type == 'link'"
:to="action.to" :to="action.to"
class="font-medium cursor-pointer select-none text-woot-500 dark:text-woot-500 hover:text-woot-600 dark:hover:text-woot-600" class="font-medium cursor-pointer select-none text-n-blue-10 hover:text-n-brand"
> >
{{ action.message }} {{ action.message }}
</router-link> </router-link>

View File

@@ -1,62 +1,72 @@
<script> <script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import WootSnackbar from './Snackbar.vue'; import WootSnackbar from './Snackbar.vue';
import { emitter } from 'shared/helpers/mitt'; import { emitter } from 'shared/helpers/mitt';
import { useI18n } from 'vue-i18n';
export default { const props = defineProps({
components: { duration: {
WootSnackbar, type: Number,
}, default: 2500,
props: {
duration: {
type: Number,
default: 2500,
},
}, },
});
data() { const { t } = useI18n();
return {
snackMessages: [],
};
},
mounted() { const snackMessages = ref([]);
emitter.on('newToastMessage', this.onNewToastMessage); const snackbarContainer = ref(null);
},
unmounted() {
emitter.off('newToastMessage', this.onNewToastMessage);
},
methods: {
onNewToastMessage({ message: originalMessage, action }) {
// FIX ME: This is a temporary workaround to pass string from functions
// that doesn't have the context of the VueApp.
const usei18n = action?.usei18n;
const duration = action?.duration || this.duration;
const message = usei18n ? this.$t(originalMessage) : originalMessage;
this.snackMessages.push({ const showPopover = () => {
key: new Date().getTime(), try {
message, const el = snackbarContainer.value;
action, if (el?.matches(':popover-open')) {
}); el.hidePopover();
window.setTimeout(() => { }
this.snackMessages.splice(0, 1); el?.showPopover();
}, duration); } catch (e) {
}, // ignore
}, }
}; };
const onNewToastMessage = ({ message: originalMessage, action }) => {
const message = action?.usei18n ? t(originalMessage) : originalMessage;
const duration = action?.duration || props.duration;
snackMessages.value.push({
key: Date.now(),
message,
action,
});
nextTick(showPopover);
setTimeout(() => {
snackMessages.value.shift();
}, duration);
};
onMounted(() => {
emitter.on('newToastMessage', onNewToastMessage);
});
onUnmounted(() => {
emitter.off('newToastMessage', onNewToastMessage);
});
</script> </script>
<template> <template>
<transition-group <div
name="toast-fade" ref="snackbarContainer"
tag="div" popover="manual"
class="left-0 my-0 mx-auto max-w-[25rem] overflow-hidden absolute right-0 text-center top-4 z-[9999]" class="fixed top-4 left-1/2 -translate-x-1/2 max-w-[25rem] w-[calc(100%-2rem)] text-center bg-transparent border-0 p-0 m-0 outline-none overflow-visible"
> >
<WootSnackbar <transition-group name="toast-fade" tag="div">
v-for="snackMessage in snackMessages" <WootSnackbar
:key="snackMessage.key" v-for="snackMessage in snackMessages"
:message="snackMessage.message" :key="snackMessage.key"
:action="snackMessage.action" :message="snackMessage.message"
/> :action="snackMessage.action"
</transition-group> />
</transition-group>
</div>
</template> </template>