From 46a5e08aa7556b57d6c9b782eb1500811f2c3e6d Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Wed, 26 Aug 2015 16:28:21 +0200 Subject: ES6ify our Popup library This is an experiment about the implications of ES6 transpilation in our code base. We should also define a new ES6 style guide and a jsHint configuration, for instance semi-colons are automatically inserted at the end of lines, so we may remove them. We also need to figure which ES6 features we want to use, currently I have followed Meteor-core guidance which is reasonable. --- client/lib/popup.js | 216 ++++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/client/lib/popup.js b/client/lib/popup.js index 6435eac6..b2340e04 100644 --- a/client/lib/popup.js +++ b/client/lib/popup.js @@ -1,34 +1,55 @@ // A simple tracker dependency that we invalidate every time the window is // resized. This is used to reactively re-calculate the popup position in case -// of a window resize. -var windowResizeDep = new Tracker.Dependency(); -$(window).on('resize', function() { windowResizeDep.changed(); }); +// of a window resize. This is the equivalent of a "Signal" in some other +// programming environments. +let windowResizeDep = new Tracker.Dependency() +$(window).on('resize', () => windowResizeDep.changed()) + +window.Popup = new class { + constructor() { + // The template we use to render popups + this.template = Template.popup + + // We only want to display one popup at a time and we keep the view object + // in this `Popup._current` variable. If there is no popup currently opened + // the value is `null`. + this._current = null + + // It's possible to open a sub-popup B from a popup A. In that case we keep + // the data of popup A so we can return back to it. Every time we open a new + // popup the stack grows, every time we go back the stack decrease, and if + // we close the popup the stack is reseted to the empty stack []. + this._stack = [] + + // We invalidate this internal dependency every time the top of the stack + // has changed and we want to re-render a popup with the new top-stack data. + this._dep = new Tracker.Dependency() + } -Popup = { /// This function returns a callback that can be used in an event map: /// /// Template.tplName.events({ /// 'click .elementClass': Popup.open("popupName") - /// }); + /// }) /// /// The popup inherit the data context of its parent. - open: function(name) { - var self = this; - var popupName = name + 'Popup'; + open(name) { + let self = this + const popupName = `${name}Popup` - var clickFromPopup = function(evt) { - return $(evt.target).closest('.js-pop-over').length !== 0; - }; + function clickFromPopup(evt) { + return $(evt.target).closest('.js-pop-over').length !== 0 + } return function(evt) { - // If a popup is already openened, clicking again on the opener element - // should close it -- and interupt the current `open` function. + // If a popup is already opened, clicking again on the opener element + // should close it -- and interrupt the current `open` function. if (self.isOpen()) { - var previousOpenerElement = self._getTopStack().openerElement; + let previousOpenerElement = self._getTopStack().openerElement if (previousOpenerElement === evt.currentTarget) { - return self.close(); + return self.close() } else { - $(previousOpenerElement).removeClass('is-active'); + $(previousOpenerElement).removeClass('is-active') } } @@ -37,28 +58,28 @@ Popup = { // if the popup has no parent, or from the parent `openerElement` if it // has one. This allows us to position a sub-popup exactly at the same // position than its parent. - var openerElement; + let openerElement if (clickFromPopup(evt)) { - openerElement = self._getTopStack().openerElement; + openerElement = self._getTopStack().openerElement } else { - self._stack = []; - openerElement = evt.currentTarget; + self._stack = [] + openerElement = evt.currentTarget } - $(openerElement).addClass('is-active'); - evt.preventDefault(); + $(openerElement).addClass('is-active') + evt.preventDefault() // We push our popup data to the stack. The top of the stack is always // used as the data source for our current popup. self._stack.push({ - popupName: popupName, + popupName, + openerElement, hasPopupParent: clickFromPopup(evt), title: self._getTitle(popupName), - openerElement: openerElement, depth: self._stack.length, offset: self._getOffset(openerElement), - dataContext: this.currentData && this.currentData() || this - }); + dataContext: this.currentData && this.currentData() || this, + }) // If there are no popup currently opened we use the Blaze API to render // one into the DOM. We use a reactive function as the data parameter that @@ -70,18 +91,16 @@ Popup = { // our internal dependency, and since we just changed the top element of // our internal stack, the popup will be updated with the new data. if (! self.isOpen()) { - self.current = Blaze.renderWithData(self.template, function() { - self._dep.depend(); - return _.extend(self._stack[self._stack.length - 1], { - stack: self._stack - }); - }, document.body); + self.current = Blaze.renderWithData(self.template, () => { + self._dep.depend() + return _.extend(self._getTopStack(), { stack: self._stack }) + }, document.body) } else { - self._dep.changed(); + self._dep.changed() } - }; - }, + } + } /// This function returns a callback that can be used in an event map: /// @@ -89,119 +108,100 @@ Popup = { /// 'click .elementClass': Popup.afterConfirm("popupName", function() { /// // What to do after the user has confirmed the action /// }) - /// }); - afterConfirm: function(name, action) { - var self = this; + /// }) + afterConfirm(name, action) { + let self = this return function(evt, tpl) { - var context = this.currentData && this.currentData() || this; - context.__afterConfirmAction = action; - self.open(name).call(context, evt, tpl); - }; - }, + let context = this.currentData && this.currentData() || this + context.__afterConfirmAction = action + self.open(name).call(context, evt, tpl) + } + } /// The public reactive state of the popup. - isOpen: function() { - this._dep.changed(); - return !! this.current; - }, + isOpen() { + this._dep.changed() + return !! this.current + } /// In case the popup was opened from a parent popup we can get back to it /// with this `Popup.back()` function. You can go back several steps at once /// by providing a number to this function, e.g. `Popup.back(2)`. In this case /// intermediate popup won't even be rendered on the DOM. If the number of /// steps back is greater than the popup stack size, the popup will be closed. - back: function(n) { - n = n || 1; - var self = this; - if (self._stack.length > n) { - _.times(n, function() { self._stack.pop(); }); - self._dep.changed(); + back(n = 1) { + if (this._stack.length > n) { + _.times(n, () => this._stack.pop()) + this._dep.changed() } else { - self.close(); + this.close() } - }, + } /// Close the current opened popup. - close: function() { + close() { if (this.isOpen()) { - Blaze.remove(this.current); - this.current = null; + Blaze.remove(this.current) + this.current = null - var openerElement = this._getTopStack().openerElement; - $(openerElement).removeClass('is-active'); + let openerElement = this._getTopStack().openerElement + $(openerElement).removeClass('is-active') - this._stack = []; + this._stack = [] } - }, - - // The template we use for every popup - template: Template.popup, - - // We only want to display one popup at a time and we keep the view object in - // this `Popup._current` variable. If there is no popup currently opened the - // value is `null`. - _current: null, - - // It's possible to open a sub-popup B from a popup A. In that case we keep - // the data of popup A so we can return back to it. Every time we open a new - // popup the stack grows, every time we go back the stack decrease, and if we - // close the popup the stack is reseted to the empty stack []. - _stack: [], - - // We invalidate this internal dependency every time the top of the stack has - // changed and we want to render a popup with the new top-stack data. - _dep: new Tracker.Dependency(), + } // An utility fonction that returns the top element of the internal stack - _getTopStack: function() { - return this._stack[this._stack.length - 1]; - }, + _getTopStack() { + return this._stack[this._stack.length - 1] + } // We automatically calculate the popup offset from the reference element // position and dimensions. We also reactively use the window dimensions to // ensure that the popup is always visible on the screen. - _getOffset: function(element) { - var $element = $(element); - return function() { - windowResizeDep.depend(); - var offset = $element.offset(); - var popupWidth = 300 + 15; + _getOffset(element) { + let $element = $(element) + return () => { + windowResizeDep.depend() + const offset = $element.offset() + const popupWidth = 300 + 15 return { left: Math.min(offset.left, $(window).width() - popupWidth), - top: offset.top + $element.outerHeight() - }; - }; - }, + top: offset.top + $element.outerHeight(), + } + } + } // We get the title from the translation files. Instead of returning the // result, we return a function that compute the result and since `TAPi18n.__` // is a reactive data source, the title will be changed reactively. - _getTitle: function(popupName) { - return function() { - var translationKey = popupName + '-title'; + _getTitle(popupName) { + return () => { + const translationKey = `${popupName}-title` // XXX There is no public API to check if there is an available // translation for a given key. So we try to translate the key and if the // translation output equals the key input we deduce that no translation // was available and returns `false`. There is a (small) risk a false // positives. - var title = TAPi18n.__(translationKey); - return title !== translationKey ? title : false; - }; + const title = TAPi18n.__(translationKey) + return title !== translationKey ? title : false + } } -}; +} // We close a potential opened popup on any left click on the document, or go // one step back by pressing escape. -var escapeActions = ['back', 'close']; -_.each(escapeActions, function(actionName) { - EscapeActions.register('popup-' + actionName, - _.bind(Popup[actionName], Popup), - _.bind(Popup.isOpen, Popup), { +const escapeActions = ['back', 'close'] +_.each(escapeActions, (actionName) => { + EscapeActions.register(`popup-${actionName}`, + () => Popup[actionName](), + () => Popup.isOpen(), + { noClickEscapeOn: '.js-pop-over', - enabledOnClick: actionName === 'close' + enabledOnClick: actionName === 'close', } - ); -}); + ) +}) -- cgit v1.2.3-1-g7c22