Foundation for new text content popovers
authorMarcel Werk <burntime@woltlab.com>
Thu, 2 Jan 2025 12:01:24 +0000 (13:01 +0100)
committerMarcel Werk <burntime@woltlab.com>
Thu, 2 Jan 2025 12:01:24 +0000 (13:01 +0100)
ts/WoltLabSuite/Core/Component/Popover.ts
ts/WoltLabSuite/Core/Component/Popover/SharedCache.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Component/Popover/SharedCache.js
wcfsetup/install/files/style/ui/popover.scss

index e0bd47174cf0a257224a561b5b3a9521b380068a..e1fdff011f5982e075e2ca96f3e585830f43e44f 100644 (file)
@@ -143,7 +143,7 @@ class Popover {
 }
 
 type Configuration = {
-  endpoint: string;
+  endpoint: string | ((objectId: number) => Promise<string>);
   identifier: string;
   selector: string;
 };
index 7e8eb3128828a9da1776ca0b8708beac48798a15..ee8017d8fa38c1583397a3f4b14c183cb9c90c80 100644 (file)
@@ -13,10 +13,23 @@ type ObjectId = number;
 
 export class SharedCache {
   readonly #data = new Map<ObjectId, string>();
-  readonly #endpoint: URL;
-
-  constructor(endpoint: string) {
-    this.#endpoint = new URL(endpoint);
+  readonly #callback: (objectId: number) => Promise<string>;
+
+  constructor(endpoint: string | ((objectId: number) => Promise<string>)) {
+    if (typeof endpoint === "string") {
+      this.#callback = async (objectId: number) => {
+        const url = new URL(endpoint);
+        url.searchParams.set("id", objectId.toString());
+        const response = await prepareRequest(url).get().fetchAsResponse();
+        if (!response?.ok) {
+          return "";
+        }
+
+        return await response.text();
+      };
+    } else {
+      this.#callback = endpoint;
+    }
   }
 
   async get(objectId: ObjectId): Promise<string> {
@@ -25,14 +38,7 @@ export class SharedCache {
       return content;
     }
 
-    this.#endpoint.searchParams.set("id", objectId.toString());
-
-    const response = await prepareRequest(this.#endpoint).get().fetchAsResponse();
-    if (!response?.ok) {
-      return "";
-    }
-
-    content = await response.text();
+    content = await this.#callback(objectId);
     this.#data.set(objectId, content);
 
     return content;
index ae9f172edc7b5deb7c64359495a836022e3d11e2..c9e47b4f120ffb9b5bf21c9f94d394323dba3a86 100644 (file)
@@ -12,21 +12,29 @@ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend"], function (requi
     exports.SharedCache = void 0;
     class SharedCache {
         #data = new Map();
-        #endpoint;
+        #callback;
         constructor(endpoint) {
-            this.#endpoint = new URL(endpoint);
+            if (typeof endpoint === "string") {
+                this.#callback = async (objectId) => {
+                    const url = new URL(endpoint);
+                    url.searchParams.set("id", objectId.toString());
+                    const response = await (0, Backend_1.prepareRequest)(url).get().fetchAsResponse();
+                    if (!response?.ok) {
+                        return "";
+                    }
+                    return await response.text();
+                };
+            }
+            else {
+                this.#callback = endpoint;
+            }
         }
         async get(objectId) {
             let content = this.#data.get(objectId);
             if (content !== undefined) {
                 return content;
             }
-            this.#endpoint.searchParams.set("id", objectId.toString());
-            const response = await (0, Backend_1.prepareRequest)(this.#endpoint).get().fetchAsResponse();
-            if (!response?.ok) {
-                return "";
-            }
-            content = await response.text();
+            content = await this.#callback(objectId);
             this.#data.set(objectId, content);
             return content;
         }
index 3e8b41abc7a65c300d9038fd1a3217aff60d25a7..e529e912179a1fc36f24cf880a5e2bed0e3e04b5 100644 (file)
@@ -95,6 +95,7 @@
        max-height: var(--maxHeight);
        max-width: var(--maxWidth);
        opacity: 0;
+       overflow: hidden;
        padding: var(--padding);
        position: absolute;
        transform: translateY(-20px);
                display: block;
        }
 }
+
+.popover__layout {
+       display: flex;
+       flex-direction: column;
+       gap: 10px;
+}
+
+.popover__header {
+       display: grid;
+       grid-template-areas:
+               "avatar title"
+               "avatar time";
+       grid-template-columns: min-content 1fr;
+       grid-template-rows: min-content 1fr;
+       column-gap: 10px;
+       margin-bottom: 10px;
+}
+
+.popover__avatar {
+       grid-area: avatar;
+}
+
+.popover__title {
+       grid-area: title;
+       font-weight: 600;
+       @include wcfFontHeadline;
+}
+
+.popover__title a,
+.popover__title a:hover {
+       color: inherit;
+}
+
+.popover__time {
+       color: var(--wcfContentDimmedText);
+       grid-area: time;
+       @include wcfFontSmall;
+}