Move the reCAPTCHA widget overlay to the `pageOverlayContainer` when widget form...
authorCyperghost <olaf_schmitz_1@t-online.de>
Fri, 1 Mar 2024 10:56:55 +0000 (11:56 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Fri, 1 Mar 2024 10:56:55 +0000 (11:56 +0100)
ts/WoltLabSuite/Core/Bootstrap.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js
wcfsetup/install/files/style/ui/recaptcha.scss

index 736d3cd3b0e5088fc617418cde0941023b41984e..5484935875fd3f605572e2b59105e80fa85ae65c 100644 (file)
@@ -31,7 +31,7 @@ import * as UiObjectActionToggle from "./Ui/Object/Action/Toggle";
 import { init as initSearch } from "./Ui/Search";
 import { PageMenuMainProvider } from "./Ui/Page/Menu/Main/Provider";
 import { whenFirstSeen } from "./LazyLoader";
-import { adoptPageOverlayContainer } from "./Helper/PageOverlay";
+import { adoptPageOverlayContainer, getPageOverlayContainer } from "./Helper/PageOverlay";
 
 // perfectScrollbar does not need to be bound anywhere, it just has to be loaded for WCF.js
 import "perfect-scrollbar";
@@ -168,4 +168,40 @@ export function setup(options: BoostrapOptions): void {
   whenFirstSeen("[data-google-maps-geocoding]", () => {
     void import("./Component/GoogleMaps/Geocoding").then(({ setup }) => setup());
   });
+
+  // Move the reCAPTCHA widget overlay to the `pageOverlayContainer`
+  // when widget form elements are placed in a dialog.
+  const observer = new MutationObserver((mutations) => {
+    for (const mutation of mutations) {
+      for (const node of mutation.addedNodes) {
+        if (!(node instanceof HTMLElement)) {
+          continue;
+        }
+
+        if (node.querySelectorAll(".g-recaptcha-bubble-arrow").length === 0) {
+          return;
+        }
+
+        const iframe = node.querySelector("iframe");
+        if (!iframe) {
+          return;
+        }
+        const name = "a-" + iframe.name.split("-")[1];
+        const widget = document.querySelector(`iframe[name="${name}"]`);
+        if (!widget) {
+          return;
+        }
+        const dialog = widget.closest("woltlab-core-dialog");
+        if (!dialog) {
+          return;
+        }
+
+        getPageOverlayContainer().append(node);
+        node.classList.add("g-recaptcha-container");
+      }
+    }
+  });
+  observer.observe(document.body, {
+    childList: true,
+  });
 }
index 3c7f0fb55081b40e4d9a9f63d69554c32f1b9ebb..cba11338bc227359937d1ab07ce20c2d67608dc8 100644 (file)
@@ -133,6 +133,38 @@ define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Devtools",
         (0, LazyLoader_1.whenFirstSeen)("[data-google-maps-geocoding]", () => {
             void new Promise((resolve_5, reject_5) => { require(["./Component/GoogleMaps/Geocoding"], resolve_5, reject_5); }).then(tslib_1.__importStar).then(({ setup }) => setup());
         });
+        // Move the reCAPTCHA widget overlay to the `pageOverlayContainer`
+        // when widget form elements are placed in a dialog.
+        const observer = new MutationObserver((mutations) => {
+            for (const mutation of mutations) {
+                for (const node of mutation.addedNodes) {
+                    if (!(node instanceof HTMLElement)) {
+                        continue;
+                    }
+                    if (node.querySelectorAll(".g-recaptcha-bubble-arrow").length === 0) {
+                        return;
+                    }
+                    const iframe = node.querySelector("iframe");
+                    if (!iframe) {
+                        return;
+                    }
+                    const name = "a-" + iframe.name.split("-")[1];
+                    const widget = document.querySelector(`iframe[name="${name}"]`);
+                    if (!widget) {
+                        return;
+                    }
+                    const dialog = widget.closest("woltlab-core-dialog");
+                    if (!dialog) {
+                        return;
+                    }
+                    (0, PageOverlay_1.getPageOverlayContainer)().append(node);
+                    node.classList.add("g-recaptcha-container");
+                }
+            }
+        });
+        observer.observe(document.body, {
+            childList: true,
+        });
     }
     exports.setup = setup;
 });
index b77b0085fd4e0a9cca40486cbe47ca538031f87e..5ac8c2144bedb06746a26c8b8adf3441de2d4435 100644 (file)
@@ -2,3 +2,21 @@
 #recaptcha_response_field {
        margin-top: 20px;
 }
+
+/* ReCAPTCHA container for a dialog element */
+.g-recaptcha-container {
+       position: fixed !important;
+
+       > div:not(:first-child):nth-of-type(-n+3) {
+               display: none !important;
+       }
+
+       > div:last-child {
+               position: fixed !important;
+               top: 0 !important;
+               left: 0 !important;
+               bottom: 0 !important;
+               right: 0 !important;
+               margin: auto !important;
+       }
+}