Przeglądaj źródła

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.
Maxime Quandalle 9 lat temu
rodzic
commit
46a5e08aa7
1 zmienionych plików z 108 dodań i 108 usunięć
  1. 108 108
      client/lib/popup.js

+ 108 - 108
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',
     }
-  );
-});
+  )
+})