Render quote tab dynamic
authorCyperghost <olaf_schmitz_1@t-online.de>
Tue, 17 Dec 2024 12:48:11 +0000 (13:48 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Tue, 17 Dec 2024 12:48:11 +0000 (13:48 +0100)
com.woltlab.wcf/templates/__messageFormQuote.tpl
com.woltlab.wcf/templates/__messageFormQuoteInline.tpl
com.woltlab.wcf/templates/messageFormTabs.tpl
com.woltlab.wcf/templates/messageFormTabsInline.tpl
ts/WoltLabSuite/Core/Component/Message/MessageTabMenu.ts
ts/WoltLabSuite/Core/Component/Quote/List.ts [new file with mode: 0644]
wcfsetup/install/files/js/WoltLabSuite/Core/Component/Message/MessageTabMenu.js
wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/List.js [new file with mode: 0644]
wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php

index 30e8947507e6a8b761b7e28cb0aa1abdd0228744..d4b635e1060efdf5799f807aaaed163eea056039 100644 (file)
@@ -1,5 +1,11 @@
 {* TODO *}
 
-<div id="quote-{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}" class="jsOnly messageTabMenuContent">
+<div id="quotes_{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}" class="messageTabMenuContent">
 
 </div>
+
+<script data-relocate="true">
+  require(["WoltLabSuite/Core/Component/Quote/List"], ({ setup }) => {
+       setup("{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}");
+  });
+</script>
index 30e8947507e6a8b761b7e28cb0aa1abdd0228744..d4b635e1060efdf5799f807aaaed163eea056039 100644 (file)
@@ -1,5 +1,11 @@
 {* TODO *}
 
-<div id="quote-{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}" class="jsOnly messageTabMenuContent">
+<div id="quotes_{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}" class="messageTabMenuContent">
 
 </div>
+
+<script data-relocate="true">
+  require(["WoltLabSuite/Core/Component/Quote/List"], ({ setup }) => {
+       setup("{if $wysiwygSelector|isset}{$wysiwygSelector}{else}text{/if}");
+  });
+</script>
index ee2625ec7214fad896a884d4f2cb87a115649dd4..0044e7b25f5e78f9e1a996584aece9888d88d913 100644 (file)
@@ -9,9 +9,8 @@
                                <li data-name="attachments"><button type="button">{icon name='paperclip'} <span>{lang}wcf.attachment.attachments{/lang}</span></button></li>
                        {/if}
                        {if $__messageFormSettings}<li data-name="settings"><button type="button">{icon name='gear'} <span>{lang}wcf.message.settings{/lang}</span></button></li>{/if}
-                       <li data-name="quote">
-                               {* TODO change count *}
-                               <button type="button">{icon name='quote-left'} <span>{lang count=10}wcf.message.quote.showQuotes{/lang}</span></button>
+                       <li data-name="quotes" hidden>
+                               <button type="button">{icon name='quote-left'} <span>{lang}wcf.bbcode.quote{/lang}</span></button>
                        </li>
                        {if $__showPoll|isset && $__showPoll}<li data-name="poll"><button type="button">{icon name='chart-bar'} <span>{lang}wcf.poll.management{/lang}</span></button></li>{/if}
                        {event name='tabMenuTabs'}
index c84fa26a825f48d2fa80900718cc2b8477ac9024..82728fce2ae4bedf5637b26bfb38abaa0b4a373c 100644 (file)
@@ -13,9 +13,8 @@
                                <li data-name="attachments"><button type="button">{icon name='paperclip'} <span>{lang}wcf.attachment.attachments{/lang}</span></button></li>
                        {/if}
                        {if $__messageFormSettingsInlineContent}<li data-name="settings"><button type="button">{icon name='gear'} <span>{lang}wcf.message.settings{/lang}</span></button></li>{/if}
-                       <li data-name="quote">
-                               {* TODO change count *}
-                               <button type="button">{icon name='quote-left'} <span>{lang count=10}wcf.message.quote.showQuotes{/lang}</span></button>
+                       <li data-name="quotes" hidden>
+                               <button type="button">{icon name='quote-left'} <span>{lang}wcf.bbcode.quote{/lang}</span></button>
                        </li>
                        {if $__showPoll|isset && $__showPoll}<li data-name="poll"><button type="button">{icon name='chart-bar'} <span>{lang}wcf.poll.management{/lang}</span></button></li>{/if}
                        {event name='tabMenuTabs'}
index 0c87f8f37032acd29b361e6b543c86f045a0b7e8..37dfd7c4549cebb27838d7a73c9fd9247c491d7c 100644 (file)
@@ -65,6 +65,31 @@ class TabMenu {
     this.#activeTabName = tabName;
   }
 
+  showTab(tabName: string, title?: string): void {
+    this.#tabs
+      .filter((element) => element.dataset.name === tabName)
+      .forEach((element) => {
+        element.hidden = false;
+
+        // Set new title
+        if (title) {
+          element.querySelector("span")!.textContent = title;
+        }
+      });
+  }
+
+  hideTab(tabName: string): void {
+    this.#tabs
+      .filter((element) => element.dataset.name === tabName)
+      .forEach((element) => {
+        element.hidden = true;
+
+        if (element.classList.contains("active")) {
+          this.#closeAllTabs();
+        }
+      });
+  }
+
   #init(): void {
     for (let i = 0; i < this.#tabs.length; i++) {
       const tab = this.#tabs[i];
diff --git a/ts/WoltLabSuite/Core/Component/Quote/List.ts b/ts/WoltLabSuite/Core/Component/Quote/List.ts
new file mode 100644 (file)
index 0000000..27604f2
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * Handles quotes for CKEditor 5 message fields.
+ *
+ * @author Olaf Braun
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.2
+ */
+import * as Core from "WoltLabSuite/Core/Core";
+import { listenToCkeditor } from "WoltLabSuite/Core/Component/Ckeditor/Event";
+import type { CKEditor } from "WoltLabSuite/Core/Component/Ckeditor";
+import { getTabMenu } from "WoltLabSuite/Core/Component/Message/MessageTabMenu";
+import { getPhrase } from "WoltLabSuite/Core/Language";
+
+export const STORAGE_KEY = Core.getStoragePrefix() + "quotes";
+const quoteLists = new Map<string, QuoteList>();
+
+class QuoteList {
+  #container: HTMLElement;
+  #editor: CKEditor;
+  #editorId: string;
+
+  constructor(editorId: string, editor: CKEditor) {
+    this.#editorId = editorId;
+    this.#editor = editor;
+    this.#container = document.getElementById(`quotes_${editorId}`)!;
+    if (this.#container === null) {
+      throw new Error(`The quotes container for '${editorId}' does not exist.`);
+    }
+
+    window.addEventListener("storage", (event) => {
+      if (event.key !== STORAGE_KEY) {
+        return;
+      }
+
+      this.renderQuotes(event.newValue);
+    });
+
+    this.renderQuotes(window.localStorage.getItem(STORAGE_KEY));
+  }
+
+  public renderQuotes(template: string | null): void {
+    this.#container.innerHTML = template || "";
+
+    if (template) {
+      getTabMenu(this.#editorId)?.showTab(
+        "quotes",
+        getPhrase("wcf.message.quote.showQuotes", {
+          count: this.#container.childElementCount,
+        }),
+      );
+    } else {
+      getTabMenu(this.#editorId)?.hideTab("quotes");
+    }
+  }
+}
+
+export function getQuoteList(editorId: string): QuoteList | undefined {
+  return quoteLists.get(editorId);
+}
+
+export function setup(editorId: string): void {
+  if (quoteLists.has(editorId)) {
+    return;
+  }
+
+  const editor = document.getElementById(editorId);
+  if (editor === null) {
+    throw new Error(`The editor '${editorId}' does not exist.`);
+  }
+
+  listenToCkeditor(editor).ready(({ ckeditor }) => {
+    if (ckeditor.features.quoteBlock) {
+      quoteLists.set(editorId, new QuoteList(editorId, ckeditor));
+    }
+  });
+}
index 84ec5d2ddc97e9f3c59d7d0f7a073218714fa09c..9cf054b3e211f94ed310827aa04dbae75ab45e51 100644 (file)
@@ -53,6 +53,27 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Helper/Selector", "Wol
             this.#tabContainers[tabIndex].classList.add("active");
             this.#activeTabName = tabName;
         }
+        showTab(tabName, title) {
+            this.#tabs
+                .filter((element) => element.dataset.name === tabName)
+                .forEach((element) => {
+                element.hidden = false;
+                // Set new title
+                if (title) {
+                    element.querySelector("span").textContent = title;
+                }
+            });
+        }
+        hideTab(tabName) {
+            this.#tabs
+                .filter((element) => element.dataset.name === tabName)
+                .forEach((element) => {
+                element.hidden = true;
+                if (element.classList.contains("active")) {
+                    this.#closeAllTabs();
+                }
+            });
+        }
         #init() {
             for (let i = 0; i < this.#tabs.length; i++) {
                 const tab = this.#tabs[i];
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/List.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Quote/List.js
new file mode 100644 (file)
index 0000000..c00ea38
--- /dev/null
@@ -0,0 +1,58 @@
+define(["require", "exports", "tslib", "WoltLabSuite/Core/Core", "WoltLabSuite/Core/Component/Ckeditor/Event", "WoltLabSuite/Core/Component/Message/MessageTabMenu", "WoltLabSuite/Core/Language"], function (require, exports, tslib_1, Core, Event_1, MessageTabMenu_1, Language_1) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.STORAGE_KEY = void 0;
+    exports.getQuoteList = getQuoteList;
+    exports.setup = setup;
+    Core = tslib_1.__importStar(Core);
+    exports.STORAGE_KEY = Core.getStoragePrefix() + "quotes";
+    const quoteLists = new Map();
+    class QuoteList {
+        #container;
+        #editor;
+        #editorId;
+        constructor(editorId, editor) {
+            this.#editorId = editorId;
+            this.#editor = editor;
+            this.#container = document.getElementById(`quotes_${editorId}`);
+            if (this.#container === null) {
+                throw new Error(`The quotes container for '${editorId}' does not exist.`);
+            }
+            window.addEventListener("storage", (event) => {
+                if (event.key !== exports.STORAGE_KEY) {
+                    return;
+                }
+                this.renderQuotes(event.newValue);
+            });
+            this.renderQuotes(window.localStorage.getItem(exports.STORAGE_KEY));
+        }
+        renderQuotes(template) {
+            this.#container.innerHTML = template || "";
+            if (template) {
+                (0, MessageTabMenu_1.getTabMenu)(this.#editorId)?.showTab("quotes", (0, Language_1.getPhrase)("wcf.message.quote.showQuotes", {
+                    count: this.#container.childElementCount,
+                }));
+            }
+            else {
+                (0, MessageTabMenu_1.getTabMenu)(this.#editorId)?.hideTab("quotes");
+            }
+        }
+    }
+    function getQuoteList(editorId) {
+        return quoteLists.get(editorId);
+    }
+    function setup(editorId) {
+        if (quoteLists.has(editorId)) {
+            return;
+        }
+        const editor = document.getElementById(editorId);
+        if (editor === null) {
+            throw new Error(`The editor '${editorId}' does not exist.`);
+        }
+        (0, Event_1.listenToCkeditor)(editor).ready(({ ckeditor }) => {
+            if (ckeditor.features.quoteBlock) {
+                quoteLists.set(editorId, new QuoteList(editorId, ckeditor));
+            }
+        });
+    }
+});
index 6bced647c05f3a51a7a25f0c9c98d2d471185a4a..54aa1975fd0779c22c830beee99b7421d971c74d 100644 (file)
@@ -136,6 +136,8 @@ final class PreloadPhrasesCollectingListener
         $event->preload('wcf.message.share.permalink.html');
         $event->preload('wcf.message.share.socialMedia');
 
+        $event->preload('wcf.message.quote.showQuotes');
+
         $event->preload('wcf.moderation.report.reportContent');
 
         $event->preload('wcf.page.jumpTo');