Skip to content

Commit 54a8ab4

Browse files
committed
Begin to use Popper for Dropdown
1 parent d8996a7 commit 54a8ab4

File tree

4 files changed

+97
-22
lines changed

4 files changed

+97
-22
lines changed

js/src/dropdown.js

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ import Util from './util'
1010

1111
const Dropdown = (($) => {
1212

13+
/**
14+
* Check for Popper dependency
15+
* Popper - https://popper.js.org
16+
*/
17+
if (typeof Popper === 'undefined') {
18+
throw new Error('Bootstrap dropdown require Popper (https://popper.js.org)')
19+
}
1320

1421
/**
1522
* ------------------------------------------------------------------------
@@ -55,6 +62,20 @@ const Dropdown = (($) => {
5562
VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled)'
5663
}
5764

65+
const Default = {
66+
animation : true,
67+
trigger : 'click',
68+
placement : 'bottom',
69+
offset : '0 0'
70+
}
71+
72+
const DefaultType = {
73+
animation : 'boolean',
74+
trigger : 'string',
75+
placement : 'string',
76+
offset : 'string'
77+
}
78+
5879

5980
/**
6081
* ------------------------------------------------------------------------
@@ -64,8 +85,11 @@ const Dropdown = (($) => {
6485

6586
class Dropdown {
6687

67-
constructor(element) {
88+
constructor(element, config) {
6889
this._element = element
90+
this._popper = null
91+
this._config = this._getConfig(config)
92+
this._menu = this._getMenuElement()
6993

7094
this._addEventListeners()
7195
}
@@ -77,16 +101,30 @@ const Dropdown = (($) => {
77101
return VERSION
78102
}
79103

104+
static get Default() {
105+
return Default
106+
}
107+
108+
static get DefaultType() {
109+
return DefaultType
110+
}
111+
80112

81113
// public
82114

83115
toggle() {
84-
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {
116+
let context = $(this).data(DATA_KEY)
117+
if (!context) {
118+
context = new Dropdown(this)
119+
$(this).data(DATA_KEY, context)
120+
}
121+
122+
if (context.disabled || $(this).hasClass(ClassName.DISABLED)) {
85123
return false
86124
}
87125

88126
const parent = Dropdown._getParentFromElement(this)
89-
const isActive = $(parent).hasClass(ClassName.SHOW)
127+
const isActive = $(context._menu).hasClass(ClassName.SHOW)
90128

91129
Dropdown._clearMenus()
92130

@@ -97,14 +135,21 @@ const Dropdown = (($) => {
97135
const relatedTarget = {
98136
relatedTarget : this
99137
}
100-
const showEvent = $.Event(Event.SHOW, relatedTarget)
138+
const showEvent = $.Event(Event.SHOW, relatedTarget)
101139

102140
$(parent).trigger(showEvent)
103141

104142
if (showEvent.isDefaultPrevented()) {
105143
return false
106144
}
107145

146+
this._popper = new Popper(this, context._menu, {
147+
placement : context._config.placement,
148+
offsets : {
149+
popper : context._config.offset
150+
}
151+
})
152+
108153
// if this is a touch-enabled device we add extra
109154
// empty mouseover listeners to the body's immediate children;
110155
// only needed because of broken event delegation on iOS
@@ -117,8 +162,10 @@ const Dropdown = (($) => {
117162
this.focus()
118163
this.setAttribute('aria-expanded', true)
119164

120-
$(parent).toggleClass(ClassName.SHOW)
121-
$(parent).trigger($.Event(Event.SHOWN, relatedTarget))
165+
$(context._menu).toggleClass(ClassName.SHOW)
166+
$(parent)
167+
.toggleClass(ClassName.SHOW)
168+
.trigger($.Event(Event.SHOWN, relatedTarget))
122169

123170
return false
124171
}
@@ -127,6 +174,10 @@ const Dropdown = (($) => {
127174
$.removeData(this._element, DATA_KEY)
128175
$(this._element).off(EVENT_KEY)
129176
this._element = null
177+
this._menu = null
178+
if (this._popper !== null) {
179+
this._popper.destroy()
180+
}
130181
}
131182

132183

@@ -136,15 +187,40 @@ const Dropdown = (($) => {
136187
$(this._element).on(Event.CLICK, this.toggle)
137188
}
138189

190+
_getConfig(config) {
191+
config = $.extend(
192+
{},
193+
this.constructor.Default,
194+
$(this._element).data(),
195+
config
196+
)
197+
198+
Util.typeCheckConfig(
199+
NAME,
200+
config,
201+
this.constructor.DefaultType
202+
)
203+
204+
return config
205+
}
206+
207+
_getMenuElement() {
208+
if (!this._menu) {
209+
let parent = Dropdown._getParentFromElement(this._element)
210+
this._menu = $(parent).find(Selector.MENU)[0]
211+
}
212+
return this._menu
213+
}
139214

140215
// static
141216

142217
static _jQueryInterface(config) {
143218
return this.each(function () {
144219
let data = $(this).data(DATA_KEY)
220+
let _config = typeof config === 'object' ? config : null
145221

146222
if (!data) {
147-
data = new Dropdown(this)
223+
data = new Dropdown(this, _config)
148224
$(this).data(DATA_KEY, data)
149225
}
150226

@@ -164,13 +240,18 @@ const Dropdown = (($) => {
164240
}
165241

166242
const toggles = $.makeArray($(Selector.DATA_TOGGLE))
167-
168243
for (let i = 0; i < toggles.length; i++) {
169244
const parent = Dropdown._getParentFromElement(toggles[i])
245+
let context = $(toggles[i]).data(DATA_KEY)
170246
const relatedTarget = {
171247
relatedTarget : toggles[i]
172248
}
173249

250+
if (!context) {
251+
continue
252+
}
253+
254+
let dropdownMenu = context._menu
174255
if (!$(parent).hasClass(ClassName.SHOW)) {
175256
continue
176257
}
@@ -195,6 +276,7 @@ const Dropdown = (($) => {
195276

196277
toggles[i].setAttribute('aria-expanded', 'false')
197278

279+
$(dropdownMenu).removeClass(ClassName.SHOW)
198280
$(parent)
199281
.removeClass(ClassName.SHOW)
200282
.trigger($.Event(Event.HIDDEN, relatedTarget))

js/src/tooltip.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const Tooltip = (($) => {
1414

1515
/**
1616
* Check for Popper dependency
17-
* Tether - https://popper.js.org
17+
* Popper - https://popper.js.org
1818
*/
1919
if (typeof Popper === 'undefined') {
2020
throw new Error('Bootstrap tooltips require Popper (https://popper.js.org)')

js/tests/visual/dropdown.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ <h1>Dropdown <small>Bootstrap Visual Test</small></h1>
6161
</div>
6262

6363
<script src="../../../docs/assets/js/vendor/jquery-slim.min.js"></script>
64+
<script src="../../../docs/assets/js/vendor/popper.min.js"></script>
6465
<script src="../../dist/util.js"></script>
6566
<script src="../../dist/dropdown.js"></script>
6667
<script src="../../dist/collapse.js"></script>

scss/_dropdown.scss

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,6 @@
100100

101101
// Open state for the dropdown
102102
.show {
103-
// Show the menu
104-
> .dropdown-menu {
105-
display: block;
106-
}
107-
108103
// Remove the outline when :focus is triggered
109104
> a {
110105
outline: 0;
@@ -125,6 +120,10 @@
125120
left: 0;
126121
}
127122

123+
.dropdown-menu.show {
124+
display: block;
125+
}
126+
128127
// Dropdown section headers
129128
.dropdown-header {
130129
display: block;
@@ -139,11 +138,4 @@
139138
//
140139
// Just add .dropup after the standard .dropdown class and you're set.
141140

142-
.dropup {
143-
// Different positioning for bottom up menu
144-
.dropdown-menu {
145-
top: auto;
146-
bottom: 100%;
147-
margin-bottom: $dropdown-margin-top;
148-
}
149-
}
141+
.dropup {}

0 commit comments

Comments
 (0)