From b53ee54e4f1172a0d13ef0808b7236c176bf8d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 18 Aug 2024 14:18:46 +0900 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20focustrap=E3=81=8Czindex?= =?UTF-8?q?=E3=82=92=E8=80=83=E6=85=AE=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=20(#14431)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/focus-trap.ts | 64 +++++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/scripts/focus-trap.ts b/packages/frontend/src/scripts/focus-trap.ts index a5df36f520..fb7caea830 100644 --- a/packages/frontend/src/scripts/focus-trap.ts +++ b/packages/frontend/src/scripts/focus-trap.ts @@ -16,21 +16,57 @@ function containsFocusTrappedElements(el: HTMLElement): boolean { }); } +function getZIndex(el: HTMLElement): number { + const zIndex = parseInt(window.getComputedStyle(el).zIndex || '0', 10); + if (isNaN(zIndex)) { + return 0; + } + return zIndex; +} + +function getHighestZIndexElement(): { el: HTMLElement; zIndex: number; } | null { + let highestZIndexElement: HTMLElement | null = null; + let highestZIndex = -Infinity; + + focusTrapElements.forEach((el) => { + const zIndex = getZIndex(el); + if (zIndex > highestZIndex) { + highestZIndex = zIndex; + highestZIndexElement = el; + } + }); + + return highestZIndexElement == null ? null : { + el: highestZIndexElement, + zIndex: highestZIndex, + }; +} + function releaseFocusTrap(el: HTMLElement): void { focusTrapElements.delete(el); if (el.inert === true) { el.inert = false; } + + const highestZIndexElement = getHighestZIndexElement(); + if (el.parentElement != null && el !== document.body) { el.parentElement.childNodes.forEach((siblingNode) => { const siblingEl = getHTMLElementOrNull(siblingNode); if (!siblingEl) return; - if (siblingEl !== el && (focusTrapElements.has(siblingEl) || containsFocusTrappedElements(siblingEl) || focusTrapElements.size === 0)) { + if ( + siblingEl !== el && + ( + highestZIndexElement == null || + siblingEl === highestZIndexElement.el || + siblingEl.contains(highestZIndexElement.el) + ) + ) { siblingEl.inert = false; } else if ( - focusTrapElements.size > 0 && - !containsFocusTrappedElements(siblingEl) && - !focusTrapElements.has(siblingEl) && + highestZIndexElement != null && + siblingEl !== highestZIndexElement.el && + !siblingEl.contains(highestZIndexElement.el) && !ignoreElements.includes(siblingEl.tagName.toLowerCase()) ) { siblingEl.inert = true; @@ -45,9 +81,29 @@ function releaseFocusTrap(el: HTMLElement): void { export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEls: boolean, parent: true): void; export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEls?: boolean, parent?: false): { release: () => void; }; export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEls = false, parent = false): { release: () => void; } | void { + const highestZIndexElement = getHighestZIndexElement(); + + const highestZIndex = highestZIndexElement == null ? -Infinity : highestZIndexElement.zIndex; + const zIndex = getZIndex(el); + + // If the element has a lower z-index than the highest z-index element, focus trap the highest z-index element instead + // Focus trapping for this element will be done in the release function + if (!parent && zIndex < highestZIndex) { + focusTrapElements.add(el); + if (highestZIndexElement) { + focusTrap(highestZIndexElement.el, hasInteractionWithOtherFocusTrappedEls); + } + return { + release: () => { + releaseFocusTrap(el); + }, + }; + } + if (el.inert === true) { el.inert = false; } + if (el.parentElement != null && el !== document.body) { el.parentElement.childNodes.forEach((siblingNode) => { const siblingEl = getHTMLElementOrNull(siblingNode);