@coreui/coreui 4.1.5 → 4.2.1

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 (203) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +64 -11
  3. package/dist/css/coreui-grid.css +23 -949
  4. package/dist/css/coreui-grid.css.map +1 -1
  5. package/dist/css/coreui-grid.min.css +3 -3
  6. package/dist/css/coreui-grid.min.css.map +1 -1
  7. package/dist/css/coreui-grid.rtl.css +25 -951
  8. package/dist/css/coreui-grid.rtl.css.map +1 -1
  9. package/dist/css/coreui-grid.rtl.min.css +5 -5
  10. package/dist/css/coreui-grid.rtl.min.css.map +1 -1
  11. package/dist/css/coreui-reboot.css +35 -42
  12. package/dist/css/coreui-reboot.css.map +1 -1
  13. package/dist/css/coreui-reboot.min.css +3 -3
  14. package/dist/css/coreui-reboot.min.css.map +1 -1
  15. package/dist/css/coreui-reboot.rtl.css +36 -44
  16. package/dist/css/coreui-reboot.rtl.css.map +1 -1
  17. package/dist/css/coreui-reboot.rtl.min.css +5 -5
  18. package/dist/css/coreui-reboot.rtl.min.css.map +1 -1
  19. package/dist/css/coreui-utilities.css +279 -951
  20. package/dist/css/coreui-utilities.css.map +1 -1
  21. package/dist/css/coreui-utilities.min.css +3 -3
  22. package/dist/css/coreui-utilities.min.css.map +1 -1
  23. package/dist/css/coreui-utilities.rtl.css +275 -953
  24. package/dist/css/coreui-utilities.rtl.css.map +1 -1
  25. package/dist/css/coreui-utilities.rtl.min.css +5 -5
  26. package/dist/css/coreui-utilities.rtl.min.css.map +1 -1
  27. package/dist/css/coreui.css +2179 -2259
  28. package/dist/css/coreui.css.map +1 -1
  29. package/dist/css/coreui.min.css +3 -3
  30. package/dist/css/coreui.min.css.map +1 -1
  31. package/dist/css/coreui.rtl.css +2166 -2253
  32. package/dist/css/coreui.rtl.css.map +1 -1
  33. package/dist/css/coreui.rtl.min.css +5 -5
  34. package/dist/css/coreui.rtl.min.css.map +1 -1
  35. package/dist/js/coreui.bundle.js +2095 -1906
  36. package/dist/js/coreui.bundle.js.map +1 -1
  37. package/dist/js/coreui.bundle.min.js +2 -2
  38. package/dist/js/coreui.bundle.min.js.map +1 -1
  39. package/dist/js/coreui.esm.js +2098 -1909
  40. package/dist/js/coreui.esm.js.map +1 -1
  41. package/dist/js/coreui.esm.min.js +2 -2
  42. package/dist/js/coreui.esm.min.js.map +1 -1
  43. package/dist/js/coreui.js +2099 -1910
  44. package/dist/js/coreui.js.map +1 -1
  45. package/dist/js/coreui.min.js +2 -2
  46. package/dist/js/coreui.min.js.map +1 -1
  47. package/js/dist/alert.js +10 -148
  48. package/js/dist/alert.js.map +1 -1
  49. package/js/dist/base-component.js +36 -122
  50. package/js/dist/base-component.js.map +1 -1
  51. package/js/dist/button.js +9 -76
  52. package/js/dist/button.js.map +1 -1
  53. package/js/dist/carousel.js +212 -507
  54. package/js/dist/carousel.js.map +1 -1
  55. package/js/dist/collapse.js +64 -251
  56. package/js/dist/collapse.js.map +1 -1
  57. package/js/dist/dom/data.js +2 -4
  58. package/js/dist/dom/data.js.map +1 -1
  59. package/js/dist/dom/event-handler.js +82 -133
  60. package/js/dist/dom/event-handler.js.map +1 -1
  61. package/js/dist/dom/manipulator.js +22 -26
  62. package/js/dist/dom/manipulator.js.map +1 -1
  63. package/js/dist/dom/selector-engine.js +16 -81
  64. package/js/dist/dom/selector-engine.js.map +1 -1
  65. package/js/dist/dropdown.js +99 -338
  66. package/js/dist/dropdown.js.map +1 -1
  67. package/js/dist/modal.js +106 -774
  68. package/js/dist/modal.js.map +1 -1
  69. package/js/dist/navigation.js +309 -0
  70. package/js/dist/navigation.js.map +1 -0
  71. package/js/dist/offcanvas.js +88 -680
  72. package/js/dist/offcanvas.js.map +1 -1
  73. package/js/dist/popover.js +35 -120
  74. package/js/dist/popover.js.map +1 -1
  75. package/js/dist/scrollspy.js +178 -264
  76. package/js/dist/scrollspy.js.map +1 -1
  77. package/js/dist/sidebar.js +347 -0
  78. package/js/dist/sidebar.js.map +1 -0
  79. package/js/dist/tab.js +226 -216
  80. package/js/dist/tab.js.map +1 -1
  81. package/js/dist/toast.js +27 -216
  82. package/js/dist/toast.js.map +1 -1
  83. package/js/dist/tooltip.js +271 -618
  84. package/js/dist/tooltip.js.map +1 -1
  85. package/js/dist/util/backdrop.js +166 -0
  86. package/js/dist/util/backdrop.js.map +1 -0
  87. package/js/dist/util/component-functions.js +47 -0
  88. package/js/dist/util/component-functions.js.map +1 -0
  89. package/js/dist/util/config.js +80 -0
  90. package/js/dist/util/config.js.map +1 -0
  91. package/js/dist/util/focustrap.js +130 -0
  92. package/js/dist/util/focustrap.js.map +1 -0
  93. package/js/dist/util/index.js +354 -0
  94. package/js/dist/util/index.js.map +1 -0
  95. package/js/dist/util/sanitizer.js +126 -0
  96. package/js/dist/util/sanitizer.js.map +1 -0
  97. package/js/dist/util/scrollbar.js +139 -0
  98. package/js/dist/util/scrollbar.js.map +1 -0
  99. package/js/dist/util/swipe.js +156 -0
  100. package/js/dist/util/swipe.js.map +1 -0
  101. package/js/dist/util/template-factory.js +178 -0
  102. package/js/dist/util/template-factory.js.map +1 -0
  103. package/js/src/alert.js +3 -15
  104. package/js/src/base-component.js +28 -18
  105. package/js/src/button.js +3 -15
  106. package/js/src/carousel.js +203 -320
  107. package/js/src/collapse.js +61 -94
  108. package/js/src/dom/data.js +1 -3
  109. package/js/src/dom/event-handler.js +74 -107
  110. package/js/src/dom/manipulator.js +22 -31
  111. package/js/src/dom/selector-engine.js +10 -19
  112. package/js/src/dropdown.js +84 -138
  113. package/js/src/modal.js +94 -158
  114. package/js/src/navigation.js +12 -13
  115. package/js/src/offcanvas.js +71 -60
  116. package/js/src/popover.js +31 -62
  117. package/js/src/scrollspy.js +166 -171
  118. package/js/src/sidebar.js +5 -8
  119. package/js/src/tab.js +201 -110
  120. package/js/src/toast.js +19 -41
  121. package/js/src/tooltip.js +264 -374
  122. package/js/src/util/backdrop.js +55 -36
  123. package/js/src/util/component-functions.js +1 -1
  124. package/js/src/util/config.js +66 -0
  125. package/js/src/util/focustrap.js +38 -28
  126. package/js/src/util/index.js +41 -57
  127. package/js/src/util/sanitizer.js +9 -17
  128. package/js/src/util/scrollbar.js +47 -30
  129. package/js/src/util/swipe.js +146 -0
  130. package/js/src/util/template-factory.js +160 -0
  131. package/package.json +40 -40
  132. package/scss/_accordion.scss +53 -25
  133. package/scss/_alert.scss +29 -9
  134. package/scss/_badge.scss +15 -6
  135. package/scss/_breadcrumb.scss +23 -11
  136. package/scss/_button-group.scss +3 -0
  137. package/scss/_buttons.scss +77 -50
  138. package/scss/_callout.scss +18 -6
  139. package/scss/_card.scss +55 -37
  140. package/scss/_carousel.scss +6 -6
  141. package/scss/_close.scss +4 -4
  142. package/scss/_containers.scss +1 -1
  143. package/scss/_dropdown.scss +86 -64
  144. package/scss/_footer.scss +15 -5
  145. package/scss/_functions.scss +11 -24
  146. package/scss/_grid.scss +3 -3
  147. package/scss/_header.scss +59 -34
  148. package/scss/_helpers.scss +1 -0
  149. package/scss/_images.scss +3 -3
  150. package/scss/_list-group.scss +47 -29
  151. package/scss/_maps.scss +54 -0
  152. package/scss/_modal.scss +70 -43
  153. package/scss/_nav.scss +53 -20
  154. package/scss/_navbar.scss +84 -94
  155. package/scss/_offcanvas.scss +120 -60
  156. package/scss/_pagination.scss +66 -21
  157. package/scss/_popover.scss +90 -52
  158. package/scss/_progress.scss +20 -9
  159. package/scss/_reboot.scss +31 -58
  160. package/scss/_root.scss +41 -21
  161. package/scss/_spinners.scss +37 -21
  162. package/scss/_subheader.scss +9 -9
  163. package/scss/_tables.scss +34 -36
  164. package/scss/_toasts.scss +35 -19
  165. package/scss/_tooltip.scss +61 -56
  166. package/scss/_utilities.scss +42 -27
  167. package/scss/_variables.scss +169 -148
  168. package/scss/coreui-grid.rtl.scss +2 -2
  169. package/scss/coreui-grid.scss +3 -2
  170. package/scss/coreui-reboot.rtl.scss +2 -2
  171. package/scss/coreui-reboot.scss +2 -2
  172. package/scss/coreui-utilities.rtl.scss +2 -2
  173. package/scss/coreui-utilities.scss +3 -2
  174. package/scss/coreui.rtl.scss +2 -2
  175. package/scss/coreui.scss +3 -2
  176. package/scss/forms/_floating-labels.scss +14 -3
  177. package/scss/forms/_form-check.scss +42 -19
  178. package/scss/forms/_form-control.scss +25 -50
  179. package/scss/forms/_form-range.scss +8 -8
  180. package/scss/forms/_form-select.scss +8 -8
  181. package/scss/forms/_form-text.scss +1 -1
  182. package/scss/forms/_input-group.scss +3 -3
  183. package/scss/forms/_labels.scss +2 -2
  184. package/scss/helpers/_color-bg.scss +10 -0
  185. package/scss/helpers/_colored-links.scss +2 -2
  186. package/scss/helpers/_position.scss +7 -1
  187. package/scss/helpers/_ratio.scss +2 -2
  188. package/scss/helpers/_vr.scss +1 -0
  189. package/scss/mixins/_alert.scss +10 -10
  190. package/scss/mixins/_breakpoints.scss +8 -8
  191. package/scss/mixins/_buttons.scss +45 -47
  192. package/scss/mixins/_container.scss +4 -2
  193. package/scss/mixins/_css-vars.scss +47 -47
  194. package/scss/mixins/_forms.scss +8 -0
  195. package/scss/mixins/_gradients.scss +1 -1
  196. package/scss/mixins/_grid.scss +11 -11
  197. package/scss/mixins/_list-group.scss +7 -9
  198. package/scss/mixins/_pagination.scss +4 -25
  199. package/scss/mixins/_table-variants.scss +20 -12
  200. package/scss/mixins/_utilities.scss +8 -3
  201. package/scss/sidebar/_sidebar-narrow.scss +10 -10
  202. package/scss/sidebar/_sidebar-nav.scss +33 -32
  203. package/scss/sidebar/_sidebar.scss +110 -56
package/js/src/tooltip.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * --------------------------------------------------------------------------
3
- * CoreUI (v4.1.5): tooltip.js
3
+ * CoreUI (v4.2.1): tooltip.js
4
4
  * Licensed under MIT (https://coreui.io/license)
5
5
  *
6
6
  * This component is a modified version of the Bootstrap's tooltip.js
@@ -9,55 +9,44 @@
9
9
  */
10
10
 
11
11
  import * as Popper from '@popperjs/core'
12
-
13
- import {
14
- defineJQueryPlugin,
15
- findShadowRoot,
16
- getElement,
17
- getUID,
18
- isElement,
19
- isRTL,
20
- noop,
21
- typeCheckConfig
22
- } from './util/index'
23
- import { DefaultAllowlist, sanitizeHtml } from './util/sanitizer'
24
- import Data from './dom/data'
12
+ import { defineJQueryPlugin, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index'
13
+ import { DefaultAllowlist } from './util/sanitizer'
25
14
  import EventHandler from './dom/event-handler'
26
15
  import Manipulator from './dom/manipulator'
27
- import SelectorEngine from './dom/selector-engine'
28
16
  import BaseComponent from './base-component'
17
+ import TemplateFactory from './util/template-factory'
29
18
 
30
19
  /**
31
- * ------------------------------------------------------------------------
32
20
  * Constants
33
- * ------------------------------------------------------------------------
34
21
  */
35
22
 
36
23
  const NAME = 'tooltip'
37
- const DATA_KEY = 'coreui.tooltip'
38
- const EVENT_KEY = `.${DATA_KEY}`
39
- const CLASS_PREFIX = 'bs-tooltip'
40
24
  const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])
41
25
 
42
- const DefaultType = {
43
- animation: 'boolean',
44
- template: 'string',
45
- title: '(string|element|function)',
46
- trigger: 'string',
47
- delay: '(number|object)',
48
- html: 'boolean',
49
- selector: '(string|boolean)',
50
- placement: '(string|function)',
51
- offset: '(array|string|function)',
52
- container: '(string|element|boolean)',
53
- fallbackPlacements: 'array',
54
- boundary: '(string|element)',
55
- customClass: '(string|function)',
56
- sanitize: 'boolean',
57
- sanitizeFn: '(null|function)',
58
- allowList: 'object',
59
- popperConfig: '(null|object|function)'
60
- }
26
+ const CLASS_NAME_FADE = 'fade'
27
+ const CLASS_NAME_MODAL = 'modal'
28
+ const CLASS_NAME_SHOW = 'show'
29
+
30
+ const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
31
+ const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`
32
+
33
+ const EVENT_MODAL_HIDE = 'hide.coreui.modal'
34
+
35
+ const TRIGGER_HOVER = 'hover'
36
+ const TRIGGER_FOCUS = 'focus'
37
+ const TRIGGER_CLICK = 'click'
38
+ const TRIGGER_MANUAL = 'manual'
39
+
40
+ const EVENT_HIDE = 'hide'
41
+ const EVENT_HIDDEN = 'hidden'
42
+ const EVENT_SHOW = 'show'
43
+ const EVENT_SHOWN = 'shown'
44
+ const EVENT_INSERTED = 'inserted'
45
+ const EVENT_CLICK = 'click'
46
+ const EVENT_FOCUSIN = 'focusin'
47
+ const EVENT_FOCUSOUT = 'focusout'
48
+ const EVENT_MOUSEENTER = 'mouseenter'
49
+ const EVENT_MOUSELEAVE = 'mouseleave'
61
50
 
62
51
  const AttachmentMap = {
63
52
  AUTO: 'auto',
@@ -68,62 +57,50 @@ const AttachmentMap = {
68
57
  }
69
58
 
70
59
  const Default = {
60
+ allowList: DefaultAllowlist,
71
61
  animation: true,
72
- template: '<div class="tooltip" role="tooltip">' +
73
- '<div class="tooltip-arrow"></div>' +
74
- '<div class="tooltip-inner"></div>' +
75
- '</div>',
76
- trigger: 'hover focus',
77
- title: '',
62
+ boundary: 'clippingParents',
63
+ container: false,
64
+ customClass: '',
78
65
  delay: 0,
66
+ fallbackPlacements: ['top', 'right', 'bottom', 'left'],
79
67
  html: false,
80
- selector: false,
81
- placement: 'top',
82
68
  offset: [0, 0],
83
- container: false,
84
- fallbackPlacements: ['top', 'right', 'bottom', 'left'],
85
- boundary: 'clippingParents',
86
- customClass: '',
69
+ placement: 'top',
70
+ popperConfig: null,
87
71
  sanitize: true,
88
72
  sanitizeFn: null,
89
- allowList: DefaultAllowlist,
90
- popperConfig: null
73
+ selector: false,
74
+ template: '<div class="tooltip" role="tooltip">' +
75
+ '<div class="tooltip-arrow"></div>' +
76
+ '<div class="tooltip-inner"></div>' +
77
+ '</div>',
78
+ title: '',
79
+ trigger: 'hover focus'
91
80
  }
92
81
 
93
- const Event = {
94
- HIDE: `hide${EVENT_KEY}`,
95
- HIDDEN: `hidden${EVENT_KEY}`,
96
- SHOW: `show${EVENT_KEY}`,
97
- SHOWN: `shown${EVENT_KEY}`,
98
- INSERTED: `inserted${EVENT_KEY}`,
99
- CLICK: `click${EVENT_KEY}`,
100
- FOCUSIN: `focusin${EVENT_KEY}`,
101
- FOCUSOUT: `focusout${EVENT_KEY}`,
102
- MOUSEENTER: `mouseenter${EVENT_KEY}`,
103
- MOUSELEAVE: `mouseleave${EVENT_KEY}`
82
+ const DefaultType = {
83
+ allowList: 'object',
84
+ animation: 'boolean',
85
+ boundary: '(string|element)',
86
+ container: '(string|element|boolean)',
87
+ customClass: '(string|function)',
88
+ delay: '(number|object)',
89
+ fallbackPlacements: 'array',
90
+ html: 'boolean',
91
+ offset: '(array|string|function)',
92
+ placement: '(string|function)',
93
+ popperConfig: '(null|object|function)',
94
+ sanitize: 'boolean',
95
+ sanitizeFn: '(null|function)',
96
+ selector: '(string|boolean)',
97
+ template: 'string',
98
+ title: '(string|element|function)',
99
+ trigger: 'string'
104
100
  }
105
101
 
106
- const CLASS_NAME_FADE = 'fade'
107
- const CLASS_NAME_MODAL = 'modal'
108
- const CLASS_NAME_SHOW = 'show'
109
-
110
- const HOVER_STATE_SHOW = 'show'
111
- const HOVER_STATE_OUT = 'out'
112
-
113
- const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
114
- const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`
115
-
116
- const EVENT_MODAL_HIDE = 'hide.coreui.modal'
117
-
118
- const TRIGGER_HOVER = 'hover'
119
- const TRIGGER_FOCUS = 'focus'
120
- const TRIGGER_CLICK = 'click'
121
- const TRIGGER_MANUAL = 'manual'
122
-
123
102
  /**
124
- * ------------------------------------------------------------------------
125
- * Class Definition
126
- * ------------------------------------------------------------------------
103
+ * Class definition
127
104
  */
128
105
 
129
106
  class Tooltip extends BaseComponent {
@@ -132,42 +109,36 @@ class Tooltip extends BaseComponent {
132
109
  throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')
133
110
  }
134
111
 
135
- super(element)
112
+ super(element, config)
136
113
 
137
- // private
114
+ // Private
138
115
  this._isEnabled = true
139
116
  this._timeout = 0
140
- this._hoverState = ''
117
+ this._isHovered = false
141
118
  this._activeTrigger = {}
142
119
  this._popper = null
120
+ this._templateFactory = null
143
121
 
144
122
  // Protected
145
- this._config = this._getConfig(config)
146
123
  this.tip = null
147
124
 
148
125
  this._setListeners()
149
126
  }
150
127
 
151
128
  // Getters
152
-
153
129
  static get Default() {
154
130
  return Default
155
131
  }
156
132
 
157
- static get NAME() {
158
- return NAME
159
- }
160
-
161
- static get Event() {
162
- return Event
163
- }
164
-
165
133
  static get DefaultType() {
166
134
  return DefaultType
167
135
  }
168
136
 
169
- // Public
137
+ static get NAME() {
138
+ return NAME
139
+ }
170
140
 
141
+ // Public
171
142
  enable() {
172
143
  this._isEnabled = true
173
144
  }
@@ -191,18 +162,20 @@ class Tooltip extends BaseComponent {
191
162
  context._activeTrigger.click = !context._activeTrigger.click
192
163
 
193
164
  if (context._isWithActiveTrigger()) {
194
- context._enter(null, context)
165
+ context._enter()
195
166
  } else {
196
- context._leave(null, context)
197
- }
198
- } else {
199
- if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {
200
- this._leave(null, this)
201
- return
167
+ context._leave()
202
168
  }
203
169
 
204
- this._enter(null, this)
170
+ return
171
+ }
172
+
173
+ if (this._isShown()) {
174
+ this._leave()
175
+ return
205
176
  }
177
+
178
+ this._enter()
206
179
  }
207
180
 
208
181
  dispose() {
@@ -223,241 +196,215 @@ class Tooltip extends BaseComponent {
223
196
  throw new Error('Please use show on visible elements')
224
197
  }
225
198
 
226
- if (!(this.isWithContent() && this._isEnabled)) {
199
+ if (!(this._isWithContent() && this._isEnabled)) {
227
200
  return
228
201
  }
229
202
 
230
- const showEvent = EventHandler.trigger(this._element, this.constructor.Event.SHOW)
203
+ const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW))
231
204
  const shadowRoot = findShadowRoot(this._element)
232
- const isInTheDom = shadowRoot === null ?
233
- this._element.ownerDocument.documentElement.contains(this._element) :
234
- shadowRoot.contains(this._element)
205
+ const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)
235
206
 
236
207
  if (showEvent.defaultPrevented || !isInTheDom) {
237
208
  return
238
209
  }
239
210
 
240
- // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-coreui-original-title`
241
- // This will be removed later in favor of a `setContent` method
242
- if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {
243
- this._disposePopper()
244
- this.tip.remove()
245
- this.tip = null
246
- }
211
+ const tip = this._getTipElement()
247
212
 
248
- const tip = this.getTipElement()
249
- const tipId = getUID(this.constructor.NAME)
250
-
251
- tip.setAttribute('id', tipId)
252
- this._element.setAttribute('aria-describedby', tipId)
253
-
254
- if (this._config.animation) {
255
- tip.classList.add(CLASS_NAME_FADE)
256
- }
257
-
258
- const placement = typeof this._config.placement === 'function' ?
259
- this._config.placement.call(this, tip, this._element) :
260
- this._config.placement
261
-
262
- const attachment = this._getAttachment(placement)
263
- this._addAttachmentClass(attachment)
213
+ this._element.setAttribute('aria-describedby', tip.getAttribute('id'))
264
214
 
265
215
  const { container } = this._config
266
- Data.set(tip, this.constructor.DATA_KEY, this)
267
216
 
268
217
  if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
269
218
  container.append(tip)
270
- EventHandler.trigger(this._element, this.constructor.Event.INSERTED)
219
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))
271
220
  }
272
221
 
273
222
  if (this._popper) {
274
223
  this._popper.update()
275
224
  } else {
276
- this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
225
+ this._createPopper(tip)
277
226
  }
278
227
 
279
228
  tip.classList.add(CLASS_NAME_SHOW)
280
229
 
281
- const customClass = this._resolvePossibleFunction(this._config.customClass)
282
- if (customClass) {
283
- tip.classList.add(...customClass.split(' '))
284
- }
285
-
286
230
  // If this is a touch-enabled device we add extra
287
231
  // empty mouseover listeners to the body's immediate children;
288
232
  // only needed because of broken event delegation on iOS
289
233
  // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
290
234
  if ('ontouchstart' in document.documentElement) {
291
- [].concat(...document.body.children).forEach(element => {
235
+ for (const element of [].concat(...document.body.children)) {
292
236
  EventHandler.on(element, 'mouseover', noop)
293
- })
237
+ }
294
238
  }
295
239
 
296
240
  const complete = () => {
297
- const prevHoverState = this._hoverState
241
+ const previousHoverState = this._isHovered
298
242
 
299
- this._hoverState = null
300
- EventHandler.trigger(this._element, this.constructor.Event.SHOWN)
243
+ this._isHovered = false
244
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))
301
245
 
302
- if (prevHoverState === HOVER_STATE_OUT) {
303
- this._leave(null, this)
246
+ if (previousHoverState) {
247
+ this._leave()
304
248
  }
305
249
  }
306
250
 
307
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)
308
- this._queueCallback(complete, this.tip, isAnimated)
251
+ this._queueCallback(complete, this.tip, this._isAnimated())
309
252
  }
310
253
 
311
254
  hide() {
312
- if (!this._popper) {
255
+ if (!this._isShown()) {
313
256
  return
314
257
  }
315
258
 
316
- const tip = this.getTipElement()
317
- const complete = () => {
318
- if (this._isWithActiveTrigger()) {
319
- return
320
- }
321
-
322
- if (this._hoverState !== HOVER_STATE_SHOW) {
323
- tip.remove()
324
- }
325
-
326
- this._cleanTipClass()
327
- this._element.removeAttribute('aria-describedby')
328
- EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)
329
-
330
- this._disposePopper()
331
- }
332
-
333
- const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE)
259
+ const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE))
334
260
  if (hideEvent.defaultPrevented) {
335
261
  return
336
262
  }
337
263
 
264
+ const tip = this._getTipElement()
338
265
  tip.classList.remove(CLASS_NAME_SHOW)
339
266
 
340
267
  // If this is a touch-enabled device we remove the extra
341
268
  // empty mouseover listeners we added for iOS support
342
269
  if ('ontouchstart' in document.documentElement) {
343
- [].concat(...document.body.children)
344
- .forEach(element => EventHandler.off(element, 'mouseover', noop))
270
+ for (const element of [].concat(...document.body.children)) {
271
+ EventHandler.off(element, 'mouseover', noop)
272
+ }
345
273
  }
346
274
 
347
275
  this._activeTrigger[TRIGGER_CLICK] = false
348
276
  this._activeTrigger[TRIGGER_FOCUS] = false
349
277
  this._activeTrigger[TRIGGER_HOVER] = false
278
+ this._isHovered = false
279
+
280
+ const complete = () => {
281
+ if (this._isWithActiveTrigger()) {
282
+ return
283
+ }
284
+
285
+ if (!this._isHovered) {
286
+ tip.remove()
287
+ }
288
+
289
+ this._element.removeAttribute('aria-describedby')
290
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))
291
+
292
+ this._disposePopper()
293
+ }
350
294
 
351
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)
352
- this._queueCallback(complete, this.tip, isAnimated)
353
- this._hoverState = ''
295
+ this._queueCallback(complete, this.tip, this._isAnimated())
354
296
  }
355
297
 
356
298
  update() {
357
- if (this._popper !== null) {
299
+ if (this._popper) {
358
300
  this._popper.update()
359
301
  }
360
302
  }
361
303
 
362
304
  // Protected
363
-
364
- isWithContent() {
365
- return Boolean(this.getTitle())
305
+ _isWithContent() {
306
+ return Boolean(this._getTitle())
366
307
  }
367
308
 
368
- getTipElement() {
369
- if (this.tip) {
370
- return this.tip
309
+ _getTipElement() {
310
+ if (!this.tip) {
311
+ this.tip = this._createTipElement(this._getContentForTemplate())
371
312
  }
372
313
 
373
- const element = document.createElement('div')
374
- element.innerHTML = this._config.template
375
-
376
- const tip = element.children[0]
377
- this.setContent(tip)
378
- tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
379
-
380
- this.tip = tip
381
314
  return this.tip
382
315
  }
383
316
 
384
- setContent(tip) {
385
- this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER)
386
- }
387
-
388
- _sanitizeAndSetContent(template, content, selector) {
389
- const templateElement = SelectorEngine.findOne(selector, template)
317
+ _createTipElement(content) {
318
+ const tip = this._getTemplateFactory(content).toHtml()
390
319
 
391
- if (!content && templateElement) {
392
- templateElement.remove()
393
- return
320
+ // todo: remove this check on v6
321
+ if (!tip) {
322
+ return null
394
323
  }
395
324
 
396
- // we use append for html objects to maintain js events
397
- this.setElementContent(templateElement, content)
398
- }
399
-
400
- setElementContent(element, content) {
401
- if (element === null) {
402
- return
403
- }
325
+ tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
326
+ // todo: on v6 the following can be achieved with CSS only
327
+ tip.classList.add(`bs-${this.constructor.NAME}-auto`)
404
328
 
405
- if (isElement(content)) {
406
- content = getElement(content)
329
+ const tipId = getUID(this.constructor.NAME).toString()
407
330
 
408
- // content is a DOM node or a jQuery
409
- if (this._config.html) {
410
- if (content.parentNode !== element) {
411
- element.innerHTML = ''
412
- element.append(content)
413
- }
414
- } else {
415
- element.textContent = content.textContent
416
- }
331
+ tip.setAttribute('id', tipId)
417
332
 
418
- return
333
+ if (this._isAnimated()) {
334
+ tip.classList.add(CLASS_NAME_FADE)
419
335
  }
420
336
 
421
- if (this._config.html) {
422
- if (this._config.sanitize) {
423
- content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn)
424
- }
337
+ return tip
338
+ }
425
339
 
426
- element.innerHTML = content
427
- } else {
428
- element.textContent = content
340
+ setContent(content) {
341
+ let isShown = false
342
+ if (this.tip) {
343
+ isShown = this._isShown()
344
+ this.tip.remove()
345
+ this.tip = null
429
346
  }
430
- }
431
347
 
432
- getTitle() {
433
- const title = this._element.getAttribute('data-coreui-original-title') || this._config.title
348
+ this._disposePopper()
349
+ this.tip = this._createTipElement(content)
434
350
 
435
- return this._resolvePossibleFunction(title)
351
+ if (isShown) {
352
+ this.show()
353
+ }
436
354
  }
437
355
 
438
- updateAttachment(attachment) {
439
- if (attachment === 'right') {
440
- return 'end'
356
+ _getTemplateFactory(content) {
357
+ if (this._templateFactory) {
358
+ this._templateFactory.changeContent(content)
359
+ } else {
360
+ this._templateFactory = new TemplateFactory({
361
+ ...this._config,
362
+ // the `content` var has to be after `this._config`
363
+ // to override config.content in case of popover
364
+ content,
365
+ extraClass: this._resolvePossibleFunction(this._config.customClass)
366
+ })
441
367
  }
442
368
 
443
- if (attachment === 'left') {
444
- return 'start'
369
+ return this._templateFactory
370
+ }
371
+
372
+ _getContentForTemplate() {
373
+ return {
374
+ [SELECTOR_TOOLTIP_INNER]: this._getTitle()
445
375
  }
376
+ }
446
377
 
447
- return attachment
378
+ _getTitle() {
379
+ return this._config.title
448
380
  }
449
381
 
450
382
  // Private
383
+ _initializeOnDelegatedTarget(event) {
384
+ return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())
385
+ }
386
+
387
+ _isAnimated() {
388
+ return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))
389
+ }
390
+
391
+ _isShown() {
392
+ return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)
393
+ }
451
394
 
452
- _initializeOnDelegatedTarget(event, context) {
453
- return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())
395
+ _createPopper(tip) {
396
+ const placement = typeof this._config.placement === 'function' ?
397
+ this._config.placement.call(this, tip, this._element) :
398
+ this._config.placement
399
+ const attachment = AttachmentMap[placement.toUpperCase()]
400
+ this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
454
401
  }
455
402
 
456
403
  _getOffset() {
457
404
  const { offset } = this._config
458
405
 
459
406
  if (typeof offset === 'string') {
460
- return offset.split(',').map(val => Number.parseInt(val, 10))
407
+ return offset.split(',').map(value => Number.parseInt(value, 10))
461
408
  }
462
409
 
463
410
  if (typeof offset === 'function') {
@@ -467,8 +414,8 @@ class Tooltip extends BaseComponent {
467
414
  return offset
468
415
  }
469
416
 
470
- _resolvePossibleFunction(content) {
471
- return typeof content === 'function' ? content.call(this._element) : content
417
+ _resolvePossibleFunction(arg) {
418
+ return typeof arg === 'function' ? arg.call(this._element) : arg
472
419
  }
473
420
 
474
421
  _getPopperConfig(attachment) {
@@ -500,17 +447,16 @@ class Tooltip extends BaseComponent {
500
447
  }
501
448
  },
502
449
  {
503
- name: 'onChange',
450
+ name: 'preSetPlacement',
504
451
  enabled: true,
505
- phase: 'afterWrite',
506
- fn: data => this._handlePopperPlacementChange(data)
507
- }
508
- ],
509
- onFirstUpdate: data => {
510
- if (data.options.placement !== data.placement) {
511
- this._handlePopperPlacementChange(data)
452
+ phase: 'beforeMain',
453
+ fn: data => {
454
+ // Pre-set Popper's placement attribute in order to read the arrow sizes properly.
455
+ // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
456
+ this._getTipElement().setAttribute('data-popper-placement', data.state.placement)
457
+ }
512
458
  }
513
- }
459
+ ]
514
460
  }
515
461
 
516
462
  return {
@@ -519,32 +465,34 @@ class Tooltip extends BaseComponent {
519
465
  }
520
466
  }
521
467
 
522
- _addAttachmentClass(attachment) {
523
- this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`)
524
- }
525
-
526
- _getAttachment(placement) {
527
- return AttachmentMap[placement.toUpperCase()]
528
- }
529
-
530
468
  _setListeners() {
531
469
  const triggers = this._config.trigger.split(' ')
532
470
 
533
- triggers.forEach(trigger => {
471
+ for (const trigger of triggers) {
534
472
  if (trigger === 'click') {
535
- EventHandler.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event))
473
+ EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => this.toggle(event))
536
474
  } else if (trigger !== TRIGGER_MANUAL) {
537
475
  const eventIn = trigger === TRIGGER_HOVER ?
538
- this.constructor.Event.MOUSEENTER :
539
- this.constructor.Event.FOCUSIN
476
+ this.constructor.eventName(EVENT_MOUSEENTER) :
477
+ this.constructor.eventName(EVENT_FOCUSIN)
540
478
  const eventOut = trigger === TRIGGER_HOVER ?
541
- this.constructor.Event.MOUSELEAVE :
542
- this.constructor.Event.FOCUSOUT
543
-
544
- EventHandler.on(this._element, eventIn, this._config.selector, event => this._enter(event))
545
- EventHandler.on(this._element, eventOut, this._config.selector, event => this._leave(event))
479
+ this.constructor.eventName(EVENT_MOUSELEAVE) :
480
+ this.constructor.eventName(EVENT_FOCUSOUT)
481
+
482
+ EventHandler.on(this._element, eventIn, this._config.selector, event => {
483
+ const context = this._initializeOnDelegatedTarget(event)
484
+ context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true
485
+ context._enter()
486
+ })
487
+ EventHandler.on(this._element, eventOut, this._config.selector, event => {
488
+ const context = this._initializeOnDelegatedTarget(event)
489
+ context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =
490
+ context._element.contains(event.relatedTarget)
491
+
492
+ context._leave()
493
+ })
546
494
  }
547
- })
495
+ }
548
496
 
549
497
  this._hideModalHandler = () => {
550
498
  if (this._element) {
@@ -566,103 +514,77 @@ class Tooltip extends BaseComponent {
566
514
  }
567
515
 
568
516
  _fixTitle() {
569
- const title = this._element.getAttribute('title')
570
- const originalTitleType = typeof this._element.getAttribute('data-coreui-original-title')
571
-
572
- if (title || originalTitleType !== 'string') {
573
- this._element.setAttribute('data-coreui-original-title', title || '')
574
- if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
575
- this._element.setAttribute('aria-label', title)
576
- }
577
-
578
- this._element.setAttribute('title', '')
579
- }
580
- }
581
-
582
- _enter(event, context) {
583
- context = this._initializeOnDelegatedTarget(event, context)
584
-
585
- if (event) {
586
- context._activeTrigger[
587
- event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER
588
- ] = true
589
- }
517
+ const title = this._config.originalTitle
590
518
 
591
- if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
592
- context._hoverState = HOVER_STATE_SHOW
519
+ if (!title) {
593
520
  return
594
521
  }
595
522
 
596
- clearTimeout(context._timeout)
597
-
598
- context._hoverState = HOVER_STATE_SHOW
599
-
600
- if (!context._config.delay || !context._config.delay.show) {
601
- context.show()
602
- return
523
+ if (!this._element.getAttribute('aria-label') && !this._element.textContent) {
524
+ this._element.setAttribute('aria-label', title)
603
525
  }
604
526
 
605
- context._timeout = setTimeout(() => {
606
- if (context._hoverState === HOVER_STATE_SHOW) {
607
- context.show()
608
- }
609
- }, context._config.delay.show)
527
+ this._element.removeAttribute('title')
610
528
  }
611
529
 
612
- _leave(event, context) {
613
- context = this._initializeOnDelegatedTarget(event, context)
614
-
615
- if (event) {
616
- context._activeTrigger[
617
- event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER
618
- ] = context._element.contains(event.relatedTarget)
619
- }
620
-
621
- if (context._isWithActiveTrigger()) {
530
+ _enter() {
531
+ if (this._isShown() || this._isHovered) {
532
+ this._isHovered = true
622
533
  return
623
534
  }
624
535
 
625
- clearTimeout(context._timeout)
536
+ this._isHovered = true
626
537
 
627
- context._hoverState = HOVER_STATE_OUT
538
+ this._setTimeout(() => {
539
+ if (this._isHovered) {
540
+ this.show()
541
+ }
542
+ }, this._config.delay.show)
543
+ }
628
544
 
629
- if (!context._config.delay || !context._config.delay.hide) {
630
- context.hide()
545
+ _leave() {
546
+ if (this._isWithActiveTrigger()) {
631
547
  return
632
548
  }
633
549
 
634
- context._timeout = setTimeout(() => {
635
- if (context._hoverState === HOVER_STATE_OUT) {
636
- context.hide()
550
+ this._isHovered = false
551
+
552
+ this._setTimeout(() => {
553
+ if (!this._isHovered) {
554
+ this.hide()
637
555
  }
638
- }, context._config.delay.hide)
556
+ }, this._config.delay.hide)
639
557
  }
640
558
 
641
- _isWithActiveTrigger() {
642
- for (const trigger in this._activeTrigger) {
643
- if (this._activeTrigger[trigger]) {
644
- return true
645
- }
646
- }
559
+ _setTimeout(handler, timeout) {
560
+ clearTimeout(this._timeout)
561
+ this._timeout = setTimeout(handler, timeout)
562
+ }
647
563
 
648
- return false
564
+ _isWithActiveTrigger() {
565
+ return Object.values(this._activeTrigger).includes(true)
649
566
  }
650
567
 
651
568
  _getConfig(config) {
652
569
  const dataAttributes = Manipulator.getDataAttributes(this._element)
653
570
 
654
- Object.keys(dataAttributes).forEach(dataAttr => {
655
- if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {
656
- delete dataAttributes[dataAttr]
571
+ for (const dataAttribute of Object.keys(dataAttributes)) {
572
+ if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {
573
+ delete dataAttributes[dataAttribute]
657
574
  }
658
- })
575
+ }
659
576
 
660
577
  config = {
661
- ...this.constructor.Default,
662
578
  ...dataAttributes,
663
579
  ...(typeof config === 'object' && config ? config : {})
664
580
  }
581
+ config = this._mergeConfigObj(config)
582
+ config = this._configAfterMerge(config)
583
+ this._typeCheckConfig(config)
584
+ return config
585
+ }
665
586
 
587
+ _configAfterMerge(config) {
666
588
  config.container = config.container === false ? document.body : getElement(config.container)
667
589
 
668
590
  if (typeof config.delay === 'number') {
@@ -672,6 +594,8 @@ class Tooltip extends BaseComponent {
672
594
  }
673
595
  }
674
596
 
597
+ config.originalTitle = this._element.getAttribute('title') || ''
598
+ config.title = this._resolvePossibleFunction(config.title) || config.originalTitle
675
599
  if (typeof config.title === 'number') {
676
600
  config.title = config.title.toString()
677
601
  }
@@ -680,12 +604,6 @@ class Tooltip extends BaseComponent {
680
604
  config.content = config.content.toString()
681
605
  }
682
606
 
683
- typeCheckConfig(NAME, config, this.constructor.DefaultType)
684
-
685
- if (config.sanitize) {
686
- config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn)
687
- }
688
-
689
607
  return config
690
608
  }
691
609
 
@@ -704,32 +622,6 @@ class Tooltip extends BaseComponent {
704
622
  return config
705
623
  }
706
624
 
707
- _cleanTipClass() {
708
- const tip = this.getTipElement()
709
- const basicClassPrefixRegex = new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`, 'g')
710
- const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex)
711
- if (tabClass !== null && tabClass.length > 0) {
712
- tabClass.map(token => token.trim())
713
- .forEach(tClass => tip.classList.remove(tClass))
714
- }
715
- }
716
-
717
- _getBasicClassPrefix() {
718
- return CLASS_PREFIX
719
- }
720
-
721
- _handlePopperPlacementChange(popperData) {
722
- const { state } = popperData
723
-
724
- if (!state) {
725
- return
726
- }
727
-
728
- this.tip = state.elements.popper
729
- this._cleanTipClass()
730
- this._addAttachmentClass(this._getAttachment(state.placement))
731
- }
732
-
733
625
  _disposePopper() {
734
626
  if (this._popper) {
735
627
  this._popper.destroy()
@@ -738,27 +630,25 @@ class Tooltip extends BaseComponent {
738
630
  }
739
631
 
740
632
  // Static
741
-
742
633
  static jQueryInterface(config) {
743
634
  return this.each(function () {
744
635
  const data = Tooltip.getOrCreateInstance(this, config)
745
636
 
746
- if (typeof config === 'string') {
747
- if (typeof data[config] === 'undefined') {
748
- throw new TypeError(`No method named "${config}"`)
749
- }
637
+ if (typeof config !== 'string') {
638
+ return
639
+ }
750
640
 
751
- data[config]()
641
+ if (typeof data[config] === 'undefined') {
642
+ throw new TypeError(`No method named "${config}"`)
752
643
  }
644
+
645
+ data[config]()
753
646
  })
754
647
  }
755
648
  }
756
649
 
757
650
  /**
758
- * ------------------------------------------------------------------------
759
651
  * jQuery
760
- * ------------------------------------------------------------------------
761
- * add .Tooltip to jQuery only if jQuery is present
762
652
  */
763
653
 
764
654
  defineJQueryPlugin(Tooltip)