import { Modal as BootstrapModal } from 'bootstrap'; import { globalState } from '../../app/global-state'; export class ModalStackingPatch { static initModalStacking() { if (globalState.__backdropFixInitialized == true) { return; } globalState.__backdropFixInitialized = true; // Bootstrap 5 dispatches show.bs.modal / hidden.bs.modal as native DOM events // on the modal element, so we delegate via document.addEventListener. Use // closest('.modal') rather than e.target.matches('.modal') so the filter mirrors // jQuery's `.on(event, '.modal', handler)` semantics: walk up from e.target to // find the matching ancestor. In practice BS5 always dispatches on the modal // element itself, but closest() is robust if that ever changes. document.addEventListener('show.bs.modal', (e) => { const target = (e.target as Element | null)?.closest('.modal') as HTMLElement | null; if (target == null) { return; } let highestZ = 0; document.querySelectorAll('.modal.show').forEach((el) => { const zi = el.style.zIndex; if (zi != null && zi.length > 0) { const numZi = Number(zi); if (numZi > highestZ && numZi > 1049) { highestZ = numZi; } } }); if (highestZ < 1049) { highestZ = 1049; } const zIndex = highestZ + 2; target.style.zIndex = String(zIndex); setTimeout(() => { document.querySelectorAll('.modal-backdrop:not(.modal-stack)').forEach((el) => { el.style.zIndex = String(zIndex - 1); el.classList.add('modal-stack'); }); }, 0); }); document.addEventListener('hidden.bs.modal', (e) => { const target = (e.target as Element | null)?.closest('.modal') as HTMLElement | null; if (target == null) { return; } target.style.zIndex = ''; // Bootstrap's Backdrop CACHES its element (_getElement() builds it once; // dispose() only detaches it from the DOM), so the `modal-stack` tag and // inline z-index stamped by the show handler above survive the close and // are re-appended STALE on this modal's next show — where the show-timeout // skips already-tagged backdrops, so the stale value is never recomputed. // When the previous show was stacked (e.g. auth modal opened over the // shortcuts modal: backdrop z=1052), a plain reopen gets modal z=1051 and // the stale backdrop paints ABOVE the dialog. Reset both so every show // starts clean. const backdropElement = (BootstrapModal.getInstance(target) as { _backdrop?: { _element?: HTMLElement } } | null)?._backdrop?._element; if (backdropElement != null) { backdropElement.style.zIndex = ''; backdropElement.classList.remove('modal-stack'); } }); } }