vuetify
vuetify copied to clipboard
[Bug Report][3.3.19] Clicking close button of `VSnackbar` opened after `VDialog` closes the dialog too
Environment
Vuetify Version: 3.5.1 Last working version: 3.1.8 Vue Version: 3.4.15 Browsers: Edge 117.0.2045.47 OS: Windows 10
Steps to reproduce
- Have a
v-snackbarthat opens after av-dialog(e.g. showing an error message after submitting a form in the dialog). - Click the close button in the snackbar
Expected Behavior
The snackbar closes.
Actual Behavior
Both the snackbar and the dialog close.
Reproduction Link
https://play.vuetifyjs.com/#...
Other comments
Probably related to the fix for https://github.com/vuetifyjs/vuetify/issues/16893.
I tried using @click.stop in the snackbar's close button but that didn't do anything.
This is a recreation of https://github.com/vuetifyjs/vuetify/issues/17013 after that was closed as a duplicate of https://github.com/vuetifyjs/vuetify/issues/7310 but then the latter was closed as only applying to v2
The dialog will close on every click outside of it, you can use the persistent prop to alter that behavior. Demo
See https://github.com/vuetifyjs/vuetify/issues/17398 for why I am not currently using persistent
The problem has been resolved ? have any new update ? pls help me
This also applies to datepickers.
I ended up having to use persistent and then manually adding back in the close-on-click-outside and close-on-escape functionality
-
Using
v-click-outsidedirective with a handler:onClickOutside = (e) => { // Only close on click outside if it's the "scrim", i.e. we are // not clicking on an error snackbar for example if (e.target?.classList.contains('v-overlay__scrim')) { close() } } -
onKeydown = (e) => { if (e.key === 'Escape') { close() } } onMounted(() => { document.addEventListener('keydown', onKeydown) }) onBeforeUnmount(() => { document.removeEventListener('keydown', onKeydown) })
However I haven't figured out how to re-enable the browser back button closing the dialog
I ran into this issue too and the only solution I found is to use the attach property to teleport the snackbar inside the dialog if one exists. I created a small helper composable to have a reactive attach variable, which will either be the dialogs overlay content or true, based on whether or not a dialog exists when the snackbar text changes.
I don't like this solution. There should be a way to make the dialog aware of the snackbar and setting an exception for the outside click close.
Composable:
type Teleport = boolean | string | HTMLElement;
const useDialogAwareTeleport = (
teleportOverride?: MaybeRefOrGetter<Teleport | undefined>,
elementRef?: MaybeRefOrGetter<undefined | { $el: Node | null }>,
resetOnChange?: MaybeRefOrGetter,
dialogSelector: string = ".v-dialog > .v-overlay__content"
) => {
const dialogAwareTeleport = ref<Teleport>();
watchEffect(() => {
toValue(resetOnChange); // This makes sure the watcher is run on changes of resetOnChange refs
if (toValue(teleportOverride) !== undefined) {
dialogAwareTeleport.value = toValue(teleportOverride)
return;
}
const containingDialog = findContainingDialog();
dialogAwareTeleport.value = containingDialog ? containingDialog : true;
});
function findContainingDialog() {
const dialogs = document.querySelectorAll<HTMLElement>(dialogSelector);
if (dialogs.length === 0) return null;
const elementContainer = toValue(elementRef);
if (!elementContainer) return dialogs[0];
let containingDialog = null;
for (let i = 0; i < dialogs.length && containingDialog === null; i++) {
const dialog = dialogs[i];
if (dialog.contains(elementContainer.$el)) {
containingDialog = dialog;
}
}
return containingDialog;
}
return { dialogAwareTeleport };
};
export { useDialogAwareTeleport };
Usage (some business logic code left out):
<template>
<v-snackbar
v-model="isOpen"
:timeout="-1"
multi-line
:attach="dialogAwareTeleport"
>
<span>{{snackbarText}}</span>
<template #actions>
<e-button
ref="closeButtonRef"
variant="text"
icon="mdi-close"
@click="closeSnackbar"
/>
</template>
</v-snackbar>
</template>
<script setup lang="ts">
const isOpen = ref(false);
const snackbarText = ref("");
const closeButtonRef = ref();
const snackbarIsVisible = useElementVisibility(closeButtonRef);
watch(snackbarIsVisible, (isVisible) => {
if (!isVisible && isOpen.value) closeSnackbar();
});
const { dialogAwareTeleport } = useDialogAwareTeleport(
undefined,
undefined,
snackbarText,
".v-dialog > .v-overlay__content"
);
</script>
I think I've got a fix for this, will open a PR soon