{
"use strict";
+ var UiScreen = require('Ui/Screen');
+
/**
* @constructor
*/
if (~~elData(reactionTypeButton, 'is-assignable') === 0) {
elShow(reactionTypeButton);
}
+
+ this._scrollReactionIntoView(reactionTypeButton);
+ }
+ },
+
+ _scrollReactionIntoView: function (reactionTypeButton) {
+ var scrollableContainer = elBySel('.reactionPopoverContent', this._getPopover());
+
+ // Do not scroll if the button is located in the upper 75%.
+ if (reactionTypeButton.offsetTop < scrollableContainer.clientHeight * 0.75) {
+ scrollableContainer.scrollTop = 0;
+ }
+ else {
+ // `Element.scrollTop` permits arbitrary values and will always clamp them to
+ // the maximum possible offset value. We can abuse this behavior by calculating
+ // the values to place the selected reaction in the center of the popover,
+ // regardless of the offset being out of range.
+ scrollableContainer.scrollTop = reactionTypeButton.offsetTop + reactionTypeButton.clientHeight / 2 - scrollableContainer.clientHeight / 2;
}
},
}
this._popoverCurrentObjectId = objectId;
- this._markReactionAsActive();
UiAlignment.set(this._getPopover(), element, {
pointer: true,
- horizontal: (this._options.isButtonGroupNavigation) ? 'left' :'center',
- vertical: 'top'
+ horizontal: (this._options.isButtonGroupNavigation) ? 'left' : 'center',
+ vertical: UiScreen.is('screen-xs') ? 'bottom' : 'top'
});
if (this._options.isButtonGroupNavigation) {
element.closest('nav').style.setProperty('opacity', '1', '');
}
- this._getPopover().classList.remove('forceHide');
- this._getPopover().classList.add('active');
+ var popover = this._getPopover();
+
+ // The popover could be rendered below the input field on mobile, in which case
+ // the "first" button is displayed at the bottom and thus farthest away. Reversing
+ // the display order will restore the logic by placing the "first" button as close
+ // to the react button as possible.
+ var inverseOrder = popover.style.getPropertyValue('bottom') === 'auto';
+ popover.classList[inverseOrder ? 'add' : 'remove']('inverseOrder');
+
+ this._markReactionAsActive();
+
+ popover.classList.remove('forceHide');
+ popover.classList.add('active');
},
/**
_popoverContent.className = 'reactionPopoverContent';
var popoverContentHTML = elCreate('ul');
+ popoverContentHTML.className = 'reactionTypeButtonList';
var sortedReactionTypes = this._getSortedReactionTypes();
}
_popoverContent.appendChild(popoverContentHTML);
+ _popoverContent.addEventListener('scroll', function () {
+ var hasTopOverflow = _popoverContent.scrollTop > 0;
+ _popoverContent.classList[hasTopOverflow ? 'add' : 'remove']('overflowTop');
+
+ var hasBottomOverflow = _popoverContent.scrollTop + _popoverContent.clientHeight < _popoverContent.scrollHeight;
+ _popoverContent.classList[hasBottomOverflow ? 'add' : 'remove']('overflowBottom');
+ }, {passive: true});
+
this._popover.appendChild(_popoverContent);
var pointer = elCreate('span');
background-color: $wcfContentBackground;
border-radius: 2px;
box-shadow: 0 2px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+ overflow: hidden;
position: absolute;
top: 0;
vertical-align: middle;
> .elementPointer {
display: none;
}
+
+ @include screen-xs {
+ &.inverseOrder .reactionTypeButtonList {
+ flex-direction: column-reverse;
+ }
+ }
}
.reactionType {
}
}
+ @include screen-xs {
+ max-height: 200px;
+ overflow: auto;
+
+ &::after,
+ &::before {
+ content: "";
+ height: 40px;
+ left: 0;
+ opacity: 0;
+ pointer-events: none;
+ position: absolute;
+ right: 0;
+ transition: opacity .12s linear;
+ }
+
+ &::after {
+ background-image: linear-gradient(to bottom, transparent, $wcfContentBackground);
+ bottom: 0;
+ }
+ &.overflowBottom::after {
+ opacity: 1;
+ }
+
+ &::before {
+ background-image: linear-gradient(to top, transparent, $wcfContentBackground);
+ top: 0;
+ }
+ &.overflowTop::before {
+ opacity: 1;
+ }
+ }
+
@include screen-md-down {
padding: 5px 0;
}
}
+.reactionTypeButtonList {
+ display: flex;
+ flex-direction: column;
+}
+
@include screen-lg {
html.touch .reactionPopoverContent .reactionTypeButton {
display: block;