]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Begin to use Popper for Dropdown
authorJohann-S <johann.servoire@gmail.com>
Fri, 14 Apr 2017 09:25:53 +0000 (11:25 +0200)
committerJohann-S <johann.servoire@gmail.com>
Sun, 14 May 2017 09:41:19 +0000 (11:41 +0200)
js/src/dropdown.js
js/src/tooltip.js
js/tests/visual/dropdown.html
scss/_dropdown.scss

index eb536dc7dc34ab18788b65f1315be9d7a9b45ed6..a2b5561c27203ce05d276df4013a03d6929e05e9 100644 (file)
@@ -10,6 +10,13 @@ import Util from './util'
 
 const Dropdown = (($) => {
 
+  /**
+   * Check for Popper dependency
+   * Popper - https://popper.js.org
+   */
+  if (typeof Popper === 'undefined') {
+    throw new Error('Bootstrap dropdown require Popper (https://popper.js.org)')
+  }
 
   /**
    * ------------------------------------------------------------------------
@@ -55,6 +62,20 @@ const Dropdown = (($) => {
     VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled)'
   }
 
+  const Default = {
+    animation   : true,
+    trigger     : 'click',
+    placement   : 'bottom',
+    offset      : '0 0'
+  }
+
+  const DefaultType = {
+    animation       : 'boolean',
+    trigger         : 'string',
+    placement       : 'string',
+    offset          : 'string'
+  }
+
 
   /**
    * ------------------------------------------------------------------------
@@ -64,8 +85,11 @@ const Dropdown = (($) => {
 
   class Dropdown {
 
-    constructor(element) {
+    constructor(element, config) {
       this._element = element
+      this._popper  = null
+      this._config = this._getConfig(config)
+      this._menu = this._getMenuElement()
 
       this._addEventListeners()
     }
@@ -77,16 +101,30 @@ const Dropdown = (($) => {
       return VERSION
     }
 
+    static get Default() {
+      return Default
+    }
+
+    static get DefaultType() {
+      return DefaultType
+    }
+
 
     // public
 
     toggle() {
-      if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {
+      let context = $(this).data(DATA_KEY)
+      if (!context) {
+        context = new Dropdown(this)
+        $(this).data(DATA_KEY, context)
+      }
+
+      if (context.disabled || $(this).hasClass(ClassName.DISABLED)) {
         return false
       }
 
       const parent   = Dropdown._getParentFromElement(this)
-      const isActive = $(parent).hasClass(ClassName.SHOW)
+      const isActive = $(context._menu).hasClass(ClassName.SHOW)
 
       Dropdown._clearMenus()
 
@@ -97,7 +135,7 @@ const Dropdown = (($) => {
       const relatedTarget = {
         relatedTarget : this
       }
-      const showEvent     = $.Event(Event.SHOW, relatedTarget)
+      const showEvent = $.Event(Event.SHOW, relatedTarget)
 
       $(parent).trigger(showEvent)
 
@@ -105,6 +143,13 @@ const Dropdown = (($) => {
         return false
       }
 
+      this._popper = new Popper(this, context._menu, {
+        placement : context._config.placement,
+        offsets : {
+          popper : context._config.offset
+        }
+      })
+
       // if this is a touch-enabled device we add extra
       // empty mouseover listeners to the body's immediate children;
       // only needed because of broken event delegation on iOS
@@ -117,8 +162,10 @@ const Dropdown = (($) => {
       this.focus()
       this.setAttribute('aria-expanded', true)
 
-      $(parent).toggleClass(ClassName.SHOW)
-      $(parent).trigger($.Event(Event.SHOWN, relatedTarget))
+      $(context._menu).toggleClass(ClassName.SHOW)
+      $(parent)
+        .toggleClass(ClassName.SHOW)
+        .trigger($.Event(Event.SHOWN, relatedTarget))
 
       return false
     }
@@ -127,6 +174,10 @@ const Dropdown = (($) => {
       $.removeData(this._element, DATA_KEY)
       $(this._element).off(EVENT_KEY)
       this._element = null
+      this._menu = null
+      if (this._popper !== null) {
+        this._popper.destroy()
+      }
     }
 
 
@@ -136,15 +187,40 @@ const Dropdown = (($) => {
       $(this._element).on(Event.CLICK, this.toggle)
     }
 
+    _getConfig(config) {
+      config = $.extend(
+        {},
+        this.constructor.Default,
+        $(this._element).data(),
+        config
+      )
+
+      Util.typeCheckConfig(
+        NAME,
+        config,
+        this.constructor.DefaultType
+      )
+
+      return config
+    }
+
+    _getMenuElement() {
+      if (!this._menu) {
+        let parent = Dropdown._getParentFromElement(this._element)
+        this._menu = $(parent).find(Selector.MENU)[0]
+      }
+      return this._menu
+    }
 
     // static
 
     static _jQueryInterface(config) {
       return this.each(function () {
         let data = $(this).data(DATA_KEY)
+        let _config = typeof config === 'object' ? config : null
 
         if (!data) {
-          data = new Dropdown(this)
+          data = new Dropdown(this, _config)
           $(this).data(DATA_KEY, data)
         }
 
@@ -164,13 +240,18 @@ const Dropdown = (($) => {
       }
 
       const toggles = $.makeArray($(Selector.DATA_TOGGLE))
-
       for (let i = 0; i < toggles.length; i++) {
         const parent        = Dropdown._getParentFromElement(toggles[i])
+        let context         = $(toggles[i]).data(DATA_KEY)
         const relatedTarget = {
           relatedTarget : toggles[i]
         }
 
+        if (!context) {
+          continue
+        }
+
+        let dropdownMenu = context._menu
         if (!$(parent).hasClass(ClassName.SHOW)) {
           continue
         }
@@ -195,6 +276,7 @@ const Dropdown = (($) => {
 
         toggles[i].setAttribute('aria-expanded', 'false')
 
+        $(dropdownMenu).removeClass(ClassName.SHOW)
         $(parent)
           .removeClass(ClassName.SHOW)
           .trigger($.Event(Event.HIDDEN, relatedTarget))
index c380f8675f929eec8f3a4fba14323cd73db84c36..2c22a7ed41207f9cf74f4a61c71282b7d1bac324 100644 (file)
@@ -14,7 +14,7 @@ const Tooltip = (($) => {
 
   /**
    * Check for Popper dependency
-   * Tether - https://popper.js.org
+   * Popper - https://popper.js.org
    */
   if (typeof Popper === 'undefined') {
     throw new Error('Bootstrap tooltips require Popper (https://popper.js.org)')
index cc8a6e709c2a680ef8123ea8ec0cf10fd63173f9..d96b367e9919a7067d21d3271f1a288e1d2e2a53 100644 (file)
@@ -61,6 +61,7 @@
     </div>
 
     <script src="../../../docs/assets/js/vendor/jquery-slim.min.js"></script>
+    <script src="../../../docs/assets/js/vendor/popper.min.js"></script>
     <script src="../../dist/util.js"></script>
     <script src="../../dist/dropdown.js"></script>
     <script src="../../dist/collapse.js"></script>
index 3c5a5c66ed70afc1698abbdf9e58d37b5067ebb8..2c57f0209c65e5afcdce71d9bfa085d1e7d891ca 100644 (file)
 
 // Open state for the dropdown
 .show {
-  // Show the menu
-  > .dropdown-menu {
-    display: block;
-  }
-
   // Remove the outline when :focus is triggered
   > a {
     outline: 0;
   left: 0;
 }
 
+.dropdown-menu.show {
+  display: block;
+}
+
 // Dropdown section headers
 .dropdown-header {
   display: block;
 //
 // Just add .dropup after the standard .dropdown class and you're set.
 
-.dropup {
-  // Different positioning for bottom up menu
-  .dropdown-menu {
-    top: auto;
-    bottom: 100%;
-    margin-bottom: $dropdown-margin-top;
-  }
-}
+.dropup {}