From 4cb8dcd3764ab003b388c4a52eb59e86267a9b15 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Mon, 8 Jul 2019 12:44:49 +0200 Subject: [PATCH] Improved the UX and the DOM markup --- .../templates/articleListItems.tpl | 7 -- .../templates/reactionSummaryList.tpl | 19 +++-- .../templates/reactionTypeImage.tpl | 2 +- .../files/acp/templates/reactionTypeImage.tpl | 2 +- .../Core/Ui/Reaction/CountButtons.js | 59 +++++---------- .../WoltLabSuite/Core/Ui/Reaction/Handler.js | 66 +++++++++-------- .../data/reaction/ReactionAction.class.php | 8 +-- wcfsetup/install/files/style/ui/message.scss | 4 +- .../install/files/style/ui/reactions.scss | 72 +++++++++---------- 9 files changed, 109 insertions(+), 130 deletions(-) diff --git a/com.woltlab.wcf/templates/articleListItems.tpl b/com.woltlab.wcf/templates/articleListItems.tpl index 0ac8920fc1..13f4a11144 100644 --- a/com.woltlab.wcf/templates/articleListItems.tpl +++ b/com.woltlab.wcf/templates/articleListItems.tpl @@ -53,13 +53,6 @@
- {if MODULE_LIKE && $__wcf->getSession()->getPermission('user.like.canViewLike') && ($article->likes || $article->dislikes || $article->neutralReactions)} -
- - {if $article->cumulativeLikes > 0}+{elseif $article->cumulativeLikes == 0}±{/if}{#$article->cumulativeLikes} - -
- {/if}
diff --git a/com.woltlab.wcf/templates/reactionSummaryList.tpl b/com.woltlab.wcf/templates/reactionSummaryList.tpl index 8a70bae2fb..50b880922a 100644 --- a/com.woltlab.wcf/templates/reactionSummaryList.tpl +++ b/com.woltlab.wcf/templates/reactionSummaryList.tpl @@ -1,9 +1,16 @@ {if $__wcf->session->getPermission('user.like.canViewLike')} - +{/if} diff --git a/com.woltlab.wcf/templates/reactionTypeImage.tpl b/com.woltlab.wcf/templates/reactionTypeImage.tpl index b8aa009259..55cd510060 100644 --- a/com.woltlab.wcf/templates/reactionTypeImage.tpl +++ b/com.woltlab.wcf/templates/reactionTypeImage.tpl @@ -2,4 +2,4 @@ src="{@$__wcf->getPath()}images/reaction/{$reactionType->iconFile}" class="reactionType" data-reaction-type-id="{$reactionType->reactionTypeID}" -/> +> diff --git a/wcfsetup/install/files/acp/templates/reactionTypeImage.tpl b/wcfsetup/install/files/acp/templates/reactionTypeImage.tpl index a4577bbdef..a44061561b 100644 --- a/wcfsetup/install/files/acp/templates/reactionTypeImage.tpl +++ b/wcfsetup/install/files/acp/templates/reactionTypeImage.tpl @@ -3,4 +3,4 @@ style="width:24px;height:24px" class="reactionType" data-reaction-type-id="{$reactionType->reactionTypeID}" -/> +> 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 4e5afd7152..d9e4f4aab7 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/CountButtons.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/CountButtons.js @@ -69,27 +69,26 @@ define( continue; } + objectId = ~~elData(element, 'object-id'); elementData = { reactButton: null, summary: null, - objectId: ~~elData(element, 'object-id'), + objectId: objectId, element: element }; this._containers.set(DomUtil.identify(element), elementData); this._initReactionCountButtons(element, elementData); - - if (!this._objects.has(~~elData(element, 'object-id'))) { - var objects = []; - } - else { - var objects = this._objects.get(~~elData(element, 'object-id')); + + var objects = []; + if (this._objects.has(objectId)) { + objects = this._objects.get(objectId); } objects.push(elementData); - this._objects.set(~~elData(element, 'object-id'), objects); + this._objects.set(objectId, objects); triggerChange = true; } @@ -113,13 +112,14 @@ define( // summary list for the object not found; abort if (summaryList === null) return; - var sortedElements = {}, elements = elBySelAll('li', summaryList); + var sortedElements = {}, elements = elBySelAll('.reactCountButton', summaryList); for (var i = 0, length = elements.length; i < length; i++) { - if (data[elData(elements[i], 'reaction-type-id')] !== undefined) { - sortedElements[elData(elements[i], 'reaction-type-id')] = elements[i]; + var reactionTypeId = elData(elements[i], 'reaction-type-id'); + if (data.hasOwnProperty(reactionTypeId)) { + sortedElements[reactionTypeId] = elements[i]; } else { - // reaction has no longer reactions + // The reaction no longer has any reactions. elRemove(elements[i]); } } @@ -130,25 +130,23 @@ define( reactionCount.innerHTML = StringUtil.shortUnit(data[key]); } else if (REACTION_TYPES[key] !== undefined) { - // create element - var createdElement = elCreate('li'); + 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); - createdElement.innerHTML = createdElement.innerHTML + REACTION_TYPES[key].renderedIcon; - summaryList.appendChild(createdElement); - this._initReactionCountButton(createdElement, objectId); - triggerChange = true; } }, this); + + window[(summaryList.childElementCount > 0 ? 'elShow' : 'elHide')](summaryList); }.bind(this)); if (triggerChange) { @@ -163,31 +161,12 @@ define( * @param {object} elementData */ _initReactionCountButtons: function(element, elementData) { - if (this._options.isSingleItem) { - var summaryList = elBySel(this._options.summaryListSelector); - } - else { - var summaryList = elBySel(this._options.summaryListSelector, element); - } - + var summaryList = elBySel(this._options.summaryListSelector, this._options.isSingleItem ? undefined : element); if (summaryList !== null) { - var elements = elBySelAll('li', summaryList); - for (var i = 0, length = elements.length; i < length; i++) { - this._initReactionCountButton(elements[i], elementData.objectId); - } + summaryList.addEventListener(WCF_CLICK_EVENT, this._showReactionOverlay.bind(this, elementData.objectId)); } }, - /** - * Initialized a specific reaction count button for an object. - * - * @param {element} element - * @param {int} objectId - */ - _initReactionCountButton: function(element, objectId) { - element.addEventListener(WCF_CLICK_EVENT, this._showReactionOverlay.bind(this, objectId)); - }, - /** * Shows the reaction overly for a specific object. * diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/Handler.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/Handler.js index eb6bdf2dbe..5e6b18046e 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/Handler.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Reaction/Handler.js @@ -40,7 +40,6 @@ define( } this._containers = new Dictionary(); - this._details = new ObjectMap(); this._objectType = objectType; this._cache = new Dictionary(); this._objects = new Dictionary(); @@ -81,25 +80,24 @@ define( continue; } + objectId = ~~elData(element, 'object-id'); elementData = { reactButton: null, - objectId: ~~elData(element, 'object-id'), + objectId: objectId, element: element }; this._containers.set(DomUtil.identify(element), elementData); this._initReactButton(element, elementData); - - if (!this._objects.has(~~elData(element, 'object-id'))) { - var objects = []; - } - else { - var objects = this._objects.get(~~elData(element, 'object-id')); + + var objects = []; + if (this._objects.has(objectId)) { + objects = this._objects.get(objectId); } objects.push(elementData); - this._objects.set(~~elData(element, 'object-id'), objects); + this._objects.set(objectId, objects); triggerChange = true; } @@ -122,11 +120,13 @@ define( } if (elementData.reactButton === null || elementData.reactButton.length === 0) { - // the element may have no react button + // The element may have no react button. return; } + //noinspection JSUnresolvedVariable if (Object.keys(REACTION_TYPES).length === 1) { + //noinspection JSUnresolvedVariable var reaction = REACTION_TYPES[Object.keys(REACTION_TYPES)[0]]; elementData.reactButton.title = reaction.title; var textSpan = elBySel('.invisible', elementData.reactButton); @@ -190,7 +190,8 @@ define( if (reactionTypeID) { elementData.reactButton.classList.add('active'); elData(elementData.reactButton, 'reaction-type-id', reactionTypeID); - } else { + } + else { elData(elementData.reactButton, 'reaction-type-id', 0); elementData.reactButton.classList.remove('active'); } @@ -199,25 +200,24 @@ define( }, _markReactionAsActive: function() { - var reactionTypeID; + var reactionTypeID = null; this._objects.get(this._popoverCurrentObjectId).forEach(function (element) { if (element.reactButton !== null) { - reactionTypeID = elData(element.reactButton, 'reaction-type-id'); + reactionTypeID = ~~elData(element.reactButton, 'reaction-type-id'); } }); - if (reactionTypeID === undefined) { + if (reactionTypeID === null) { throw new Error("Unable to find react button for current popover."); } - // clear old active state - var elements = elBySelAll('.reactionTypeButton.active', this._getPopover()); - for (var i = 0, length = elements.length; i < length; i++) { - elements[i].classList.remove('active'); - } + // Clear the old active state. + elBySelAll('.reactionTypeButton.active', this._getPopover(), function(element) { + element.classList.remove('active'); + }); - if (reactionTypeID != 0) { - elBySel('.reactionTypeButton[data-reaction-type-id="'+reactionTypeID+'"]', this._getPopover()).classList.add('active'); + if (reactionTypeID) { + elBySel('.reactionTypeButton[data-reaction-type-id="' + reactionTypeID + '"]', this._getPopover()).classList.add('active'); } }, @@ -226,14 +226,17 @@ define( * * @param {int} objectId * @param {Element} element + * @param {?Event} event */ _toggleReactPopover: function(objectId, element, event) { if (event !== null) { event.preventDefault(); event.stopPropagation(); } - + + //noinspection JSUnresolvedVariable if (Object.keys(REACTION_TYPES).length === 1) { + //noinspection JSUnresolvedVariable var reaction = REACTION_TYPES[Object.keys(REACTION_TYPES)[0]]; this._popoverCurrentObjectId = objectId; @@ -256,7 +259,6 @@ define( * @param {Element} element container element */ _openReactPopover: function(objectId, element) { - // first close old popover, if exists if (this._popoverCurrentObjectId !== 0) { this._closePopover(); } @@ -271,9 +273,7 @@ define( }); if (this._options.isButtonGroupNavigation) { - // find nav element - var nav = element.closest('nav'); - nav.style.opacity = "1"; + element.closest('nav').style.setProperty('opacity', '1', ''); } this._getPopover().classList.remove('forceHide'); @@ -311,7 +311,8 @@ define( var reactionTypeItemSpan = elCreate('span'); reactionTypeItemSpan.classList = 'reactionTypeButtonTitle'; reactionTypeItemSpan.innerHTML = reactionType.title; - + + //noinspection JSUnresolvedVariable reactionTypeItem.innerHTML = reactionType.renderedIcon; reactionTypeItem.appendChild(reactionTypeItemSpan); @@ -346,13 +347,18 @@ define( var sortedReactionTypes = []; // convert our reaction type object to an array + //noinspection JSUnresolvedVariable for (var key in REACTION_TYPES) { - if (!REACTION_TYPES.hasOwnProperty(key)) continue; - sortedReactionTypes.push(REACTION_TYPES[key]); + //noinspection JSUnresolvedVariable + if (REACTION_TYPES.hasOwnProperty(key)) { + //noinspection JSUnresolvedVariable + sortedReactionTypes.push(REACTION_TYPES[key]); + } } // sort the array sortedReactionTypes.sort(function (a, b) { + //noinspection JSUnresolvedVariable return a.showOrder - b.showOrder; }); @@ -395,9 +401,9 @@ define( }, _ajaxSuccess: function(data) { + //noinspection JSUnresolvedVariable this.countButtons.updateCountButtons(data.returnValues.objectID, data.returnValues.reactions); - // update react button status this._updateReactButton(data.returnValues.objectID, data.returnValues.reactionTypeID); }, diff --git a/wcfsetup/install/files/lib/data/reaction/ReactionAction.class.php b/wcfsetup/install/files/lib/data/reaction/ReactionAction.class.php index 98c91034ce..50fe922e39 100644 --- a/wcfsetup/install/files/lib/data/reaction/ReactionAction.class.php +++ b/wcfsetup/install/files/lib/data/reaction/ReactionAction.class.php @@ -52,25 +52,25 @@ class ReactionAction extends AbstractDatabaseObjectAction { * likeable object * @var ILikeObject */ - public $likeableObject = null; + public $likeableObject; /** * object type object * @var ObjectType */ - public $objectType = null; + public $objectType; /** * like object type provider object * @var ILikeObjectTypeProvider */ - public $objectTypeProvider = null; + public $objectTypeProvider; /** * reaction type for the reaction * @var ReactionType */ - public $reactionType = null; + public $reactionType; /** * Validates parameters to fetch like details. diff --git a/wcfsetup/install/files/style/ui/message.scss b/wcfsetup/install/files/style/ui/message.scss index fa6010cba8..90ee43c241 100644 --- a/wcfsetup/install/files/style/ui/message.scss +++ b/wcfsetup/install/files/style/ui/message.scss @@ -504,14 +504,14 @@ flex-wrap: wrap; &:not(:first-child) { - > .likesSummary, + > .reactionSummaryList, > .messageFooterButtons, > .messageFooterButtonsExtra { margin-top: 20px; } } - > .likesSummary { + > .reactionSummaryList { flex: 0 1 auto; @include wcfFontSmall; diff --git a/wcfsetup/install/files/style/ui/reactions.scss b/wcfsetup/install/files/style/ui/reactions.scss index 08337081c9..c112d283fe 100644 --- a/wcfsetup/install/files/style/ui/reactions.scss +++ b/wcfsetup/install/files/style/ui/reactions.scss @@ -25,14 +25,38 @@ } } -img.reactionType { - width: 24px; - height: 24px; +.reactionType { + width: 20px; + height: 20px; } .reactionSummaryList { - span.reactionCount::after { - content: ' × '; + display: flex; + flex-wrap: wrap; + margin: -5px -5px 0 0; + + .reactionCount{ + @include wcfFontSmall; + + &::before { + content: ' × '; + } + } + + &.reactionSummaryListTiny .reactionType { + width: 16px; + height: 16px; + } +} + +.reactCountButton { + color: $wcfContentDimmedText; + flex: 0 0 auto; + margin: 5px 5px 0 0; + white-space: nowrap; + + &:hover { + color: $wcfContentText; } } @@ -59,10 +83,10 @@ img.reactionType { } @include screen-md-down { - padding: 5px 0px; + padding: 5px 0; > ul > li.reactionTypeButton { - margin: 0px; + margin: 0; display: block; padding: 5px 25px; } @@ -106,23 +130,11 @@ img.reactionType { } > ul > li.reactionTypeButton:first-child { - margin-left: 0px; + margin-left: 0; } } } -li.reactCountButton { - display: inline; - padding: 5px; - cursor: pointer; - color: $wcfContentDimmedText; - white-space: nowrap; -} - -li.reactCountButton:hover { - color: $wcfContentText; -} - .reputationCounter { color: $wcfContentDimmedText; @@ -153,24 +165,6 @@ li.reactCountButton:hover { } } -.reactionSummaryListTiny { - display: inline; - - li.reactCountButton > img { - width: 16px; - height: 16px; - } - - span.reactionCount { - @include wcfFontSmall; - } - - li.reactCountButton { - background-color: transparent; - padding: 0px; - } -} - @include screen-sm-down { .reactionStatusContainer { display: none; @@ -195,4 +189,4 @@ li.reactCountButton:hover { align-items: center; } } -} \ No newline at end of file +} -- 2.20.1