Last active
December 6, 2022 14:33
-
-
Save IlCallo/d4e3117cb7a856b29b0026c8ad2417a5 to your computer and use it in GitHub Desktop.
Hijack QDialog click on backdrop to manage it ourselves
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { QDialog } from 'quasar'; | |
import { computed, Ref, watch } from 'vue'; | |
// Quasar doesn't offer a way to hook into the backdrop click event from inside the dialog component itself, | |
// so we have to do it ourselves and prevent the default handler from Quasar from firing | |
export function useDialogBackdropClick( | |
dialogRef: Ref<QDialog | undefined>, | |
handler: (event: FocusEvent) => void | Promise<void>, | |
) { | |
// Apparently retrieving `document.querySelector('.q-dialog__backdrop')` on onMounted doesn't work, | |
// possibly because the backdrop element is somehow re-created later on in the Dialog lifecycle, losing all event listeners | |
// So we obtain it via the dialogRef, which is always updated when its DOM reference changes | |
const backdropRef = computed(() => | |
dialogRef.value?.contentEl.parentElement?.querySelector<HTMLDivElement>( | |
'.q-dialog__backdrop', | |
), | |
); | |
async function onBackdropClick(event: FocusEvent) { | |
// Only react to events that are fired on the backdrop element | |
if (event.target !== backdropRef.value) { | |
return; | |
} | |
// Stops propagation to avoid the listener defined in QDialog from firing | |
// Since all listeners are executed synchronously, we must add this before calling the async handler | |
event.stopPropagation(); | |
await handler(event); | |
// If the handler provides focus to another element, e.g. a dialog, | |
// that could return the focus to the backdrop as soon as it closes, | |
// generating a focus loop. | |
// We prevent this by removing the focus from the backdrop as soon as the handler finishes | |
(event.target as HTMLDivElement).blur(); | |
} | |
watch( | |
backdropRef, | |
(backdrop) => { | |
if (backdrop) { | |
// There's no way to execute our listener before the default handler when adding the listener | |
// on the backdrop element itself, as listeners are executed in the order they are added | |
// So we use a capturing listener on the backdrop parent element to prevent the default handler | |
// from QDialog from firing, as it would close the dialog | |
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
backdrop.parentElement!.addEventListener('focusin', onBackdropClick, { | |
capture: true, | |
}); | |
} | |
}, | |
{ immediate: true }, | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment