"use strict";
var _activeId = null;
- var _baseHeight = 0;
var _cache = new Dictionary();
var _elements = new Dictionary();
var _handlers = new Dictionary();
var _popover = null;
var _popoverContent = null;
- var _popoverLoading = null;
var _callbackClick = null;
var _callbackHide = null;
/** @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.
*/
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)
_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;
* 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) {
_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;
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);
/**
* 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();
- }
},
/**
while (_popoverContent.childNodes.length) {
activeElData.content.appendChild(_popoverContent.childNodes[0]);
}
-
- _popoverContent.style.removeProperty('height');
}
},
}
_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,
Ajax.api(this, data, success, failure);
}
};
-
- return ControllerPopover;
});
+@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;
+ }
}
}