summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/awards_handler.js
diff options
context:
space:
mode:
authorBryce Johnson <bryce@gitlab.com>2017-09-12 16:16:37 -0400
committerBryce Johnson <bryce@gitlab.com>2017-09-19 19:56:59 -0400
commit8990410ec8935913c2bea46c90cc48a15b8d9f62 (patch)
tree6369dcae25ecab4d61fbab1e6837ce0941ba0219 /app/assets/javascripts/awards_handler.js
parentd15109dc5e6185b512785611cd3d146010e1eacc (diff)
downloadgitlab-ce-8990410ec8935913c2bea46c90cc48a15b8d9f62.tar.gz
Remove emoji-menu from the render tree when hidden.
Diffstat (limited to 'app/assets/javascripts/awards_handler.js')
-rw-r--r--app/assets/javascripts/awards_handler.js45
1 files changed, 39 insertions, 6 deletions
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 22fa1f2a609..5313d834b40 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -23,6 +23,9 @@ const categoryLabelMap = {
flags: 'Flags',
};
+const IS_VISIBLE = 'is-visible';
+const IS_RENDERED = 'is-rendered';
+
class AwardsHandler {
constructor(emoji) {
this.emoji = emoji;
@@ -50,7 +53,7 @@ class AwardsHandler {
if (!$target.closest('.emoji-menu').length) {
if ($('.emoji-menu').is(':visible')) {
$('.js-add-award.is-active').removeClass('is-active');
- $('.emoji-menu').removeClass('is-visible');
+ this.hideMenuElement($('.emoji-menu'));
}
}
});
@@ -87,12 +90,12 @@ class AwardsHandler {
if ($menu.length) {
if ($menu.is('.is-visible')) {
$addBtn.removeClass('is-active');
- $menu.removeClass('is-visible');
+ this.hideMenuElement($menu);
$('.js-emoji-menu-search').blur();
} else {
$addBtn.addClass('is-active');
this.positionMenu($menu, $addBtn);
- $menu.addClass('is-visible');
+ this.showMenuElement($menu);
$('.js-emoji-menu-search').focus();
}
} else {
@@ -102,7 +105,7 @@ class AwardsHandler {
$addBtn.removeClass('is-loading');
this.positionMenu($createdMenu, $addBtn);
return setTimeout(() => {
- $createdMenu.addClass('is-visible');
+ this.showMenuElement($createdMenu);
$('.js-emoji-menu-search').focus();
}, 200);
});
@@ -240,7 +243,8 @@ class AwardsHandler {
if (gl.utils.isInIssuePage() && !isMainAwardsBlock) {
const id = votesBlock.attr('id').replace('note_', '');
- $('.emoji-menu').removeClass('is-visible');
+ this.hideMenuElement($('.emoji-menu'));
+
$('.js-add-award.is-active').removeClass('is-active');
const toggleAwardEvent = new CustomEvent('toggleAward', {
detail: {
@@ -260,7 +264,8 @@ class AwardsHandler {
return typeof callback === 'function' ? callback() : undefined;
});
- $('.emoji-menu').removeClass('is-visible');
+ this.hideMenuElement($('.emoji-menu'));
+
return $('.js-add-award.is-active').removeClass('is-active');
}
@@ -528,6 +533,34 @@ class AwardsHandler {
return $matchingElements.closest('li').clone();
}
+ /* showMenuElement and hideMenuElement are performance optimizations. We use
+ * opacity to show/hide the emoji menu, because we can animate it. But opacity
+ * leaves hidden elements in the render tree, which is unacceptable given the number
+ * of emoji elements in the emoji menu (5k+). To get the best of both worlds, we separately
+ * apply IS_RENDERED to add/remove the menu from the render tree and IS_VISIBLE to animate
+ * the menu being opened and closed. */
+
+ showMenuElement($emojiMenu) {
+ // enqueues animation as a microtask, so it begins ASAP once IS_RENDERED added
+ Promise.resolve().then(() => { // eslint-disable-line promise/catch-or-return
+ $emojiMenu.addClass(IS_VISIBLE);
+ });
+
+ $emojiMenu.addClass(IS_RENDERED);
+ }
+
+ hideMenuElement($emojiMenu) {
+ $emojiMenu.on(transitionEndEventString, (e) => {
+ if (e.currentTarget === e.target) {
+ $emojiMenu
+ .removeClass(IS_RENDERED)
+ .off(transitionEndEventString);
+ }
+ });
+
+ $emojiMenu.removeClass(IS_VISIBLE);
+ }
+
destroy() {
this.eventListeners.forEach((entry) => {
entry.element.off.call(entry.element, ...entry.args);