@@ -16,6 +16,31 @@ const emit = defineEmits<{ close: [] }>()
1616
1717const dialog = ref <HTMLElement | null >(null )
1818
19+ /**
20+ * Refcounted, because several modals are mounted at once - a page can hold two ConfirmModals and a
21+ * LogModal, and every table row with a result mounts its own. Releasing unconditionally let a modal
22+ * that had never been open clear the lock a genuinely open one was holding, so the page scrolled
23+ * behind the dialog.
24+ */
25+ let bodyLocks = 0
26+
27+ function lockBody() {
28+ if (++ bodyLocks === 1 ) {
29+ document .body .classList .add (' modal-open' )
30+ document .body .style .overflow = ' hidden'
31+ }
32+ }
33+
34+ function unlockBody() {
35+ if (-- bodyLocks === 0 ) {
36+ document .body .classList .remove (' modal-open' )
37+ document .body .style .overflow = ' '
38+ }
39+ }
40+
41+ // whether THIS instance currently holds a lock, so it only ever releases its own
42+ let locked = false
43+
1944// so closing returns the keyboard to whatever opened the modal, rather than to <body>
2045let previouslyFocused: HTMLElement | null = null
2146
@@ -29,14 +54,19 @@ function onKeydown(event: KeyboardEvent) {
2954watch (
3055 () => props .open ,
3156 async (open ) => {
32- document .body .classList .toggle (' modal-open' , open )
33- document .body .style .overflow = open ? ' hidden' : ' '
57+ if (open === locked ) {
58+ return
59+ }
60+ locked = open
61+
3462 if (open ) {
63+ lockBody ()
3564 previouslyFocused = document .activeElement as HTMLElement | null
3665 document .addEventListener (' keydown' , onKeydown )
3766 await nextTick ()
3867 dialog .value ?.focus ()
3968 } else {
69+ unlockBody ()
4070 document .removeEventListener (' keydown' , onKeydown )
4171 previouslyFocused ?.focus ()
4272 previouslyFocused = null
@@ -48,8 +78,10 @@ watch(
4878
4979onBeforeUnmount (() => {
5080 document .removeEventListener (' keydown' , onKeydown )
51- document .body .classList .remove (' modal-open' )
52- document .body .style .overflow = ' '
81+ if (locked ) {
82+ locked = false
83+ unlockBody ()
84+ }
5385})
5486 </script >
5587
0 commit comments