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>
<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">
{{ message }}
@@ -29,7 +29,7 @@ export default {
<router-link
v-if="action.type == 'link'"
: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 }}
</router-link>

View File

@@ -1,62 +1,72 @@
<script>
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import WootSnackbar from './Snackbar.vue';
import { emitter } from 'shared/helpers/mitt';
import { useI18n } from 'vue-i18n';
export default {
components: {
WootSnackbar,
},
props: {
duration: {
type: Number,
default: 2500,
},
const props = defineProps({
duration: {
type: Number,
default: 2500,
},
});
data() {
return {
snackMessages: [],
};
},
const { t } = useI18n();
mounted() {
emitter.on('newToastMessage', this.onNewToastMessage);
},
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;
const snackMessages = ref([]);
const snackbarContainer = ref(null);
this.snackMessages.push({
key: new Date().getTime(),
message,
action,
});
window.setTimeout(() => {
this.snackMessages.splice(0, 1);
}, duration);
},
},
const showPopover = () => {
try {
const el = snackbarContainer.value;
if (el?.matches(':popover-open')) {
el.hidePopover();
}
el?.showPopover();
} 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>
<template>
<transition-group
name="toast-fade"
tag="div"
class="left-0 my-0 mx-auto max-w-[25rem] overflow-hidden absolute right-0 text-center top-4 z-[9999]"
<div
ref="snackbarContainer"
popover="manual"
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
v-for="snackMessage in snackMessages"
:key="snackMessage.key"
:message="snackMessage.message"
:action="snackMessage.action"
/>
</transition-group>
<transition-group name="toast-fade" tag="div">
<WootSnackbar
v-for="snackMessage in snackMessages"
:key="snackMessage.key"
:message="snackMessage.message"
:action="snackMessage.action"
/>
</transition-group>
</div>
</template>