Add cache reset and enabled state handling
authorAlexander Ebert <ebert@woltlab.com>
Thu, 25 Jan 2024 11:35:24 +0000 (12:35 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 25 Jan 2024 15:29:59 +0000 (16:29 +0100)
ts/WoltLabSuite/Core/Component/Popover.ts
ts/WoltLabSuite/Core/Component/Popover/SharedCache.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Component/Popover.js
wcfsetup/install/files/js/WoltLabSuite/Core/Component/Popover/SharedCache.js
wcfsetup/install/files/style/ui/popover.scss

index 10b5d0e147879b388e4a4bbb719490b7cc7e7993..6addd292ce595d9dea533409c9d43b44b46deabf 100644 (file)
@@ -1,3 +1,13 @@
+/**
+ * Customizable popover overlays that show additional information after a short
+ * delay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.1
+ */
+
 import DomUtil from "../Dom/Util";
 import { getPageOverlayContainer } from "../Helper/PageOverlay";
 import { wheneverFirstSeen } from "../Helper/Selector";
@@ -46,6 +56,10 @@ class Popover {
   }
 
   #showPopover(): void {
+    if (!this.#enabled) {
+      return;
+    }
+
     this.#timerHide?.stop();
 
     if (this.#timerShouldShow === undefined) {
@@ -68,6 +82,10 @@ class Popover {
   }
 
   #hidePopover(): void {
+    if (!this.#enabled) {
+      return;
+    }
+
     this.#timerShouldShow?.stop();
 
     if (this.#timerHide === undefined) {
@@ -83,6 +101,8 @@ class Popover {
 
   #setEnabled(enabled: boolean): void {
     this.#enabled = enabled;
+
+    this.#container?.setAttribute("aria-hidden", "true");
   }
 
   #getObjectId(): number {
@@ -101,6 +121,13 @@ class Popover {
           this.#container!.remove();
         }
       });
+
+      this.#container.addEventListener("mouseenter", () => {
+        this.#timerHide?.stop();
+      });
+      this.#container.addEventListener("mouseleave", () => {
+        this.#hidePopover();
+      });
     }
 
     if (this.#container.parentNode === null) {
@@ -117,12 +144,20 @@ type Configuration = {
   selector: string;
 };
 
+const cacheByIdentifier = new Map<string, SharedCache>();
+
 export function setupFor(configuration: Configuration): void {
   const { identifier, endpoint, selector } = configuration;
 
   const cache = new SharedCache(endpoint);
+  cacheByIdentifier.set(identifier, cache);
 
   wheneverFirstSeen(selector, (element) => {
+    // Disregard elements nested inside a popover.
+    if (element.closest(".popoverContainer") !== null) {
+      return;
+    }
+
     element.addEventListener(
       "mouseenter",
       () => {
@@ -132,3 +167,7 @@ export function setupFor(configuration: Configuration): void {
     );
   });
 }
+
+export function resetCache(identifier: string, objectId: number): void {
+  cacheByIdentifier.get(identifier)!.reset(objectId);
+}
index c39b91da43e2b22727cb8d88fa307d7daad9decf..7e8eb3128828a9da1776ca0b8708beac48798a15 100644 (file)
@@ -1,3 +1,12 @@
+/**
+ * Shared cache for popover instances serving the same selector.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.1
+ */
+
 import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
 
 type ObjectId = number;
@@ -28,6 +37,10 @@ export class SharedCache {
 
     return content;
   }
+
+  reset(objectId: ObjectId): void {
+    this.#data.delete(objectId);
+  }
 }
 
 export default SharedCache;
index 1753fff5a83d7696db73ccdf441f30459f704224..3b30e185c3df9c94fcb28fa705fe63f1c76edaf9 100644 (file)
@@ -1,7 +1,16 @@
+/**
+ * Customizable popover overlays that show additional information after a short
+ * delay.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.1
+ */
 define(["require", "exports", "tslib", "../Dom/Util", "../Helper/PageOverlay", "../Helper/Selector", "../Timer/Repeating", "../Ui/Alignment", "./Popover/SharedCache"], function (require, exports, tslib_1, Util_1, PageOverlay_1, Selector_1, Repeating_1, UiAlignment, SharedCache_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
-    exports.setupFor = void 0;
+    exports.resetCache = exports.setupFor = void 0;
     Util_1 = tslib_1.__importDefault(Util_1);
     Repeating_1 = tslib_1.__importDefault(Repeating_1);
     UiAlignment = tslib_1.__importStar(UiAlignment);
@@ -35,6 +44,9 @@ define(["require", "exports", "tslib", "../Dom/Util", "../Helper/PageOverlay", "
             this.#showPopover();
         }
         #showPopover() {
+            if (!this.#enabled) {
+                return;
+            }
             this.#timerHide?.stop();
             if (this.#timerShouldShow === undefined) {
                 this.#timerShouldShow = new Repeating_1.default((timer) => {
@@ -53,6 +65,9 @@ define(["require", "exports", "tslib", "../Dom/Util", "../Helper/PageOverlay", "
             }
         }
         #hidePopover() {
+            if (!this.#enabled) {
+                return;
+            }
             this.#timerShouldShow?.stop();
             if (this.#timerHide === undefined) {
                 this.#timerHide = new Repeating_1.default((timer) => {
@@ -66,6 +81,7 @@ define(["require", "exports", "tslib", "../Dom/Util", "../Helper/PageOverlay", "
         }
         #setEnabled(enabled) {
             this.#enabled = enabled;
+            this.#container?.setAttribute("aria-hidden", "true");
         }
         #getObjectId() {
             return parseInt(this.#element.dataset.objectId);
@@ -81,6 +97,12 @@ define(["require", "exports", "tslib", "../Dom/Util", "../Helper/PageOverlay", "
                         this.#container.remove();
                     }
                 });
+                this.#container.addEventListener("mouseenter", () => {
+                    this.#timerHide?.stop();
+                });
+                this.#container.addEventListener("mouseleave", () => {
+                    this.#hidePopover();
+                });
             }
             if (this.#container.parentNode === null) {
                 (0, PageOverlay_1.getPageOverlayContainer)().append(this.#container);
@@ -88,14 +110,24 @@ define(["require", "exports", "tslib", "../Dom/Util", "../Helper/PageOverlay", "
             return this.#container;
         }
     }
+    const cacheByIdentifier = new Map();
     function setupFor(configuration) {
         const { identifier, endpoint, selector } = configuration;
         const cache = new SharedCache_1.default(endpoint);
+        cacheByIdentifier.set(identifier, cache);
         (0, Selector_1.wheneverFirstSeen)(selector, (element) => {
+            // Disregard elements nested inside a popover.
+            if (element.closest(".popoverContainer") !== null) {
+                return;
+            }
             element.addEventListener("mouseenter", () => {
                 new Popover(cache, element, identifier);
             }, { once: true });
         });
     }
     exports.setupFor = setupFor;
+    function resetCache(identifier, objectId) {
+        cacheByIdentifier.get(identifier).reset(objectId);
+    }
+    exports.resetCache = resetCache;
 });
index 03ffc150ec753c1139ffa6e0826756aaec8e4d71..ae9f172edc7b5deb7c64359495a836022e3d11e2 100644 (file)
@@ -1,3 +1,11 @@
+/**
+ * Shared cache for popover instances serving the same selector.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.1
+ */
 define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend"], function (require, exports, Backend_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
@@ -22,6 +30,9 @@ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend"], function (requi
             this.#data.set(objectId, content);
             return content;
         }
+        reset(objectId) {
+            this.#data.delete(objectId);
+        }
     }
     exports.SharedCache = SharedCache;
     exports.default = SharedCache;
index 77c374faccfbbf2fb16ea0a3da66f4c9425cf35f..68a286efd67e8579d11376eccf2059d0a31c9cb8 100644 (file)
@@ -89,6 +89,7 @@
        border-radius: var(--wcfBorderRadius);
        box-shadow: var(--wcfBoxShadow);
        color: var(--wcfContentText);
+       display: none;
        max-height: 300px;
        max-width: 500px;
        opacity: 0;
                transform: translateY(0);
        }
 }
+
+@media (hover: hover) {
+       .popoverContainer {
+               display: block;
+       }
+}