From 44341b88647c424279287ef9555641cd51fc3054 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Mon, 2 Nov 2020 18:10:30 +0100 Subject: [PATCH] Convert `Ui/Reaction/CountButton` to TypeScript --- global.d.ts | 14 +- .../Core/Ui/Reaction/CountButtons.js | 225 +++++++-------- .../js/WoltLabSuite/Core/Ui/Reaction/Data.js | 4 + .../Core/Ui/Reaction/CountButtons.js | 224 --------------- .../Core/Ui/Reaction/CountButtons.ts | 261 ++++++++++++++++++ .../ts/WoltLabSuite/Core/Ui/Reaction/Data.ts | 12 + 6 files changed, 402 insertions(+), 338 deletions(-) create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/Data.js delete mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/CountButtons.js create mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/CountButtons.ts create mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/Data.ts diff --git a/global.d.ts b/global.d.ts index 20766bfff5..23265d7316 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,14 +1,18 @@ -import DatePicker from './wcfsetup/install/files/ts/WoltLabSuite/Core/Date/Picker'; -import Devtools from './wcfsetup/install/files/ts/WoltLabSuite/Core/Devtools'; -import DomUtil from './wcfsetup/install/files/ts/WoltLabSuite/Core/Dom/Util'; -import * as ColorUtil from './wcfsetup/install/files/ts/WoltLabSuite/Core/ColorUtil'; -import UiDropdownSimple from './wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Dropdown/Simple'; +import DatePicker from "./wcfsetup/install/files/ts/WoltLabSuite/Core/Date/Picker"; +import Devtools from "./wcfsetup/install/files/ts/WoltLabSuite/Core/Devtools"; +import DomUtil from "./wcfsetup/install/files/ts/WoltLabSuite/Core/Dom/Util"; +import * as ColorUtil from "./wcfsetup/install/files/ts/WoltLabSuite/Core/ColorUtil"; +import UiDropdownSimple from "./wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Dropdown/Simple"; import "@woltlab/zxcvbn"; +import { Reaction } from "./wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/Data"; declare global { interface Window { Devtools?: typeof Devtools; ENABLE_DEBUG_MODE: boolean; + REACTION_TYPES: { + [key: string]: Reaction; + }; SECURITY_TOKEN: string; TIME_NOW: number; WCF_PATH: string; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/CountButtons.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/CountButtons.js index 359846bf4d..9bd0fd6c4b 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/CountButtons.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/CountButtons.js @@ -1,184 +1,191 @@ /** * Provides interface elements to use reactions. * - * @author Joshua Ruesweg - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @module WoltLabSuite/Core/Ui/Reaction/Handler + * @author Joshua Ruesweg + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Reaction/Handler * @since 5.2 */ -define([ - 'Ajax', 'Core', 'Dictionary', 'Language', - 'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util', - 'Ui/Dialog', 'EventHandler' -], function (Ajax, Core, Dictionary, Language, ObjectMap, StringUtil, DomChangeListener, DomUtil, UiDialog, EventHandler) { +define(["require", "exports", "tslib", "../../Ajax", "../../Core", "../../Dom/Change/Listener", "../../Dom/Util", "../../Event/Handler", "../../StringUtil", "../Dialog"], function (require, exports, tslib_1, Ajax, Core, Listener_1, Util_1, EventHandler, StringUtil, Dialog_1) { "use strict"; - /** - * @constructor - */ - function CountButtons(objectType, options) { this.init(objectType, options); } - CountButtons.prototype = { + Ajax = tslib_1.__importStar(Ajax); + Core = tslib_1.__importStar(Core); + Listener_1 = tslib_1.__importDefault(Listener_1); + Util_1 = tslib_1.__importDefault(Util_1); + EventHandler = tslib_1.__importStar(EventHandler); + StringUtil = tslib_1.__importStar(StringUtil); + Dialog_1 = tslib_1.__importDefault(Dialog_1); + class CountButtons { /** * Initializes the like handler. - * - * @param {string} objectType object type - * @param {object} options initialization options */ - init: function (objectType, options) { - if (options.containerSelector === '') { + constructor(objectType, opts) { + this._containers = new Map(); + this._currentObjectId = 0; + this._objects = new Map(); + if (!opts.containerSelector) { throw new Error("[WoltLabSuite/Core/Ui/Reaction/CountButtons] Expected a non-empty string for option 'containerSelector'."); } - this._containers = new Dictionary(); - this._objects = new Dictionary(); this._objectType = objectType; this._options = Core.extend({ // selectors - summaryListSelector: '.reactionSummaryList', - containerSelector: '', + summaryListSelector: ".reactionSummaryList", + containerSelector: "", isSingleItem: false, // optional parameters parameters: { - data: {} - } - }, options); - this.initContainers(options, objectType); - DomChangeListener.add('WoltLabSuite/Core/Ui/Reaction/CountButtons-' + objectType, this.initContainers.bind(this)); - }, + data: {}, + }, + }, opts); + this.initContainers(); + Listener_1.default.add(`WoltLabSuite/Core/Ui/Reaction/CountButtons-${objectType}`, () => this.initContainers()); + } /** * Initialises the containers. */ - initContainers: function () { - var element, elements = elBySelAll(this._options.containerSelector), elementData, triggerChange = false, objectId; - for (var i = 0, length = elements.length; i < length; i++) { - element = elements[i]; - if (this._containers.has(DomUtil.identify(element))) { - continue; + initContainers() { + let triggerChange = false; + document.querySelectorAll(this._options.containerSelector).forEach((element) => { + const elementId = Util_1.default.identify(element); + if (this._containers.has(elementId)) { + return; } - objectId = ~~elData(element, 'object-id'); - elementData = { + const objectId = ~~element.dataset.objectId; + const elementData = { reactButton: null, summary: null, objectId: objectId, - element: element + element: element, }; - this._containers.set(DomUtil.identify(element), elementData); + this._containers.set(elementId, elementData); this._initReactionCountButtons(element, elementData); - var objects = []; - if (this._objects.has(objectId)) { - objects = this._objects.get(objectId); - } + const objects = this._objects.get(objectId) || []; objects.push(elementData); this._objects.set(objectId, objects); triggerChange = true; - } + }); if (triggerChange) { - DomChangeListener.trigger(); + Listener_1.default.trigger(); } - }, + } /** * Update the count buttons with the given data. - * - * @param {int} objectId - * @param {object} data */ - updateCountButtons: function (objectId, data) { - var triggerChange = false; - this._objects.get(objectId).forEach(function (elementData) { - var summaryList = elBySel(this._options.summaryListSelector, this._options.isSingleItem ? undefined : elementData.element); + updateCountButtons(objectId, data) { + let triggerChange = false; + this._objects.get(objectId).forEach((elementData) => { + let summaryList; + if (this._options.isSingleItem) { + summaryList = document.querySelector(this._options.summaryListSelector); + } + else { + summaryList = elementData.element.querySelector(this._options.summaryListSelector); + } // summary list for the object not found; abort - if (summaryList === null) + if (summaryList === null) { return; - var sortedElements = {}, elements = elBySelAll('.reactCountButton', summaryList); - for (var i = 0, length = elements.length; i < length; i++) { - var reactionTypeId = elData(elements[i], 'reaction-type-id'); - if (data.hasOwnProperty(reactionTypeId)) { - sortedElements[reactionTypeId] = elements[i]; + } + const existingReactions = new Map(Object.entries(data)); + const sortedElements = new Map(); + summaryList.querySelectorAll(".reactCountButton").forEach((reaction) => { + const reactionTypeId = reaction.dataset.reactionTypeId; + if (existingReactions.has(reactionTypeId)) { + sortedElements.set(reactionTypeId, reaction); } else { // The reaction no longer has any reactions. - elRemove(elements[i]); + reaction.remove(); } - } - Object.keys(data).forEach(function (key) { - if (sortedElements[key] !== undefined) { - var reactionCount = elBySel('.reactionCount', sortedElements[key]); - reactionCount.innerHTML = StringUtil.shortUnit(data[key]); + }); + const availableReactions = new Map(Object.entries(window.REACTION_TYPES)); + existingReactions.forEach((count, reactionTypeId) => { + if (sortedElements.has(reactionTypeId)) { + const reaction = sortedElements.get(reactionTypeId); + const reactionCount = reaction.querySelector(".reactionCount"); + reactionCount.innerHTML = StringUtil.shortUnit(count); } - else if (REACTION_TYPES[key] !== undefined) { - var createdElement = elCreate('span'); - createdElement.className = 'reactCountButton'; - createdElement.innerHTML = REACTION_TYPES[key].renderedIcon; - elData(createdElement, 'reaction-type-id', key); - var countSpan = elCreate('span'); - countSpan.className = 'reactionCount'; - countSpan.innerHTML = StringUtil.shortUnit(data[key]); + else if (availableReactions.has(reactionTypeId)) { + const createdElement = document.createElement("span"); + createdElement.className = "reactCountButton"; + createdElement.innerHTML = availableReactions.get(reactionTypeId).renderedIcon; + createdElement.dataset.reactionTypeId = reactionTypeId; + const countSpan = document.createElement("span"); + countSpan.className = "reactionCount"; + countSpan.innerHTML = StringUtil.shortUnit(count); createdElement.appendChild(countSpan); summaryList.appendChild(createdElement); triggerChange = true; } - }, this); - window[(summaryList.childElementCount > 0 ? 'elShow' : 'elHide')](summaryList); - }.bind(this)); + }); + if (summaryList.childElementCount > 0) { + Util_1.default.show(summaryList); + } + else { + Util_1.default.hide(summaryList); + } + }); if (triggerChange) { - DomChangeListener.trigger(); + Listener_1.default.trigger(); } - }, + } /** * Initialized the reaction count buttons. - * - * @param {element} element - * @param {object} elementData */ - _initReactionCountButtons: function (element, elementData) { - var summaryList = elBySel(this._options.summaryListSelector, this._options.isSingleItem ? undefined : element); + _initReactionCountButtons(element, elementData) { + let summaryList; + if (this._options.isSingleItem) { + summaryList = document.querySelector(this._options.summaryListSelector); + } + else { + summaryList = element.querySelector(this._options.summaryListSelector); + } if (summaryList !== null) { - summaryList.addEventListener('click', this._showReactionOverlay.bind(this, elementData.objectId)); + summaryList.addEventListener("click", (ev) => this._showReactionOverlay(elementData.objectId, ev)); } - }, + } /** * Shows the reaction overly for a specific object. - * - * @param {int} objectId - * @param {Event} event */ - _showReactionOverlay: function (objectId, event) { + _showReactionOverlay(objectId, event) { event.preventDefault(); this._currentObjectId = objectId; this._showOverlay(); - }, + } /** * Shows a specific page of the current opened reaction overlay. */ - _showOverlay: function () { - this._options.parameters.data.containerID = this._objectType + '-' + this._currentObjectId; + _showOverlay() { + this._options.parameters.data.containerID = `${this._objectType}-${this._currentObjectId}`; this._options.parameters.data.objectID = this._currentObjectId; this._options.parameters.data.objectType = this._objectType; Ajax.api(this, { - parameters: this._options.parameters + parameters: this._options.parameters, }); - }, - _ajaxSuccess: function (data) { - EventHandler.fire('com.woltlab.wcf.ReactionCountButtons', 'openDialog', data); - UiDialog.open(this, data.returnValues.template); - UiDialog.setTitle('userReactionOverlay-' + this._objectType, data.returnValues.title); - }, - _ajaxSetup: function () { + } + _ajaxSuccess(data) { + EventHandler.fire("com.woltlab.wcf.ReactionCountButtons", "openDialog", data); + Dialog_1.default.open(this, data.returnValues.template); + Dialog_1.default.setTitle("userReactionOverlay-" + this._objectType, data.returnValues.title); + } + _ajaxSetup() { return { data: { - actionName: 'getReactionDetails', - className: '\\wcf\\data\\reaction\\ReactionAction' - } + actionName: "getReactionDetails", + className: "\\wcf\\data\\reaction\\ReactionAction", + }, }; - }, - _dialogSetup: function () { + } + _dialogSetup() { return { - id: 'userReactionOverlay-' + this._objectType, + id: `userReactionOverlay-${this._objectType}`, options: { - title: "" + title: "", }, - source: null + source: null, }; } - }; + } + Core.enableLegacyInheritance(CountButtons); return CountButtons; }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/Data.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/Data.js new file mode 100644 index 0000000000..2ae92b6a8b --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/Data.js @@ -0,0 +1,4 @@ +define(["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); +}); diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/CountButtons.js b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/CountButtons.js deleted file mode 100644 index 9f03754374..0000000000 --- a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/CountButtons.js +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Provides interface elements to use reactions. - * - * @author Joshua Ruesweg - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @module WoltLabSuite/Core/Ui/Reaction/Handler - * @since 5.2 - */ -define( - [ - 'Ajax', 'Core', 'Dictionary', 'Language', - 'ObjectMap', 'StringUtil', 'Dom/ChangeListener', 'Dom/Util', - 'Ui/Dialog', 'EventHandler' - ], - function( - Ajax, Core, Dictionary, Language, - ObjectMap, StringUtil, DomChangeListener, DomUtil, - UiDialog, EventHandler - ) - { - "use strict"; - - /** - * @constructor - */ - function CountButtons(objectType, options) { this.init(objectType, options); } - CountButtons.prototype = { - /** - * Initializes the like handler. - * - * @param {string} objectType object type - * @param {object} options initialization options - */ - init: function(objectType, options) { - if (options.containerSelector === '') { - throw new Error("[WoltLabSuite/Core/Ui/Reaction/CountButtons] Expected a non-empty string for option 'containerSelector'."); - } - - this._containers = new Dictionary(); - this._objects = new Dictionary(); - this._objectType = objectType; - - this._options = Core.extend({ - // selectors - summaryListSelector: '.reactionSummaryList', - containerSelector: '', - isSingleItem: false, - - // optional parameters - parameters: { - data: {} - } - }, options); - - this.initContainers(options, objectType); - - DomChangeListener.add('WoltLabSuite/Core/Ui/Reaction/CountButtons-' + objectType, this.initContainers.bind(this)); - }, - - /** - * Initialises the containers. - */ - initContainers: function() { - var element, elements = elBySelAll(this._options.containerSelector), elementData, triggerChange = false, objectId; - for (var i = 0, length = elements.length; i < length; i++) { - element = elements[i]; - if (this._containers.has(DomUtil.identify(element))) { - continue; - } - - objectId = ~~elData(element, 'object-id'); - elementData = { - reactButton: null, - summary: null, - - objectId: objectId, - element: element - }; - - this._containers.set(DomUtil.identify(element), elementData); - this._initReactionCountButtons(element, elementData); - - var objects = []; - if (this._objects.has(objectId)) { - objects = this._objects.get(objectId); - } - - objects.push(elementData); - - this._objects.set(objectId, objects); - - triggerChange = true; - } - - if (triggerChange) { - DomChangeListener.trigger(); - } - }, - - /** - * Update the count buttons with the given data. - * - * @param {int} objectId - * @param {object} data - */ - updateCountButtons: function(objectId, data) { - var triggerChange = false; - this._objects.get(objectId).forEach(function(elementData) { - var summaryList = elBySel(this._options.summaryListSelector, this._options.isSingleItem ? undefined : elementData.element); - - // summary list for the object not found; abort - if (summaryList === null) return; - - var sortedElements = {}, elements = elBySelAll('.reactCountButton', summaryList); - for (var i = 0, length = elements.length; i < length; i++) { - var reactionTypeId = elData(elements[i], 'reaction-type-id'); - if (data.hasOwnProperty(reactionTypeId)) { - sortedElements[reactionTypeId] = elements[i]; - } - else { - // The reaction no longer has any reactions. - elRemove(elements[i]); - } - } - - Object.keys(data).forEach(function(key) { - if (sortedElements[key] !== undefined) { - var reactionCount = elBySel('.reactionCount', sortedElements[key]); - reactionCount.innerHTML = StringUtil.shortUnit(data[key]); - } - else if (REACTION_TYPES[key] !== undefined) { - var createdElement = elCreate('span'); - createdElement.className = 'reactCountButton'; - createdElement.innerHTML = REACTION_TYPES[key].renderedIcon; - elData(createdElement, 'reaction-type-id', key); - - var countSpan = elCreate('span'); - countSpan.className = 'reactionCount'; - countSpan.innerHTML = StringUtil.shortUnit(data[key]); - createdElement.appendChild(countSpan); - - summaryList.appendChild(createdElement); - - triggerChange = true; - } - }, this); - - window[(summaryList.childElementCount > 0 ? 'elShow' : 'elHide')](summaryList); - }.bind(this)); - - if (triggerChange) { - DomChangeListener.trigger(); - } - }, - - /** - * Initialized the reaction count buttons. - * - * @param {element} element - * @param {object} elementData - */ - _initReactionCountButtons: function(element, elementData) { - var summaryList = elBySel(this._options.summaryListSelector, this._options.isSingleItem ? undefined : element); - if (summaryList !== null) { - summaryList.addEventListener('click', this._showReactionOverlay.bind(this, elementData.objectId)); - } - }, - - /** - * Shows the reaction overly for a specific object. - * - * @param {int} objectId - * @param {Event} event - */ - _showReactionOverlay: function(objectId, event) { - event.preventDefault(); - - this._currentObjectId = objectId; - this._showOverlay(); - }, - - /** - * Shows a specific page of the current opened reaction overlay. - */ - _showOverlay: function() { - this._options.parameters.data.containerID = this._objectType + '-' + this._currentObjectId; - this._options.parameters.data.objectID = this._currentObjectId; - this._options.parameters.data.objectType = this._objectType; - - Ajax.api(this, { - parameters: this._options.parameters - }); - }, - - _ajaxSuccess: function(data) { - EventHandler.fire('com.woltlab.wcf.ReactionCountButtons', 'openDialog', data); - - UiDialog.open(this, data.returnValues.template); - UiDialog.setTitle('userReactionOverlay-' + this._objectType, data.returnValues.title); - }, - - _ajaxSetup: function() { - return { - data: { - actionName: 'getReactionDetails', - className: '\\wcf\\data\\reaction\\ReactionAction' - } - }; - }, - - _dialogSetup: function() { - return { - id: 'userReactionOverlay-' + this._objectType, - options: { - title: "" - }, - source: null - }; - } - }; - - return CountButtons; - }); diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/CountButtons.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/CountButtons.ts new file mode 100644 index 0000000000..f02a96bc59 --- /dev/null +++ b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/CountButtons.ts @@ -0,0 +1,261 @@ +/** + * Provides interface elements to use reactions. + * + * @author Joshua Ruesweg + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Reaction/Handler + * @since 5.2 + */ + +import * as Ajax from "../../Ajax"; +import { AjaxCallbackSetup, ResponseData } from "../../Ajax/Data"; +import * as Core from "../../Core"; +import { DialogCallbackSetup } from "../Dialog/Data"; +import DomChangeListener from "../../Dom/Change/Listener"; +import DomUtil from "../../Dom/Util"; +import * as EventHandler from "../../Event/Handler"; +import { Reaction, ReactionStats } from "./Data"; +import * as StringUtil from "../../StringUtil"; +import UiDialog from "../Dialog"; + +interface CountButtonsOptions { + // selectors + summaryListSelector: string; + containerSelector: string; + isSingleItem: boolean; + + // optional parameters + parameters: { + data: { + [key: string]: unknown; + }; + }; +} + +interface ElementData { + element: HTMLElement; + objectId: number; + reactButton: null; + summary: null; +} + +interface AjaxResponse extends ResponseData { + returnValues: { + template: string; + title: string; + }; +} + +class CountButtons { + protected readonly _containers = new Map(); + protected _currentObjectId = 0; + protected readonly _objects = new Map(); + protected readonly _objectType: string; + protected readonly _options: CountButtonsOptions; + + /** + * Initializes the like handler. + */ + constructor(objectType: string, opts: Partial) { + if (!opts.containerSelector) { + throw new Error( + "[WoltLabSuite/Core/Ui/Reaction/CountButtons] Expected a non-empty string for option 'containerSelector'.", + ); + } + + this._objectType = objectType; + + this._options = Core.extend( + { + // selectors + summaryListSelector: ".reactionSummaryList", + containerSelector: "", + isSingleItem: false, + + // optional parameters + parameters: { + data: {}, + }, + }, + opts, + ) as CountButtonsOptions; + + this.initContainers(); + + DomChangeListener.add(`WoltLabSuite/Core/Ui/Reaction/CountButtons-${objectType}`, () => this.initContainers()); + } + + /** + * Initialises the containers. + */ + initContainers(): void { + let triggerChange = false; + document.querySelectorAll(this._options.containerSelector).forEach((element: HTMLElement) => { + const elementId = DomUtil.identify(element); + if (this._containers.has(elementId)) { + return; + } + + const objectId = ~~element.dataset.objectId!; + const elementData: ElementData = { + reactButton: null, + summary: null, + + objectId: objectId, + element: element, + }; + + this._containers.set(elementId, elementData); + this._initReactionCountButtons(element, elementData); + + const objects = this._objects.get(objectId) || []; + + objects.push(elementData); + + this._objects.set(objectId, objects); + + triggerChange = true; + }); + + if (triggerChange) { + DomChangeListener.trigger(); + } + } + + /** + * Update the count buttons with the given data. + */ + updateCountButtons(objectId: number, data: ReactionStats): void { + let triggerChange = false; + this._objects.get(objectId)!.forEach((elementData) => { + let summaryList: HTMLElement | null; + if (this._options.isSingleItem) { + summaryList = document.querySelector(this._options.summaryListSelector); + } else { + summaryList = elementData.element.querySelector(this._options.summaryListSelector); + } + + // summary list for the object not found; abort + if (summaryList === null) { + return; + } + + const existingReactions = new Map(Object.entries(data)); + + const sortedElements = new Map(); + summaryList.querySelectorAll(".reactCountButton").forEach((reaction: HTMLElement) => { + const reactionTypeId = reaction.dataset.reactionTypeId!; + if (existingReactions.has(reactionTypeId)) { + sortedElements.set(reactionTypeId, reaction); + } else { + // The reaction no longer has any reactions. + reaction.remove(); + } + }); + + const availableReactions = new Map(Object.entries(window.REACTION_TYPES)); + + existingReactions.forEach((count, reactionTypeId) => { + if (sortedElements.has(reactionTypeId)) { + const reaction = sortedElements.get(reactionTypeId)!; + const reactionCount = reaction.querySelector(".reactionCount") as HTMLElement; + reactionCount.innerHTML = StringUtil.shortUnit(count); + } else if (availableReactions.has(reactionTypeId)) { + const createdElement = document.createElement("span"); + createdElement.className = "reactCountButton"; + createdElement.innerHTML = availableReactions.get(reactionTypeId)!.renderedIcon; + createdElement.dataset.reactionTypeId = reactionTypeId; + + const countSpan = document.createElement("span"); + countSpan.className = "reactionCount"; + countSpan.innerHTML = StringUtil.shortUnit(count); + createdElement.appendChild(countSpan); + + summaryList!.appendChild(createdElement); + + triggerChange = true; + } + }); + + if (summaryList.childElementCount > 0) { + DomUtil.show(summaryList); + } else { + DomUtil.hide(summaryList); + } + }); + + if (triggerChange) { + DomChangeListener.trigger(); + } + } + + /** + * Initialized the reaction count buttons. + */ + protected _initReactionCountButtons(element: HTMLElement, elementData: ElementData): void { + let summaryList: HTMLElement | null; + if (this._options.isSingleItem) { + summaryList = document.querySelector(this._options.summaryListSelector); + } else { + summaryList = element.querySelector(this._options.summaryListSelector); + } + + if (summaryList !== null) { + summaryList.addEventListener("click", (ev) => this._showReactionOverlay(elementData.objectId, ev)); + } + } + + /** + * Shows the reaction overly for a specific object. + */ + protected _showReactionOverlay(objectId: number, event: MouseEvent): void { + event.preventDefault(); + + this._currentObjectId = objectId; + this._showOverlay(); + } + + /** + * Shows a specific page of the current opened reaction overlay. + */ + protected _showOverlay(): void { + this._options.parameters.data.containerID = `${this._objectType}-${this._currentObjectId}`; + this._options.parameters.data.objectID = this._currentObjectId; + this._options.parameters.data.objectType = this._objectType; + + Ajax.api(this, { + parameters: this._options.parameters, + }); + } + + _ajaxSuccess(data: AjaxResponse): void { + EventHandler.fire("com.woltlab.wcf.ReactionCountButtons", "openDialog", data); + + UiDialog.open(this, data.returnValues.template); + UiDialog.setTitle("userReactionOverlay-" + this._objectType, data.returnValues.title); + } + + _ajaxSetup(): ReturnType { + return { + data: { + actionName: "getReactionDetails", + className: "\\wcf\\data\\reaction\\ReactionAction", + }, + }; + } + + _dialogSetup(): ReturnType { + return { + id: `userReactionOverlay-${this._objectType}`, + options: { + title: "", + }, + source: null, + }; + } +} + +Core.enableLegacyInheritance(CountButtons); + +export = CountButtons; diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/Data.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/Data.ts new file mode 100644 index 0000000000..f27516c7e2 --- /dev/null +++ b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Reaction/Data.ts @@ -0,0 +1,12 @@ +export interface Reaction { + title: string; + renderedIcon: string; + iconPath: string; + showOrder: number; + reactionTypeID: number; + isAssignable: 1 | 0; +} + +export interface ReactionStats { + [key: string]: number; +} -- 2.20.1