From 2a34443f7384c816372612b1259d767e93b49d64 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 8 Jan 2025 17:17:19 +0100 Subject: [PATCH] Add support for counters for message tab menus --- com.woltlab.wcf/templates/messageFormTabs.tpl | 48 ++++++++++++++---- .../templates/messageFormTabsInline.tpl | 49 +++++++++++++++---- .../Core/Component/Attachment/List.ts | 21 ++++++++ .../Core/Component/Message/MessageTabMenu.ts | 32 ++++++++++++ .../Core/Component/Attachment/List.js | 18 +++++++ .../Core/Component/Message/MessageTabMenu.js | 23 +++++++++ .../files/style/ui/tabMenuMessage.scss | 12 ++++- 7 files changed, 182 insertions(+), 21 deletions(-) diff --git a/com.woltlab.wcf/templates/messageFormTabs.tpl b/com.woltlab.wcf/templates/messageFormTabs.tpl index 1aa77e1081..bfc4726dc0 100644 --- a/com.woltlab.wcf/templates/messageFormTabs.tpl +++ b/com.woltlab.wcf/templates/messageFormTabs.tpl @@ -1,26 +1,56 @@ {* the settings template does not generate direct ouput anymore, but captures it content *} {include file='messageFormSettings'} -
+
- + {if MODULE_SMILEY && !$smileyCategories|empty}{include file='shared_messageFormSmileyTab'}{/if} {if !$attachmentHandler|empty && $attachmentHandler->canUpload()} {include file='shared_messageFormAttachments'} {/if} - + {if $__messageFormSettings}{unsafe:$__messageFormSettings}{/if} {include file='__messageFormPoll'} - + {event name='tabMenuContents'} -
+
\ No newline at end of file diff --git a/com.woltlab.wcf/templates/messageFormTabsInline.tpl b/com.woltlab.wcf/templates/messageFormTabsInline.tpl index 9f81bf37f8..71c23dbcb2 100644 --- a/com.woltlab.wcf/templates/messageFormTabsInline.tpl +++ b/com.woltlab.wcf/templates/messageFormTabsInline.tpl @@ -5,27 +5,56 @@ {capture assign='__messageFormSettingsInlineContent'}{include file='messageFormSettingsInline'}{/capture} {assign var='__messageFormSettingsInlineContent' value=$__messageFormSettingsInlineContent|trim} -
+
- + {if MODULE_SMILEY && !$smileyCategories|empty}{include file='shared_messageFormSmileyTab'}{/if} {if !$attachmentHandler|empty && $attachmentHandler->canUpload()} {include file='shared_messageFormAttachments'} {/if} - + {if $__messageFormSettingsInlineContent}{@$__messageFormSettingsInlineContent}{/if} - + {include file='__messageFormPollInline'} - + {event name='tabMenuContents'} -
+
\ No newline at end of file diff --git a/ts/WoltLabSuite/Core/Component/Attachment/List.ts b/ts/WoltLabSuite/Core/Component/Attachment/List.ts index 46a84b5715..2fdf779c15 100644 --- a/ts/WoltLabSuite/Core/Component/Attachment/List.ts +++ b/ts/WoltLabSuite/Core/Component/Attachment/List.ts @@ -18,6 +18,11 @@ export function setup(editorId: string): void { throw new Error(`The attachments container for '${editorId}' does not exist.`); } + const tabMenu = getTabMenu(editorId); + if (tabMenu === undefined) { + throw new Error("Unable to find the corresponding tab menu."); + } + const editor = document.getElementById(editorId); if (editor === null) { throw new Error(`The editor element for '${editorId}' does not exist.`); @@ -85,4 +90,20 @@ export function setup(editorId: string): void { existingFiles.remove(); } + + const files = fileList.getElementsByTagName("woltlab-core-file"); + const observer = new MutationObserver(() => { + let counter = 0; + for (const file of files) { + if (!file.isFailedUpload()) { + counter++; + } + } + + tabMenu.setTabCounter("attachments", counter); + }); + observer.observe(fileList, { + childList: true, + subtree: true, + }); } diff --git a/ts/WoltLabSuite/Core/Component/Message/MessageTabMenu.ts b/ts/WoltLabSuite/Core/Component/Message/MessageTabMenu.ts index 0c87f8f370..0d3d4d7579 100644 --- a/ts/WoltLabSuite/Core/Component/Message/MessageTabMenu.ts +++ b/ts/WoltLabSuite/Core/Component/Message/MessageTabMenu.ts @@ -65,6 +65,28 @@ class TabMenu { this.#activeTabName = tabName; } + setTabCounter(tabName: string, value: number): void { + const tab = this.#tabs.find((element) => element.dataset.name === tabName); + if (tab === undefined) { + throw new Error(`Unknown tab '${tabName}'.`); + } + + let badgeUpdate = tab.querySelector(".badgeUpdate"); + if (value === 0) { + badgeUpdate?.remove(); + + return; + } + + if (badgeUpdate === null) { + badgeUpdate = document.createElement("span"); + badgeUpdate.classList.add("badge", "badgeUpdate"); + tab.querySelector("a, button")!.append(badgeUpdate); + } + + badgeUpdate.textContent = value.toString(); + } + #init(): void { for (let i = 0; i < this.#tabs.length; i++) { const tab = this.#tabs[i]; @@ -142,9 +164,19 @@ function initTabMenu(tabMenu: HTMLElement): void { } export function getTabMenu(identifier: string): TabMenu | undefined { + setup(); + return tabMenus.get(identifier); } +let initialized = false; + export function setup(): void { + if (initialized) { + return; + } + + initialized = true; + wheneverFirstSeen(".messageTabMenu", (tabMenu) => initTabMenu(tabMenu)); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/List.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/List.js index 189c882cf6..74ef52916a 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/List.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Attachment/List.js @@ -10,6 +10,10 @@ define(["require", "exports", "./Entry", "../Ckeditor/Event", "../Message/Messag if (container === null) { throw new Error(`The attachments container for '${editorId}' does not exist.`); } + const tabMenu = (0, MessageTabMenu_1.getTabMenu)(editorId); + if (tabMenu === undefined) { + throw new Error("Unable to find the corresponding tab menu."); + } const editor = document.getElementById(editorId); if (editor === null) { throw new Error(`The editor element for '${editorId}' does not exist.`); @@ -69,5 +73,19 @@ define(["require", "exports", "./Entry", "../Ckeditor/Event", "../Message/Messag }); existingFiles.remove(); } + const files = fileList.getElementsByTagName("woltlab-core-file"); + const observer = new MutationObserver(() => { + let counter = 0; + for (const file of files) { + if (!file.isFailedUpload()) { + counter++; + } + } + tabMenu.setTabCounter("attachments", counter); + }); + observer.observe(fileList, { + childList: true, + subtree: true, + }); } }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Message/MessageTabMenu.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Message/MessageTabMenu.js index 84ec5d2ddc..fcbfb6d4fe 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Message/MessageTabMenu.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Message/MessageTabMenu.js @@ -53,6 +53,23 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Helper/Selector", "Wol this.#tabContainers[tabIndex].classList.add("active"); this.#activeTabName = tabName; } + setTabCounter(tabName, value) { + const tab = this.#tabs.find((element) => element.dataset.name === tabName); + if (tab === undefined) { + throw new Error(`Unknown tab '${tabName}'.`); + } + let badgeUpdate = tab.querySelector(".badgeUpdate"); + if (value === 0) { + badgeUpdate?.remove(); + return; + } + if (badgeUpdate === null) { + badgeUpdate = document.createElement("span"); + badgeUpdate.classList.add("badge", "badgeUpdate"); + tab.querySelector("a, button").append(badgeUpdate); + } + badgeUpdate.textContent = value.toString(); + } #init() { for (let i = 0; i < this.#tabs.length; i++) { const tab = this.#tabs[i]; @@ -114,9 +131,15 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Helper/Selector", "Wol } } function getTabMenu(identifier) { + setup(); return tabMenus.get(identifier); } + let initialized = false; function setup() { + if (initialized) { + return; + } + initialized = true; (0, Selector_1.wheneverFirstSeen)(".messageTabMenu", (tabMenu) => initTabMenu(tabMenu)); } }); diff --git a/wcfsetup/install/files/style/ui/tabMenuMessage.scss b/wcfsetup/install/files/style/ui/tabMenuMessage.scss index 5ca84fa87e..ab4a8b355e 100644 --- a/wcfsetup/install/files/style/ui/tabMenuMessage.scss +++ b/wcfsetup/install/files/style/ui/tabMenuMessage.scss @@ -108,13 +108,21 @@ > a, > button { + align-items: center; color: inherit; - display: block; + /* 0.2em matches the width of a space */ + column-gap: 0.2em; + display: flex; padding: 10px 20px; @include userSelectNone; @include wcfFontDefault; + .badgeUpdate { + font-variant: tabular-nums; + margin-left: 0.3em; + } + @include screen-md-up { > .icon { display: none; @@ -126,7 +134,7 @@ display: block; } - > span:not(.icon) { + > span:not(.badgeUpdate) { display: none; } } -- 2.20.1