@openeuropa/bcl-bootstrap 0.24.1 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/bootstrap-icons.svg +1 -1
  2. package/icons/apple.svg +2 -2
  3. package/icons/boombox-fill.svg +2 -2
  4. package/icons/cup-fill.svg +1 -1
  5. package/icons/cup.svg +1 -1
  6. package/js/dist/alert.js +3 -12
  7. package/js/dist/alert.js.map +1 -1
  8. package/js/dist/base-component.js +32 -18
  9. package/js/dist/base-component.js.map +1 -1
  10. package/js/dist/button.js +3 -12
  11. package/js/dist/button.js.map +1 -1
  12. package/js/dist/carousel.js +207 -307
  13. package/js/dist/carousel.js.map +1 -1
  14. package/js/dist/collapse.js +57 -88
  15. package/js/dist/collapse.js.map +1 -1
  16. package/js/dist/dom/data.js +1 -3
  17. package/js/dist/dom/data.js.map +1 -1
  18. package/js/dist/dom/event-handler.js +87 -106
  19. package/js/dist/dom/event-handler.js.map +1 -1
  20. package/js/dist/dom/manipulator.js +21 -25
  21. package/js/dist/dom/manipulator.js.map +1 -1
  22. package/js/dist/dom/selector-engine.js +11 -10
  23. package/js/dist/dom/selector-engine.js.map +1 -1
  24. package/js/dist/dropdown.js +83 -115
  25. package/js/dist/dropdown.js.map +1 -1
  26. package/js/dist/modal.js +95 -152
  27. package/js/dist/modal.js.map +1 -1
  28. package/js/dist/offcanvas.js +75 -58
  29. package/js/dist/offcanvas.js.map +1 -1
  30. package/js/dist/popover.js +29 -56
  31. package/js/dist/popover.js.map +1 -1
  32. package/js/dist/scrollspy.js +176 -125
  33. package/js/dist/scrollspy.js.map +1 -1
  34. package/js/dist/tab.js +207 -92
  35. package/js/dist/tab.js.map +1 -1
  36. package/js/dist/toast.js +23 -37
  37. package/js/dist/toast.js.map +1 -1
  38. package/js/dist/tooltip.js +259 -348
  39. package/js/dist/tooltip.js.map +1 -1
  40. package/js/dist/util/backdrop.js +62 -39
  41. package/js/dist/util/backdrop.js.map +1 -1
  42. package/js/dist/util/component-functions.js +1 -1
  43. package/js/dist/util/component-functions.js.map +1 -1
  44. package/js/dist/util/config.js +75 -0
  45. package/js/dist/util/config.js.map +1 -0
  46. package/js/dist/util/focustrap.js +41 -34
  47. package/js/dist/util/focustrap.js.map +1 -1
  48. package/js/dist/util/index.js +56 -52
  49. package/js/dist/util/index.js.map +1 -1
  50. package/js/dist/util/sanitizer.js +12 -19
  51. package/js/dist/util/sanitizer.js.map +1 -1
  52. package/js/dist/util/scrollbar.js +49 -34
  53. package/js/dist/util/scrollbar.js.map +1 -1
  54. package/js/dist/util/swipe.js +151 -0
  55. package/js/dist/util/swipe.js.map +1 -0
  56. package/js/dist/util/template-factory.js +173 -0
  57. package/js/dist/util/template-factory.js.map +1 -0
  58. package/js/src/alert.js +3 -15
  59. package/js/src/base-component.js +28 -18
  60. package/js/src/button.js +3 -17
  61. package/js/src/carousel.js +203 -319
  62. package/js/src/collapse.js +61 -94
  63. package/js/src/dom/data.js +1 -3
  64. package/js/src/dom/event-handler.js +80 -108
  65. package/js/src/dom/manipulator.js +22 -31
  66. package/js/src/dom/selector-engine.js +10 -19
  67. package/js/src/dropdown.js +84 -138
  68. package/js/src/modal.js +94 -158
  69. package/js/src/offcanvas.js +72 -61
  70. package/js/src/popover.js +31 -62
  71. package/js/src/scrollspy.js +166 -171
  72. package/js/src/tab.js +193 -110
  73. package/js/src/toast.js +19 -41
  74. package/js/src/tooltip.js +259 -371
  75. package/js/src/util/backdrop.js +55 -36
  76. package/js/src/util/component-functions.js +1 -1
  77. package/js/src/util/config.js +66 -0
  78. package/js/src/util/focustrap.js +38 -28
  79. package/js/src/util/index.js +67 -64
  80. package/js/src/util/sanitizer.js +11 -19
  81. package/js/src/util/scrollbar.js +47 -30
  82. package/js/src/util/swipe.js +146 -0
  83. package/js/src/util/template-factory.js +160 -0
  84. package/package.json +4 -4
  85. package/scss/_accordion.scss +52 -24
  86. package/scss/_alert.scss +18 -4
  87. package/scss/_badge.scss +14 -5
  88. package/scss/_breadcrumb.scss +22 -10
  89. package/scss/_button-group.scss +3 -0
  90. package/scss/_buttons.scss +97 -22
  91. package/scss/_card.scss +55 -37
  92. package/scss/_close.scss +1 -1
  93. package/scss/_containers.scss +1 -1
  94. package/scss/_dropdown.scss +83 -75
  95. package/scss/_functions.scss +7 -7
  96. package/scss/_grid.scss +3 -3
  97. package/scss/_helpers.scss +1 -0
  98. package/scss/_list-group.scss +44 -27
  99. package/scss/_maps.scss +54 -0
  100. package/scss/_modal.scss +71 -43
  101. package/scss/_nav.scss +53 -20
  102. package/scss/_navbar.scss +91 -150
  103. package/scss/_offcanvas.scss +119 -59
  104. package/scss/_pagination.scss +66 -21
  105. package/scss/_placeholders.scss +1 -1
  106. package/scss/_popover.scss +90 -52
  107. package/scss/_progress.scss +20 -9
  108. package/scss/_reboot.scss +25 -40
  109. package/scss/_root.scss +40 -21
  110. package/scss/_spinners.scss +38 -22
  111. package/scss/_tables.scss +32 -23
  112. package/scss/_toasts.scss +35 -16
  113. package/scss/_tooltip.scss +61 -56
  114. package/scss/_type.scss +2 -0
  115. package/scss/_utilities.scss +43 -26
  116. package/scss/_variables.scss +113 -121
  117. package/scss/bootstrap-grid.scss +3 -6
  118. package/scss/bootstrap-reboot.scss +3 -7
  119. package/scss/bootstrap-utilities.scss +3 -6
  120. package/scss/bootstrap.scss +4 -6
  121. package/scss/forms/_floating-labels.scss +14 -3
  122. package/scss/forms/_form-check.scss +28 -5
  123. package/scss/forms/_form-control.scss +12 -37
  124. package/scss/forms/_form-select.scss +0 -1
  125. package/scss/forms/_input-group.scss +15 -7
  126. package/scss/helpers/_color-bg.scss +10 -0
  127. package/scss/helpers/_colored-links.scss +2 -2
  128. package/scss/helpers/_position.scss +7 -1
  129. package/scss/helpers/_ratio.scss +2 -2
  130. package/scss/helpers/_vr.scss +1 -1
  131. package/scss/mixins/_alert.scss +7 -3
  132. package/scss/mixins/_banner.scss +9 -0
  133. package/scss/mixins/_breakpoints.scss +8 -8
  134. package/scss/mixins/_buttons.scss +32 -95
  135. package/scss/mixins/_container.scss +4 -2
  136. package/scss/mixins/_forms.scss +8 -0
  137. package/scss/mixins/_gradients.scss +1 -1
  138. package/scss/mixins/_grid.scss +12 -12
  139. package/scss/mixins/_pagination.scss +4 -25
  140. package/scss/mixins/_reset-text.scss +1 -1
  141. package/scss/mixins/_table-variants.scss +12 -9
  142. package/scss/mixins/_utilities.scss +12 -4
@@ -1,28 +1,36 @@
1
1
  /**
2
2
  * --------------------------------------------------------------------------
3
- * Bootstrap (v5.1.3): dom/manipulator.js
3
+ * Bootstrap (v5.2.0): dom/manipulator.js
4
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
5
  * --------------------------------------------------------------------------
6
6
  */
7
7
 
8
- function normalizeData(val) {
9
- if (val === 'true') {
8
+ function normalizeData(value) {
9
+ if (value === 'true') {
10
10
  return true
11
11
  }
12
12
 
13
- if (val === 'false') {
13
+ if (value === 'false') {
14
14
  return false
15
15
  }
16
16
 
17
- if (val === Number(val).toString()) {
18
- return Number(val)
17
+ if (value === Number(value).toString()) {
18
+ return Number(value)
19
19
  }
20
20
 
21
- if (val === '' || val === 'null') {
21
+ if (value === '' || value === 'null') {
22
22
  return null
23
23
  }
24
24
 
25
- return val
25
+ if (typeof value !== 'string') {
26
+ return value
27
+ }
28
+
29
+ try {
30
+ return JSON.parse(decodeURIComponent(value))
31
+ } catch {
32
+ return value
33
+ }
26
34
  }
27
35
 
28
36
  function normalizeDataKey(key) {
@@ -44,36 +52,19 @@ const Manipulator = {
44
52
  }
45
53
 
46
54
  const attributes = {}
55
+ const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))
47
56
 
48
- Object.keys(element.dataset)
49
- .filter(key => key.startsWith('bs'))
50
- .forEach(key => {
51
- let pureKey = key.replace(/^bs/, '')
52
- pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)
53
- attributes[pureKey] = normalizeData(element.dataset[key])
54
- })
57
+ for (const key of bsKeys) {
58
+ let pureKey = key.replace(/^bs/, '')
59
+ pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)
60
+ attributes[pureKey] = normalizeData(element.dataset[key])
61
+ }
55
62
 
56
63
  return attributes
57
64
  },
58
65
 
59
66
  getDataAttribute(element, key) {
60
67
  return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))
61
- },
62
-
63
- offset(element) {
64
- const rect = element.getBoundingClientRect()
65
-
66
- return {
67
- top: rect.top + window.pageYOffset,
68
- left: rect.left + window.pageXOffset
69
- }
70
- },
71
-
72
- position(element) {
73
- return {
74
- top: element.offsetTop,
75
- left: element.offsetLeft
76
- }
77
68
  }
78
69
  }
79
70
 
@@ -1,20 +1,16 @@
1
1
  /**
2
2
  * --------------------------------------------------------------------------
3
- * Bootstrap (v5.1.3): dom/selector-engine.js
3
+ * Bootstrap (v5.2.0): dom/selector-engine.js
4
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
5
  * --------------------------------------------------------------------------
6
6
  */
7
7
 
8
+ import { isDisabled, isVisible } from '../util/index'
9
+
8
10
  /**
9
- * ------------------------------------------------------------------------
10
11
  * Constants
11
- * ------------------------------------------------------------------------
12
12
  */
13
13
 
14
- import { isDisabled, isVisible } from '../util/index'
15
-
16
- const NODE_TEXT = 3
17
-
18
14
  const SelectorEngine = {
19
15
  find(selector, element = document.documentElement) {
20
16
  return [].concat(...Element.prototype.querySelectorAll.call(element, selector))
@@ -25,21 +21,16 @@ const SelectorEngine = {
25
21
  },
26
22
 
27
23
  children(element, selector) {
28
- return [].concat(...element.children)
29
- .filter(child => child.matches(selector))
24
+ return [].concat(...element.children).filter(child => child.matches(selector))
30
25
  },
31
26
 
32
27
  parents(element, selector) {
33
28
  const parents = []
29
+ let ancestor = element.parentNode.closest(selector)
34
30
 
35
- let ancestor = element.parentNode
36
-
37
- while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {
38
- if (ancestor.matches(selector)) {
39
- parents.push(ancestor)
40
- }
41
-
42
- ancestor = ancestor.parentNode
31
+ while (ancestor) {
32
+ parents.push(ancestor)
33
+ ancestor = ancestor.parentNode.closest(selector)
43
34
  }
44
35
 
45
36
  return parents
@@ -58,7 +49,7 @@ const SelectorEngine = {
58
49
 
59
50
  return []
60
51
  },
61
-
52
+ // TODO: this is now unused; remove later along with prev()
62
53
  next(element, selector) {
63
54
  let next = element.nextElementSibling
64
55
 
@@ -83,7 +74,7 @@ const SelectorEngine = {
83
74
  'details',
84
75
  '[tabindex]',
85
76
  '[contenteditable="true"]'
86
- ].map(selector => `${selector}:not([tabindex^="-"])`).join(', ')
77
+ ].map(selector => `${selector}:not([tabindex^="-"])`).join(',')
87
78
 
88
79
  return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))
89
80
  }
@@ -1,23 +1,20 @@
1
1
  /**
2
2
  * --------------------------------------------------------------------------
3
- * Bootstrap (v5.1.3): dropdown.js
3
+ * Bootstrap (v5.2.0): dropdown.js
4
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
5
  * --------------------------------------------------------------------------
6
6
  */
7
7
 
8
8
  import * as Popper from '@popperjs/core'
9
-
10
9
  import {
11
10
  defineJQueryPlugin,
12
11
  getElement,
13
- getElementFromSelector,
14
12
  getNextActiveElement,
15
13
  isDisabled,
16
14
  isElement,
17
15
  isRTL,
18
16
  isVisible,
19
- noop,
20
- typeCheckConfig
17
+ noop
21
18
  } from './util/index'
22
19
  import EventHandler from './dom/event-handler'
23
20
  import Manipulator from './dom/manipulator'
@@ -25,9 +22,7 @@ import SelectorEngine from './dom/selector-engine'
25
22
  import BaseComponent from './base-component'
26
23
 
27
24
  /**
28
- * ------------------------------------------------------------------------
29
25
  * Constants
30
- * ------------------------------------------------------------------------
31
26
  */
32
27
 
33
28
  const NAME = 'dropdown'
@@ -36,14 +31,11 @@ const EVENT_KEY = `.${DATA_KEY}`
36
31
  const DATA_API_KEY = '.data-api'
37
32
 
38
33
  const ESCAPE_KEY = 'Escape'
39
- const SPACE_KEY = 'Space'
40
34
  const TAB_KEY = 'Tab'
41
35
  const ARROW_UP_KEY = 'ArrowUp'
42
36
  const ARROW_DOWN_KEY = 'ArrowDown'
43
37
  const RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button
44
38
 
45
- const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`)
46
-
47
39
  const EVENT_HIDE = `hide${EVENT_KEY}`
48
40
  const EVENT_HIDDEN = `hidden${EVENT_KEY}`
49
41
  const EVENT_SHOW = `show${EVENT_KEY}`
@@ -56,10 +48,13 @@ const CLASS_NAME_SHOW = 'show'
56
48
  const CLASS_NAME_DROPUP = 'dropup'
57
49
  const CLASS_NAME_DROPEND = 'dropend'
58
50
  const CLASS_NAME_DROPSTART = 'dropstart'
59
- const CLASS_NAME_NAVBAR = 'navbar'
51
+ const CLASS_NAME_DROPUP_CENTER = 'dropup-center'
52
+ const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'
60
53
 
61
- const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]'
54
+ const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'
55
+ const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`
62
56
  const SELECTOR_MENU = '.dropdown-menu'
57
+ const SELECTOR_NAVBAR = '.navbar'
63
58
  const SELECTOR_NAVBAR_NAV = '.navbar-nav'
64
59
  const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
65
60
 
@@ -69,43 +64,42 @@ const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'
69
64
  const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'
70
65
  const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'
71
66
  const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'
67
+ const PLACEMENT_TOPCENTER = 'top'
68
+ const PLACEMENT_BOTTOMCENTER = 'bottom'
72
69
 
73
70
  const Default = {
74
- offset: [0, 2],
71
+ autoClose: true,
75
72
  boundary: 'clippingParents',
76
- reference: 'toggle',
77
73
  display: 'dynamic',
74
+ offset: [0, 2],
78
75
  popperConfig: null,
79
- autoClose: true
76
+ reference: 'toggle'
80
77
  }
81
78
 
82
79
  const DefaultType = {
83
- offset: '(array|string|function)',
80
+ autoClose: '(boolean|string)',
84
81
  boundary: '(string|element)',
85
- reference: '(string|element|object)',
86
82
  display: 'string',
83
+ offset: '(array|string|function)',
87
84
  popperConfig: '(null|object|function)',
88
- autoClose: '(boolean|string)'
85
+ reference: '(string|element|object)'
89
86
  }
90
87
 
91
88
  /**
92
- * ------------------------------------------------------------------------
93
- * Class Definition
94
- * ------------------------------------------------------------------------
89
+ * Class definition
95
90
  */
96
91
 
97
92
  class Dropdown extends BaseComponent {
98
93
  constructor(element, config) {
99
- super(element)
94
+ super(element, config)
100
95
 
101
96
  this._popper = null
102
- this._config = this._getConfig(config)
103
- this._menu = this._getMenuElement()
97
+ this._parent = this._element.parentNode // dropdown wrapper
98
+ this._menu = SelectorEngine.findOne(SELECTOR_MENU, this._parent)
104
99
  this._inNavbar = this._detectNavbar()
105
100
  }
106
101
 
107
102
  // Getters
108
-
109
103
  static get Default() {
110
104
  return Default
111
105
  }
@@ -119,13 +113,12 @@ class Dropdown extends BaseComponent {
119
113
  }
120
114
 
121
115
  // Public
122
-
123
116
  toggle() {
124
117
  return this._isShown() ? this.hide() : this.show()
125
118
  }
126
119
 
127
120
  show() {
128
- if (isDisabled(this._element) || this._isShown(this._menu)) {
121
+ if (isDisabled(this._element) || this._isShown()) {
129
122
  return
130
123
  }
131
124
 
@@ -139,22 +132,16 @@ class Dropdown extends BaseComponent {
139
132
  return
140
133
  }
141
134
 
142
- const parent = Dropdown.getParentFromElement(this._element)
143
- // Totally disable Popper for Dropdowns in Navbar
144
- if (this._inNavbar) {
145
- Manipulator.setDataAttribute(this._menu, 'popper', 'none')
146
- } else {
147
- this._createPopper(parent)
148
- }
135
+ this._createPopper()
149
136
 
150
137
  // If this is a touch-enabled device we add extra
151
138
  // empty mouseover listeners to the body's immediate children;
152
139
  // only needed because of broken event delegation on iOS
153
140
  // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
154
- if ('ontouchstart' in document.documentElement &&
155
- !parent.closest(SELECTOR_NAVBAR_NAV)) {
156
- [].concat(...document.body.children)
157
- .forEach(elem => EventHandler.on(elem, 'mouseover', noop))
141
+ if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {
142
+ for (const element of [].concat(...document.body.children)) {
143
+ EventHandler.on(element, 'mouseover', noop)
144
+ }
158
145
  }
159
146
 
160
147
  this._element.focus()
@@ -166,7 +153,7 @@ class Dropdown extends BaseComponent {
166
153
  }
167
154
 
168
155
  hide() {
169
- if (isDisabled(this._element) || !this._isShown(this._menu)) {
156
+ if (isDisabled(this._element) || !this._isShown()) {
170
157
  return
171
158
  }
172
159
 
@@ -193,7 +180,6 @@ class Dropdown extends BaseComponent {
193
180
  }
194
181
 
195
182
  // Private
196
-
197
183
  _completeHide(relatedTarget) {
198
184
  const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)
199
185
  if (hideEvent.defaultPrevented) {
@@ -203,8 +189,9 @@ class Dropdown extends BaseComponent {
203
189
  // If this is a touch-enabled device we remove the extra
204
190
  // empty mouseover listeners we added for iOS support
205
191
  if ('ontouchstart' in document.documentElement) {
206
- [].concat(...document.body.children)
207
- .forEach(elem => EventHandler.off(elem, 'mouseover', noop))
192
+ for (const element of [].concat(...document.body.children)) {
193
+ EventHandler.off(element, 'mouseover', noop)
194
+ }
208
195
  }
209
196
 
210
197
  if (this._popper) {
@@ -219,13 +206,7 @@ class Dropdown extends BaseComponent {
219
206
  }
220
207
 
221
208
  _getConfig(config) {
222
- config = {
223
- ...this.constructor.Default,
224
- ...Manipulator.getDataAttributes(this._element),
225
- ...config
226
- }
227
-
228
- typeCheckConfig(NAME, config, this.constructor.DefaultType)
209
+ config = super._getConfig(config)
229
210
 
230
211
  if (typeof config.reference === 'object' && !isElement(config.reference) &&
231
212
  typeof config.reference.getBoundingClientRect !== 'function'
@@ -237,7 +218,7 @@ class Dropdown extends BaseComponent {
237
218
  return config
238
219
  }
239
220
 
240
- _createPopper(parent) {
221
+ _createPopper() {
241
222
  if (typeof Popper === 'undefined') {
242
223
  throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)')
243
224
  }
@@ -245,7 +226,7 @@ class Dropdown extends BaseComponent {
245
226
  let referenceElement = this._element
246
227
 
247
228
  if (this._config.reference === 'parent') {
248
- referenceElement = parent
229
+ referenceElement = this._parent
249
230
  } else if (isElement(this._config.reference)) {
250
231
  referenceElement = getElement(this._config.reference)
251
232
  } else if (typeof this._config.reference === 'object') {
@@ -253,25 +234,15 @@ class Dropdown extends BaseComponent {
253
234
  }
254
235
 
255
236
  const popperConfig = this._getPopperConfig()
256
- const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false)
257
-
258
237
  this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)
259
-
260
- if (isDisplayStatic) {
261
- Manipulator.setDataAttribute(this._menu, 'popper', 'static')
262
- }
263
238
  }
264
239
 
265
- _isShown(element = this._element) {
266
- return element.classList.contains(CLASS_NAME_SHOW)
267
- }
268
-
269
- _getMenuElement() {
270
- return SelectorEngine.next(this._element, SELECTOR_MENU)[0]
240
+ _isShown() {
241
+ return this._menu.classList.contains(CLASS_NAME_SHOW)
271
242
  }
272
243
 
273
244
  _getPlacement() {
274
- const parentDropdown = this._element.parentNode
245
+ const parentDropdown = this._parent
275
246
 
276
247
  if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
277
248
  return PLACEMENT_RIGHT
@@ -281,6 +252,14 @@ class Dropdown extends BaseComponent {
281
252
  return PLACEMENT_LEFT
282
253
  }
283
254
 
255
+ if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {
256
+ return PLACEMENT_TOPCENTER
257
+ }
258
+
259
+ if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {
260
+ return PLACEMENT_BOTTOMCENTER
261
+ }
262
+
284
263
  // We need to trim the value because custom properties can also include spaces
285
264
  const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'
286
265
 
@@ -292,14 +271,14 @@ class Dropdown extends BaseComponent {
292
271
  }
293
272
 
294
273
  _detectNavbar() {
295
- return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null
274
+ return this._element.closest(SELECTOR_NAVBAR) !== null
296
275
  }
297
276
 
298
277
  _getOffset() {
299
278
  const { offset } = this._config
300
279
 
301
280
  if (typeof offset === 'string') {
302
- return offset.split(',').map(val => Number.parseInt(val, 10))
281
+ return offset.split(',').map(value => Number.parseInt(value, 10))
303
282
  }
304
283
 
305
284
  if (typeof offset === 'function') {
@@ -326,8 +305,9 @@ class Dropdown extends BaseComponent {
326
305
  }]
327
306
  }
328
307
 
329
- // Disable Popper if we have a static display
330
- if (this._config.display === 'static') {
308
+ // Disable Popper if we have a static display or Dropdown is in Navbar
309
+ if (this._inNavbar || this._config.display === 'static') {
310
+ Manipulator.setDataAttribute(this._menu, 'popper', 'static') // todo:v6 remove
331
311
  defaultBsPopperConfig.modifiers = [{
332
312
  name: 'applyStyles',
333
313
  enabled: false
@@ -341,7 +321,7 @@ class Dropdown extends BaseComponent {
341
321
  }
342
322
 
343
323
  _selectMenuItem({ key, target }) {
344
- const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)
324
+ const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))
345
325
 
346
326
  if (!items.length) {
347
327
  return
@@ -353,7 +333,6 @@ class Dropdown extends BaseComponent {
353
333
  }
354
334
 
355
335
  // Static
356
-
357
336
  static jQueryInterface(config) {
358
337
  return this.each(function () {
359
338
  const data = Dropdown.getOrCreateInstance(this, config)
@@ -371,111 +350,81 @@ class Dropdown extends BaseComponent {
371
350
  }
372
351
 
373
352
  static clearMenus(event) {
374
- if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) {
353
+ if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {
375
354
  return
376
355
  }
377
356
 
378
- const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
357
+ const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)
379
358
 
380
- for (let i = 0, len = toggles.length; i < len; i++) {
381
- const context = Dropdown.getInstance(toggles[i])
359
+ for (const toggle of openToggles) {
360
+ const context = Dropdown.getInstance(toggle)
382
361
  if (!context || context._config.autoClose === false) {
383
362
  continue
384
363
  }
385
364
 
386
- if (!context._isShown()) {
365
+ const composedPath = event.composedPath()
366
+ const isMenuTarget = composedPath.includes(context._menu)
367
+ if (
368
+ composedPath.includes(context._element) ||
369
+ (context._config.autoClose === 'inside' && !isMenuTarget) ||
370
+ (context._config.autoClose === 'outside' && isMenuTarget)
371
+ ) {
387
372
  continue
388
373
  }
389
374
 
390
- const relatedTarget = {
391
- relatedTarget: context._element
375
+ // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
376
+ if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
377
+ continue
392
378
  }
393
379
 
394
- if (event) {
395
- const composedPath = event.composedPath()
396
- const isMenuTarget = composedPath.includes(context._menu)
397
- if (
398
- composedPath.includes(context._element) ||
399
- (context._config.autoClose === 'inside' && !isMenuTarget) ||
400
- (context._config.autoClose === 'outside' && isMenuTarget)
401
- ) {
402
- continue
403
- }
404
-
405
- // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
406
- if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
407
- continue
408
- }
380
+ const relatedTarget = { relatedTarget: context._element }
409
381
 
410
- if (event.type === 'click') {
411
- relatedTarget.clickEvent = event
412
- }
382
+ if (event.type === 'click') {
383
+ relatedTarget.clickEvent = event
413
384
  }
414
385
 
415
386
  context._completeHide(relatedTarget)
416
387
  }
417
388
  }
418
389
 
419
- static getParentFromElement(element) {
420
- return getElementFromSelector(element) || element.parentNode
421
- }
422
-
423
390
  static dataApiKeydownHandler(event) {
424
- // If not input/textarea:
425
- // - And not a key in REGEXP_KEYDOWN => not a dropdown command
426
- // If input/textarea:
427
- // - If space key => not a dropdown command
428
- // - If key is other than escape
429
- // - If key is not up or down => not a dropdown command
430
- // - If trigger inside the menu => not a dropdown command
431
- if (/input|textarea/i.test(event.target.tagName) ?
432
- event.key === SPACE_KEY || (event.key !== ESCAPE_KEY &&
433
- ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) ||
434
- event.target.closest(SELECTOR_MENU))) :
435
- !REGEXP_KEYDOWN.test(event.key)) {
436
- return
437
- }
391
+ // If not an UP | DOWN | ESCAPE key => not a dropdown command
392
+ // If input/textarea && if key is other than ESCAPE => not a dropdown command
438
393
 
439
- const isActive = this.classList.contains(CLASS_NAME_SHOW)
394
+ const isInput = /input|textarea/i.test(event.target.tagName)
395
+ const isEscapeEvent = event.key === ESCAPE_KEY
396
+ const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)
440
397
 
441
- if (!isActive && event.key === ESCAPE_KEY) {
398
+ if (!isUpOrDownEvent && !isEscapeEvent) {
442
399
  return
443
400
  }
444
401
 
445
- event.preventDefault()
446
- event.stopPropagation()
447
-
448
- if (isDisabled(this)) {
402
+ if (isInput && !isEscapeEvent) {
449
403
  return
450
404
  }
451
405
 
452
- const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
453
- const instance = Dropdown.getOrCreateInstance(getToggleButton)
454
-
455
- if (event.key === ESCAPE_KEY) {
456
- instance.hide()
457
- return
458
- }
406
+ event.preventDefault()
459
407
 
460
- if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {
461
- if (!isActive) {
462
- instance.show()
463
- }
408
+ const getToggleButton = SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode)
409
+ const instance = Dropdown.getOrCreateInstance(getToggleButton)
464
410
 
411
+ if (isUpOrDownEvent) {
412
+ event.stopPropagation()
413
+ instance.show()
465
414
  instance._selectMenuItem(event)
466
415
  return
467
416
  }
468
417
 
469
- if (!isActive || event.key === SPACE_KEY) {
470
- Dropdown.clearMenus()
418
+ if (instance._isShown()) { // else is escape and we check if it is shown
419
+ event.stopPropagation()
420
+ instance.hide()
421
+ getToggleButton.focus()
471
422
  }
472
423
  }
473
424
  }
474
425
 
475
426
  /**
476
- * ------------------------------------------------------------------------
477
- * Data Api implementation
478
- * ------------------------------------------------------------------------
427
+ * Data API implementation
479
428
  */
480
429
 
481
430
  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)
@@ -488,10 +437,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
488
437
  })
489
438
 
490
439
  /**
491
- * ------------------------------------------------------------------------
492
440
  * jQuery
493
- * ------------------------------------------------------------------------
494
- * add .Dropdown to jQuery only if jQuery is present
495
441
  */
496
442
 
497
443
  defineJQueryPlugin(Dropdown)