From 99247776866b8593e80969b3d125c6caadc016fd Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 26 Jan 2016 22:56:58 +0100 Subject: [PATCH] Overhauled popover functionality --- .../js/WoltLab/WCF/Controller/Popover.js | 86 +++------- wcfsetup/install/files/style/ui/popover.scss | 148 +++++++----------- 2 files changed, 81 insertions(+), 153 deletions(-) diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Controller/Popover.js b/wcfsetup/install/files/js/WoltLab/WCF/Controller/Popover.js index 06ab96ff0d..7d7949ea56 100644 --- a/wcfsetup/install/files/js/WoltLab/WCF/Controller/Popover.js +++ b/wcfsetup/install/files/js/WoltLab/WCF/Controller/Popover.js @@ -10,7 +10,6 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' "use strict"; var _activeId = null; - var _baseHeight = 0; var _cache = new Dictionary(); var _elements = new Dictionary(); var _handlers = new Dictionary(); @@ -21,7 +20,6 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' var _popover = null; var _popoverContent = null; - var _popoverLoading = null; var _callbackClick = null; var _callbackHide = null; @@ -32,13 +30,13 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' /** @const */ var STATE_LOADING = 1; /** @const */ var STATE_READY = 2; - /** @const */ var DELAY_SHOW = 800; /** @const */ var DELAY_HIDE = 500; + /** @const */ var DELAY_SHOW = 300; /** * @exports WoltLab/WCF/Controller/Popover */ - var ControllerPopover = { + return { /** * Builds popover DOM elements and binds event listeners. */ @@ -59,10 +57,6 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' pointer.appendChild(elCreate('span')); _popover.appendChild(pointer); - _popoverLoading = elCreate('span'); - _popoverLoading.className = 'icon icon32 fa-spinner'; - _popover.appendChild(_popoverLoading); - document.body.appendChild(_popover); // static binding for callbacks (they don't change anyway and binding each time is expensive) @@ -74,13 +68,7 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' _popover.addEventListener('mouseenter', this._popoverMouseEnter.bind(this)); _popover.addEventListener('mouseleave', _callbackMouseLeave); - _popoverContent.addEventListener('transitionend', function(event) { - if (event.propertyName === 'height') { - _popoverContent.classList.remove('loading'); - } - }); - - _popover.addEventListener('transitionend', this._clearContent.bind(this)); + _popover.addEventListener('animationend', this._clearContent.bind(this)); window.addEventListener('beforeunload', (function() { _suspended = true; @@ -201,7 +189,7 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' * Sets the content for given identifier and object id. * * @param {string} identifier handler identifier - * @param {integer} objectId object id + * @param {int} objectId object id * @param {string} content HTML string */ setContent: function(identifier, objectId, content) { @@ -299,9 +287,25 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' _timeoutLeave = null; } - var disableAnimation = (_activeId !== null && _activeId !== _hoverId); + var forceHide = false; if (_popover.classList.contains('active')) { - this._hide(disableAnimation); + this._hide(); + + forceHide = true; + } + else if (_popoverContent.childElementCount) { + forceHide = true; + } + + if (forceHide) { + _popover.classList.add('forceHide'); + + // force layout + _popover.offsetTop; + + this._clearContent(); + + _popover.classList.remove('forceHide'); } _activeId = _hoverId; @@ -311,14 +315,10 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' if (data.state === STATE_READY) { _popoverContent.appendChild(data.content); + + this._rebuild(_activeId); } else if (data.state === STATE_NONE) { - _popoverContent.classList.add('loading'); - } - - this._rebuild(_activeId); - - if (data.state === STATE_NONE) { data.state = STATE_LOADING; _handlers.get(elData.identifier).loadCallback(elData.objectId, this); @@ -327,25 +327,14 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' /** * Hides the popover element. - * - * @param {(object|boolean)} event event object or boolean if popover should be forced hidden */ - _hide: function(event) { + _hide: function() { if (_timeoutLeave !== null) { window.clearTimeout(_timeoutLeave); _timeoutLeave = null; } _popover.classList.remove('active'); - - if (typeof event === 'boolean' && event === true) { - _popover.classList.add('disableAnimation'); - - // force reflow - _popover.offsetHeight; - - this._clearContent(); - } }, /** @@ -357,8 +346,6 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' while (_popoverContent.childNodes.length) { activeElData.content.appendChild(_popoverContent.childNodes[0]); } - - _popoverContent.style.removeProperty('height'); } }, @@ -371,27 +358,6 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' } _popover.classList.add('active'); - _popover.classList.remove('disableAnimation'); - if (_popoverContent.classList.contains('loading')) { - if (_popoverContent.childElementCount === 0) { - if (_baseHeight === 0) { - _baseHeight = _popoverContent.offsetHeight; - } - - _popoverContent.style.setProperty('height', _baseHeight + 'px'); - } - else { - _popoverContent.style.removeProperty('height'); - - var height = _popoverContent.offsetHeight; - _popoverContent.style.setProperty('height', _baseHeight + 'px'); - - // force reflow - _popoverContent.offsetHeight; - - _popoverContent.style.setProperty('height', height + 'px'); - } - } UiAlignment.set(_popover, _elements.get(_activeId).element, { pointer: true, @@ -420,6 +386,4 @@ define(['Ajax', 'Dictionary', 'Environment', 'Dom/ChangeListener', 'Dom/Util', ' Ajax.api(this, data, success, failure); } }; - - return ControllerPopover; }); diff --git a/wcfsetup/install/files/style/ui/popover.scss b/wcfsetup/install/files/style/ui/popover.scss index abe8880a58..a77b472628 100644 --- a/wcfsetup/install/files/style/ui/popover.scss +++ b/wcfsetup/install/files/style/ui/popover.scss @@ -1,116 +1,80 @@ +@keyframes wcfPopover { + 0% { visibility: visible; transform: translateY(-20px); opacity: 0; } + 100% { visibility: visible; transform: translateY(0); opacity: 1; } +} + +@keyframes wcfPopoverOut { + 0% { visibility: visible; transform: translateY(0); opacity: 1; } + 100% { visibility: hidden; transform: translateY(-20px); opacity: 0; } +} + +/* outer element containg both the pointer and content element */ .popover { + animation: wcfPopoverOut .3s; + animation-fill-mode: forwards; background-color: $wcfContentBackground; - border: 1px solid $wcfContentBorder; - opacity: 0; + border: 1px solid $wcfDropdownBorder; + box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, .2); position: absolute; top: 0; vertical-align: middle; - visibility: hidden; width: 400px !important; z-index: 500; - @include boxShadow(2px, 2px, rgba(0, 0, 0, .2), 10px); - - transition: visibility 0s linear .3s, opacity .3s linear; - &.active { - opacity: 1; - visibility: visible; - - transition-delay: 0s; + animation: wcfPopover .3s; + animation-fill-mode: forwards; } - &.disableAnimation { - transition: none !important; - - > .popoverContent { - transition: none !important; - } - - > .elementPointer > span { - transition: none !important; - } - } - - > .popoverContent { - background-color: $wcfContentBackground; - border-radius: 3px; - color: $wcfContentText; - max-height: 320px; - min-height: 36px; - opacity: 1; - overflow: hidden; - padding: 10px; - - transition: opacity .3s linear; - - a { - color: $wcfContentLink; - - &:hover { - color: $wcfContentLinkActive; - } - } - - &:not(.loading) { - ~ .fa-spinner { - display: none; - } - - ~ .elementPointer { - > span { - border-color: $wcfContentBackground $wcfContentBorder; - border-style: solid; - border-width: 0 5px 5px; - left: -5px; - opacity: 1; - position: absolute; - top: 3px; - - transition: opacity .3s linear; - } - - &.flipVertical > span { - border-width: 5px 5px 0; - bottom: 3px; - top: auto; - } - } - } - - &.loading { - opacity: 0; - transition: height .3s linear, opacity 0s; - - ~ .elementPointer > span { - opacity: 0; - - transition: opacity 0s; - } - } + &.forceHide { + animation: 0; + visibility: hidden; } > .elementPointer { - border-color: rgba(0, 0, 0, .4) transparent; + border-width: 0 7px 7px; border-style: solid; - border-width: 0 6px 6px; - top: -2px; + border-color: $wcfDropdownBorder transparent; + top: 0; &.flipVertical { - border-width: 6px 6px 0; - bottom: -2px; + border-width: 7px 7px 0; + bottom: 0; top: auto; + + > span { + border-width: 5px 5px 0; + bottom: 2px; + top: auto; + } } + + > span { + border-color: $wcfContentBackground transparent; + border-style: solid; + border-width: 0 5px 5px; + left: -5px; + position: absolute; + top: 2px; + } } +} + +/* actual popover content */ +.popoverContent { + background-color: $wcfContentBackground; + border-radius: 3px; + color: $wcfContentText; + max-height: 320px; + min-height: 36px; + overflow: hidden; + padding: 10px; - > .fa-spinner { - color: rgba(255, 255, 255, 1); - left: 50%; - margin-left: -14px; - margin-top: -14px; - position: absolute; - top: 50%; + a { + color: $wcfContentLink; - @include textShadow(black); + &:hover { + color: $wcfContentLinkActive; + } } } -- 2.20.1