Improved the behavior of the search overlay
authorAlexander Ebert <ebert@woltlab.com>
Mon, 20 Dec 2021 14:43:23 +0000 (15:43 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 20 Dec 2021 14:43:23 +0000 (15:43 +0100)
com.woltlab.wcf/templates/pageHeaderSearch.tpl
ts/WoltLabSuite/Core/Ui/CloseOverlay.ts
ts/WoltLabSuite/Core/Ui/Dropdown/Simple.ts
ts/WoltLabSuite/Core/Ui/Mobile.ts
ts/WoltLabSuite/Core/Ui/Page/Header/Fixed.ts
ts/WoltLabSuite/Core/Ui/Search/Page.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/CloseOverlay.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Dropdown/Simple.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Mobile.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Header/Fixed.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Search/Page.js

index 0483f63b8d7a47020b0465e8efa5d7584b986324..5b4a44d45137dc6d5bd5a7e7d41687f1a6be03fb 100644 (file)
@@ -28,7 +28,7 @@
        <form method="post" action="{@$__searchLink}">
                <div id="pageHeaderSearchInputContainer" class="pageHeaderSearchInputContainer">
                        <div class="pageHeaderSearchType dropdown">
-                               <a href="#" class="button dropdownToggle"><span class="pageHeaderSearchTypeLabel">{@$__searchTypeLabel}</span></a>
+                               <a href="#" class="button dropdownToggle" id="pageHeaderSearchTypeSelect"><span class="pageHeaderSearchTypeLabel">{@$__searchTypeLabel}</span></a>
                                <ul class="dropdownMenu">
                                        <li><a href="#" data-extended-link="{link controller='Search'}{/link}" data-object-type="everywhere">{lang}wcf.search.type.everywhere{/lang}</a></li>
                                        <li class="dropdownDivider"></li>
index d01867f9f2ecab53ae185b9145ad8d5064aecff7..980022ed87655490a48ea03d369f73d9f53def17 100644 (file)
@@ -12,25 +12,41 @@ import CallbackList from "../CallbackList";
 
 const _callbackList = new CallbackList();
 
-const UiCloseOverlay = {
-  /**
-   * @see CallbackList.add
-   */
-  add: _callbackList.add.bind(_callbackList),
-
-  /**
-   * @see CallbackList.remove
-   */
-  remove: _callbackList.remove.bind(_callbackList),
-
-  /**
-   * Invokes all registered callbacks.
-   */
-  execute(): void {
-    _callbackList.forEach(null, (callback) => callback());
-  },
-};
+export enum Origin {
+  Document = "document",
+  DropDown = "dropdown",
+}
+
+type Callback = (origin?: string | Origin, identifier?: string) => void;
+
+let hasGlobalListener = false;
+export function add(identifier: string, callback: Callback): void {
+  _callbackList.add(identifier, callback);
+
+  if (!hasGlobalListener) {
+    document.body.addEventListener("click", () => {
+      execute(Origin.Document);
+    });
 
-document.body.addEventListener("click", () => UiCloseOverlay.execute());
+    hasGlobalListener = true;
+  }
+}
 
-export = UiCloseOverlay;
+export function remove(identifier: string): void {
+  _callbackList.remove(identifier);
+}
+
+export function execute(): void;
+export function execute(origin: string | Origin): void;
+export function execute(origin: string | Origin, identifier: string): void;
+export function execute(origin?: string | Origin, identifier?: string): void {
+  _callbackList.forEach(null, (callback) => callback(origin, identifier));
+}
+
+// This is required for the backwards compatibility with WSC <= 5.4.
+const UiCloseOverlay = {
+  add,
+  remove,
+  execute,
+};
+export default UiCloseOverlay;
index fa5e40cb6647bb7944f5ed649a3f82d3587d654f..41e94ad8297a40460b584ddbca69aa9d526c5805 100644 (file)
@@ -14,7 +14,7 @@ import DomChangeListener from "../../Dom/Change/Listener";
 import * as DomTraverse from "../../Dom/Traverse";
 import DomUtil from "../../Dom/Util";
 import * as UiAlignment from "../Alignment";
-import UiCloseOverlay from "../CloseOverlay";
+import UiCloseOverlay, { Origin } from "../CloseOverlay";
 import { AllowFlip } from "../Alignment";
 import { NotificationAction, NotificationCallback } from "./Data";
 
@@ -95,13 +95,6 @@ function toggle(
   alternateElement?: HTMLElement,
   disableAutoFocus?: boolean,
 ): boolean {
-  _blockCloseAll = true;
-  try {
-    UiCloseOverlay.execute();
-  } finally {
-    _blockCloseAll = false;
-  }
-
   let isKeyboardClick = false;
   if (event !== null) {
     event.preventDefault();
@@ -120,6 +113,13 @@ function toggle(
     }
   }
 
+  _blockCloseAll = true;
+  try {
+    UiCloseOverlay.execute(Origin.DropDown, targetId!);
+  } finally {
+    _blockCloseAll = false;
+  }
+
   let dropdown = _dropdowns.get(targetId!) as HTMLElement;
   let preventToggle = false;
   if (dropdown !== undefined) {
index 642dc7db035a3097f0a5070bfb77d82239ffe1c0..2f98c216eadf75396c29c51a55d05717d6a52b3d 100644 (file)
@@ -11,7 +11,7 @@ import * as Core from "../Core";
 import DomChangeListener from "../Dom/Change/Listener";
 import * as Environment from "../Environment";
 import * as UiAlignment from "./Alignment";
-import UiCloseOverlay from "./CloseOverlay";
+import UiCloseOverlay, { Origin } from "./CloseOverlay";
 import * as UiDropdownReusable from "./Dropdown/Reusable";
 import { closeSearchBar, openSearchBar } from "./Page/Header/Fixed";
 import { PageMenuMain } from "./Page/Menu/Main";
@@ -82,6 +82,8 @@ function initSearchButton(): void {
   });
 
   searchBar.addEventListener("click", (event) => {
+    event.stopPropagation();
+
     if (event.target === searchBar) {
       event.preventDefault();
 
@@ -92,7 +94,14 @@ function initSearchButton(): void {
     }
   });
 
-  UiCloseOverlay.add("WoltLabSuite/Core/Ui/MobileSearch", () => {
+  UiCloseOverlay.add("WoltLabSuite/Core/Ui/MobileSearch", (origin, identifier) => {
+    if (origin === Origin.DropDown) {
+      const button = document.getElementById("pageHeaderSearchTypeSelect")!;
+      if (button.dataset.target === identifier) {
+        return;
+      }
+    }
+
     closeSearch(searchBar, scrollTop);
     closeSearchBar();
 
index 1295f3396d5a3f1ccf8a23af9b0c9e86d3a792ef..ae30b0f8a9cd9b7f45158461e2260d674e8459d5 100644 (file)
@@ -9,7 +9,7 @@
 
 import * as EventHandler from "../../../Event/Handler";
 import * as UiAlignment from "../../Alignment";
-import UiCloseOverlay from "../../CloseOverlay";
+import UiCloseOverlay, { Origin } from "../../CloseOverlay";
 import UiDropdownSimple from "../../Dropdown/Simple";
 import * as UiScreen from "../../Screen";
 
@@ -45,7 +45,14 @@ function initSearchBar(): void {
     }
   });
 
-  UiCloseOverlay.add("WoltLabSuite/Core/Ui/Page/Header/Fixed", () => {
+  UiCloseOverlay.add("WoltLabSuite/Core/Ui/Page/Header/Fixed", (origin, identifier) => {
+    if (origin === Origin.DropDown) {
+      const button = document.getElementById("pageHeaderSearchTypeSelect")!;
+      if (button.dataset.target === identifier) {
+        return;
+      }
+    }
+
     if (_pageHeader.classList.contains("searchBarForceOpen")) {
       return;
     }
index ac908495119e8caff745939c15465f3ee1be3cc1..2a71f01cc0fcc4c1362ded3694c035df864b371f 100644 (file)
@@ -7,6 +7,10 @@ import UiSearchInput from "./Input";
 
 function click(event: MouseEvent): void {
   event.preventDefault();
+  event.stopPropagation();
+
+  const searchType = document.querySelector(".pageHeaderSearchType") as HTMLElement;
+  UiDropdownSimple.close(DomUtil.identify(searchType))!;
 
   const pageHeader = document.getElementById("pageHeader") as HTMLElement;
   pageHeader.classList.add("searchBarForceOpen");
index b52bcbf01072259479c9251b8d2ef0ee8058fe5c..5d134dda6f491958daaff8e1ae86338797d15aef 100644 (file)
@@ -9,24 +9,39 @@
  */
 define(["require", "exports", "tslib", "../CallbackList"], function (require, exports, tslib_1, CallbackList_1) {
     "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.execute = exports.remove = exports.add = exports.Origin = void 0;
     CallbackList_1 = (0, tslib_1.__importDefault)(CallbackList_1);
     const _callbackList = new CallbackList_1.default();
+    var Origin;
+    (function (Origin) {
+        Origin["Document"] = "document";
+        Origin["DropDown"] = "dropdown";
+    })(Origin = exports.Origin || (exports.Origin = {}));
+    let hasGlobalListener = false;
+    function add(identifier, callback) {
+        _callbackList.add(identifier, callback);
+        if (!hasGlobalListener) {
+            document.body.addEventListener("click", () => {
+                execute(Origin.Document);
+            });
+            hasGlobalListener = true;
+        }
+    }
+    exports.add = add;
+    function remove(identifier) {
+        _callbackList.remove(identifier);
+    }
+    exports.remove = remove;
+    function execute(origin, identifier) {
+        _callbackList.forEach(null, (callback) => callback(origin, identifier));
+    }
+    exports.execute = execute;
+    // This is required for the backwards compatibility with WSC <= 5.4.
     const UiCloseOverlay = {
-        /**
-         * @see CallbackList.add
-         */
-        add: _callbackList.add.bind(_callbackList),
-        /**
-         * @see CallbackList.remove
-         */
-        remove: _callbackList.remove.bind(_callbackList),
-        /**
-         * Invokes all registered callbacks.
-         */
-        execute() {
-            _callbackList.forEach(null, (callback) => callback());
-        },
+        add,
+        remove,
+        execute,
     };
-    document.body.addEventListener("click", () => UiCloseOverlay.execute());
-    return UiCloseOverlay;
+    exports.default = UiCloseOverlay;
 });
index e90c2c7d0a1883fc3bcdf5b3bc8932b98210eae5..e1167c2d07ee264f433e7e01c01dcf13871ae5ab 100644 (file)
@@ -15,7 +15,7 @@ define(["require", "exports", "tslib", "../../CallbackList", "../../Core", "../.
     DomTraverse = (0, tslib_1.__importStar)(DomTraverse);
     Util_1 = (0, tslib_1.__importDefault)(Util_1);
     UiAlignment = (0, tslib_1.__importStar)(UiAlignment);
-    CloseOverlay_1 = (0, tslib_1.__importDefault)(CloseOverlay_1);
+    CloseOverlay_1 = (0, tslib_1.__importStar)(CloseOverlay_1);
     let _availableDropdowns;
     const _callbacks = new CallbackList_1.default();
     let _didInit = false;
@@ -87,13 +87,6 @@ define(["require", "exports", "tslib", "../../CallbackList", "../../Core", "../.
      * Toggles the drop-down's state between open and close.
      */
     function toggle(event, targetId, alternateElement, disableAutoFocus) {
-        _blockCloseAll = true;
-        try {
-            CloseOverlay_1.default.execute();
-        }
-        finally {
-            _blockCloseAll = false;
-        }
         let isKeyboardClick = false;
         if (event !== null) {
             event.preventDefault();
@@ -110,6 +103,13 @@ define(["require", "exports", "tslib", "../../CallbackList", "../../Core", "../.
                 }
             }
         }
+        _blockCloseAll = true;
+        try {
+            CloseOverlay_1.default.execute(CloseOverlay_1.Origin.DropDown, targetId);
+        }
+        finally {
+            _blockCloseAll = false;
+        }
         let dropdown = _dropdowns.get(targetId);
         let preventToggle = false;
         if (dropdown !== undefined) {
index 32bb9478f522fb2a8c745e6cb5090ccce22d9dd5..87a5b344e3000764857419174e65f0b57aa4319c 100644 (file)
@@ -14,7 +14,7 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
     Listener_1 = (0, tslib_1.__importDefault)(Listener_1);
     Environment = (0, tslib_1.__importStar)(Environment);
     UiAlignment = (0, tslib_1.__importStar)(UiAlignment);
-    CloseOverlay_1 = (0, tslib_1.__importDefault)(CloseOverlay_1);
+    CloseOverlay_1 = (0, tslib_1.__importStar)(CloseOverlay_1);
     UiDropdownReusable = (0, tslib_1.__importStar)(UiDropdownReusable);
     UiScreen = (0, tslib_1.__importStar)(UiScreen);
     let _dropdownMenu = null;
@@ -70,6 +70,7 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
             }
         });
         searchBar.addEventListener("click", (event) => {
+            event.stopPropagation();
             if (event.target === searchBar) {
                 event.preventDefault();
                 closeSearch(searchBar, scrollTop);
@@ -77,7 +78,13 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
                 searchButton.setAttribute("aria-expanded", "false");
             }
         });
-        CloseOverlay_1.default.add("WoltLabSuite/Core/Ui/MobileSearch", () => {
+        CloseOverlay_1.default.add("WoltLabSuite/Core/Ui/MobileSearch", (origin, identifier) => {
+            if (origin === CloseOverlay_1.Origin.DropDown) {
+                const button = document.getElementById("pageHeaderSearchTypeSelect");
+                if (button.dataset.target === identifier) {
+                    return;
+                }
+            }
             closeSearch(searchBar, scrollTop);
             (0, Fixed_1.closeSearchBar)();
             searchButton.setAttribute("aria-expanded", "false");
index b55cdeff55f52c2bab210ba1351370d83ddd727d..2ffd9d2b3924f482465dbdc1dd98fbdedd5b9be2 100644 (file)
@@ -12,7 +12,7 @@ define(["require", "exports", "tslib", "../../../Event/Handler", "../../Alignmen
     exports.init = exports.closeSearchBar = exports.openSearchBar = void 0;
     EventHandler = (0, tslib_1.__importStar)(EventHandler);
     UiAlignment = (0, tslib_1.__importStar)(UiAlignment);
-    CloseOverlay_1 = (0, tslib_1.__importDefault)(CloseOverlay_1);
+    CloseOverlay_1 = (0, tslib_1.__importStar)(CloseOverlay_1);
     Simple_1 = (0, tslib_1.__importDefault)(Simple_1);
     UiScreen = (0, tslib_1.__importStar)(UiScreen);
     let _isMobile = false;
@@ -42,7 +42,13 @@ define(["require", "exports", "tslib", "../../../Event/Handler", "../../Alignmen
                 openSearchBar();
             }
         });
-        CloseOverlay_1.default.add("WoltLabSuite/Core/Ui/Page/Header/Fixed", () => {
+        CloseOverlay_1.default.add("WoltLabSuite/Core/Ui/Page/Header/Fixed", (origin, identifier) => {
+            if (origin === CloseOverlay_1.Origin.DropDown) {
+                const button = document.getElementById("pageHeaderSearchTypeSelect");
+                if (button.dataset.target === identifier) {
+                    return;
+                }
+            }
             if (_pageHeader.classList.contains("searchBarForceOpen")) {
                 return;
             }
index 77665bb2f7425c786c41c9996362ad73b1e3590f..382a4dbed06ae1e2dfcff83bc66fea06b6348870 100644 (file)
@@ -10,6 +10,9 @@ define(["require", "exports", "tslib", "../../Core", "../../Dom/Traverse", "../.
     Input_1 = (0, tslib_1.__importDefault)(Input_1);
     function click(event) {
         event.preventDefault();
+        event.stopPropagation();
+        const searchType = document.querySelector(".pageHeaderSearchType");
+        Simple_1.default.close(Util_1.default.identify(searchType));
         const pageHeader = document.getElementById("pageHeader");
         pageHeader.classList.add("searchBarForceOpen");
         window.setTimeout(() => {