@gorse/shards-vue 1.0.8

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 (173) hide show
  1. package/.editorconfig +13 -0
  2. package/CHANGELOG.md +49 -0
  3. package/CONTRIBUTING.md +92 -0
  4. package/ISSUE_TEMPLATE.md +19 -0
  5. package/LICENSE +21 -0
  6. package/README.md +157 -0
  7. package/build/optimize.js +49 -0
  8. package/build/paths.js +12 -0
  9. package/build/rollup.config.js +106 -0
  10. package/dist/shards-vue.common.js +13994 -0
  11. package/dist/shards-vue.common.js.map +1 -0
  12. package/dist/shards-vue.common.min.js +1 -0
  13. package/dist/shards-vue.common.min.map +1 -0
  14. package/dist/shards-vue.esm.js +13990 -0
  15. package/dist/shards-vue.esm.js.map +1 -0
  16. package/dist/shards-vue.esm.min.js +1 -0
  17. package/dist/shards-vue.esm.min.map +1 -0
  18. package/dist/shards-vue.umd.js +13997 -0
  19. package/dist/shards-vue.umd.js.map +1 -0
  20. package/dist/shards-vue.umd.min.js +1 -0
  21. package/dist/shards-vue.umd.min.map +1 -0
  22. package/logo.jpg +0 -0
  23. package/package.json +121 -0
  24. package/sandbox/Sandbox.vue +45 -0
  25. package/src/components/alert/Alert.vue +156 -0
  26. package/src/components/alert/README.md +86 -0
  27. package/src/components/alert/index.js +17 -0
  28. package/src/components/badge/Badge.vue +62 -0
  29. package/src/components/badge/README.md +112 -0
  30. package/src/components/badge/index.js +17 -0
  31. package/src/components/breadcrumb/Breadcrumb.vue +55 -0
  32. package/src/components/breadcrumb/BreadcrumbItem.vue +42 -0
  33. package/src/components/breadcrumb/BreadcrumbLink.vue +56 -0
  34. package/src/components/breadcrumb/README.md +53 -0
  35. package/src/components/breadcrumb/index.js +23 -0
  36. package/src/components/button/Button.vue +107 -0
  37. package/src/components/button/ButtonClose.vue +47 -0
  38. package/src/components/button/README.md +197 -0
  39. package/src/components/button/index.js +21 -0
  40. package/src/components/button-group/ButtonGroup.vue +66 -0
  41. package/src/components/button-group/README.md +72 -0
  42. package/src/components/button-group/index.js +18 -0
  43. package/src/components/button-toolbar/ButtonToolbar.vue +29 -0
  44. package/src/components/button-toolbar/README.md +24 -0
  45. package/src/components/button-toolbar/index.js +18 -0
  46. package/src/components/card/Card.vue +57 -0
  47. package/src/components/card/CardBody.vue +92 -0
  48. package/src/components/card/CardFooter.vue +61 -0
  49. package/src/components/card/CardGroup.vue +47 -0
  50. package/src/components/card/CardHeader.vue +61 -0
  51. package/src/components/card/CardImg.vue +64 -0
  52. package/src/components/card/README.md +96 -0
  53. package/src/components/card/index.js +27 -0
  54. package/src/components/collapse/Collapse.vue +209 -0
  55. package/src/components/collapse/README.md +86 -0
  56. package/src/components/collapse/index.js +17 -0
  57. package/src/components/container/Col.vue +125 -0
  58. package/src/components/container/Container.vue +31 -0
  59. package/src/components/container/README.md +91 -0
  60. package/src/components/container/Row.vue +64 -0
  61. package/src/components/container/index.js +21 -0
  62. package/src/components/datepicker/Datepicker.vue +391 -0
  63. package/src/components/datepicker/README.md +185 -0
  64. package/src/components/datepicker/index.js +17 -0
  65. package/src/components/dropdown/Dropdown.vue +442 -0
  66. package/src/components/dropdown/DropdownDivider.vue +22 -0
  67. package/src/components/dropdown/DropdownHeader.vue +29 -0
  68. package/src/components/dropdown/DropdownItem.vue +25 -0
  69. package/src/components/dropdown/README.md +177 -0
  70. package/src/components/dropdown/index.js +23 -0
  71. package/src/components/embed/Embed.vue +47 -0
  72. package/src/components/embed/README.md +23 -0
  73. package/src/components/embed/index.js +17 -0
  74. package/src/components/form/Form.vue +40 -0
  75. package/src/components/form/FormFeedback.vue +39 -0
  76. package/src/components/form/FormInvalidFeedback.vue +39 -0
  77. package/src/components/form/FormRow.vue +21 -0
  78. package/src/components/form/FormText.vue +41 -0
  79. package/src/components/form/FormValidFeedback.vue +39 -0
  80. package/src/components/form/README.md +84 -0
  81. package/src/components/form/index.js +29 -0
  82. package/src/components/form-checkbox/FormCheckbox.vue +200 -0
  83. package/src/components/form-checkbox/README.md +131 -0
  84. package/src/components/form-checkbox/index.js +18 -0
  85. package/src/components/form-input/FormInput.vue +176 -0
  86. package/src/components/form-input/README.md +110 -0
  87. package/src/components/form-input/index.js +18 -0
  88. package/src/components/form-radio/FormRadio.vue +155 -0
  89. package/src/components/form-radio/README.md +61 -0
  90. package/src/components/form-radio/index.js +18 -0
  91. package/src/components/form-select/FormSelect.vue +246 -0
  92. package/src/components/form-select/README.md +180 -0
  93. package/src/components/form-select/index.js +18 -0
  94. package/src/components/form-textarea/FormTextarea.vue +250 -0
  95. package/src/components/form-textarea/README.md +74 -0
  96. package/src/components/form-textarea/index.js +18 -0
  97. package/src/components/image/Image.vue +123 -0
  98. package/src/components/image/README.md +32 -0
  99. package/src/components/image/index.js +18 -0
  100. package/src/components/index.js +63 -0
  101. package/src/components/input-group/InputGroup.vue +125 -0
  102. package/src/components/input-group/InputGroupAddon.vue +58 -0
  103. package/src/components/input-group/InputGroupText.vue +20 -0
  104. package/src/components/input-group/README.md +188 -0
  105. package/src/components/input-group/index.js +21 -0
  106. package/src/components/link/Link.vue +157 -0
  107. package/src/components/link/README.md +27 -0
  108. package/src/components/link/create-link-props.js +54 -0
  109. package/src/components/link/index.js +17 -0
  110. package/src/components/list-group/ListGroup.vue +30 -0
  111. package/src/components/list-group/ListGroupItem.vue +90 -0
  112. package/src/components/list-group/README.md +23 -0
  113. package/src/components/list-group/index.js +19 -0
  114. package/src/components/modal/Modal.vue +121 -0
  115. package/src/components/modal/ModalBody.vue +20 -0
  116. package/src/components/modal/ModalFooter.vue +21 -0
  117. package/src/components/modal/ModalHeader.vue +39 -0
  118. package/src/components/modal/ModalTitle.vue +21 -0
  119. package/src/components/modal/README.md +74 -0
  120. package/src/components/modal/index.js +25 -0
  121. package/src/components/nav/Nav.vue +62 -0
  122. package/src/components/nav/NavItem.vue +23 -0
  123. package/src/components/nav/NavText.vue +21 -0
  124. package/src/components/nav/README.md +94 -0
  125. package/src/components/nav/index.js +19 -0
  126. package/src/components/navbar/Navbar.vue +63 -0
  127. package/src/components/navbar/NavbarBrand.vue +41 -0
  128. package/src/components/navbar/NavbarNav.vue +38 -0
  129. package/src/components/navbar/NavbarToggle.vue +55 -0
  130. package/src/components/navbar/README.md +51 -0
  131. package/src/components/navbar/index.js +23 -0
  132. package/src/components/popover/Popover.vue +126 -0
  133. package/src/components/popover/README.md +73 -0
  134. package/src/components/popover/index.js +17 -0
  135. package/src/components/progress/Progress.vue +93 -0
  136. package/src/components/progress/ProgressBar.vue +123 -0
  137. package/src/components/progress/README.md +95 -0
  138. package/src/components/progress/index.js +19 -0
  139. package/src/components/slider/README.md +120 -0
  140. package/src/components/slider/Slider.vue +103 -0
  141. package/src/components/slider/index.js +17 -0
  142. package/src/components/tabs/README.md +111 -0
  143. package/src/components/tabs/Tab.vue +132 -0
  144. package/src/components/tabs/Tabs.vue +290 -0
  145. package/src/components/tabs/_TabButton.vue +130 -0
  146. package/src/components/tabs/index.js +19 -0
  147. package/src/components/tooltip/README.md +70 -0
  148. package/src/components/tooltip/Tooltip.vue +115 -0
  149. package/src/components/tooltip/index.js +17 -0
  150. package/src/directives/index.js +7 -0
  151. package/src/directives/toggle/index.js +16 -0
  152. package/src/directives/toggle/toggle.js +45 -0
  153. package/src/directives/tooltip/README.md +42 -0
  154. package/src/directives/tooltip/index.js +16 -0
  155. package/src/directives/tooltip/meta.json +5 -0
  156. package/src/directives/tooltip/tooltip.js +145 -0
  157. package/src/index.js +27 -0
  158. package/src/markdown/getting-started/README.md +80 -0
  159. package/src/markdown/getting-started/meta.json +4 -0
  160. package/src/markdown/roadmap/README.md +3 -0
  161. package/src/markdown/roadmap/meta.json +4 -0
  162. package/src/mixins/checkbox-radio.mixin.js +73 -0
  163. package/src/mixins/root-listener.mixin.js +32 -0
  164. package/src/mixins/tooltip-popover.mixin.js +292 -0
  165. package/src/scripts/utils.js +18 -0
  166. package/src/utils/constants.js +191 -0
  167. package/src/utils/events.js +48 -0
  168. package/src/utils/index.js +251 -0
  169. package/src/utils/observer.js +56 -0
  170. package/src/utils/popover.class.js +91 -0
  171. package/src/utils/target.js +50 -0
  172. package/src/utils/tooltip.class.js +60 -0
  173. package/src/utils/tpmanager.class.js +730 -0
@@ -0,0 +1,730 @@
1
+ import Popper from 'popper.js'
2
+ import { CancelableEvent } from './events'
3
+ import { TP_STATE_CLASSES, TP_OFFSET_MAP, N_TP_PLACEMENTS, TOOLTIP_SELECTORS, TOOLTIP_HOVER_STATE_CLASSES, MODAL_EVENTS } from './constants'
4
+ import { getAttr, setAttr, removeAttr, guid, hasClass, addClass, removeClass, isDisabled, selectElement, isVisible, closest, getComputedStyles } from './index'
5
+
6
+ const Defaults = {
7
+ animation: true,
8
+ template: '',
9
+ trigger: 'hover focus',
10
+ title: '',
11
+ delay: 0,
12
+ html: false,
13
+ placement: 'top',
14
+ offset: 0,
15
+ arrowPadding: 6,
16
+ container: false,
17
+ fallbackPlacement: 'flip',
18
+ callbacks: {},
19
+ boundary: 'scrollParent'
20
+ }
21
+
22
+ const TransitionEndEvents = {
23
+ WebkitTransition: ['webkitTransitionEnd'],
24
+ MozTransition: ['transitionend'],
25
+ OTransition: ['otransitionend', 'oTransitionEnd'],
26
+ transition: ['transitionend']
27
+ }
28
+
29
+ const MODAL_CLASS = '.modal-content'
30
+
31
+ export default class TPManager {
32
+ constructor(targetElement, config, $root) {
33
+ this._config = null
34
+ this._isEnabled = true
35
+ this._fadeTimeout = null
36
+ this._hoverTimeout = null
37
+ this._visibleInterval = null
38
+ this._hoverState = ''
39
+ this._activeTrigger = {}
40
+ this._popperInstance = null
41
+ this._targetElement = targetElement
42
+ this._TPElement = null
43
+ this._id = guid()
44
+ this._$root = $root || null
45
+ this._routeWatcher = null
46
+
47
+ this.updateConfig(config)
48
+ }
49
+
50
+ static get Defaults() {
51
+ return Defaults
52
+ }
53
+
54
+ static getPlacement(placement) {
55
+ return N_TP_PLACEMENTS[placement.toUpperCase()]
56
+ }
57
+
58
+ /*--------------------------------------------------------------------------
59
+ /* PUBLIC
60
+ /*--------------------------------------------------------------------------*/
61
+
62
+ updateConfig(config) {
63
+ let updatedConfig = { ...this.constructor.Defaults, ...config }
64
+
65
+ if (config.delay && typeof config.delay === 'number') {
66
+ updatedConfig.delay = {
67
+ show: config.delay,
68
+ hide: config.delay
69
+ }
70
+ }
71
+
72
+ ['title', 'content'].forEach(part => {
73
+ if (config[part] && typeof config[part] === 'number') {
74
+ updatedConfig[part] = config[part].toString()
75
+ }
76
+ })
77
+
78
+ this._config = updatedConfig
79
+
80
+ this._updateTitleAttributes()
81
+ this._removeEventListeners()
82
+ this._addEventListeners()
83
+ }
84
+
85
+ show() {
86
+ if (!document.body.contains(this._targetElement) || !isVisible(this._targetElement)) {
87
+ return
88
+ }
89
+
90
+ const TPElement = this._getElement()
91
+ this._updateTitleAttributes()
92
+ this.setContent(TPElement)
93
+
94
+ // Don't show if there's no content
95
+ if (!this.hasContent(TPElement)) {
96
+ this._TPElement = null
97
+ return
98
+ }
99
+
100
+ // Set the ID on the TP element
101
+ setAttr(TPElement, 'id', this._id)
102
+
103
+ // Set the aria-describedby attribute on the target element
104
+ let desc = getAttr(this._targetElement, 'aria-describedby') || ''
105
+ desc = desc.split(/\s+/).concat(this._id).join(' ').trim()
106
+ setAttr(this._targetElement, 'aria-describedby', desc)
107
+
108
+ // Set animations
109
+ if (this._config.animation) {
110
+ addClass(TPElement, TP_STATE_CLASSES.FADE)
111
+ } else {
112
+ removeClass(TPElement, TP_STATE_CLASSES.FADE)
113
+ }
114
+
115
+ // Process placement
116
+ let placement = this._config.placement
117
+
118
+ if (typeof placement === 'function') {
119
+ placement = placement.call(this, this._TPElement, this._targetElement)
120
+ }
121
+
122
+ const attachment = this.constructor.getPlacement(placement)
123
+ this._addPlacementClass(attachment)
124
+
125
+ // Emit and process a custom event
126
+ const _showEvent = new CancelableEvent('show', {
127
+ cancelable: true,
128
+ target: this._targetElement,
129
+ relatedTarget: TPElement
130
+ })
131
+
132
+ this._emitCustomEvent(_showEvent)
133
+
134
+ if (_showEvent.defaultPrevented) {
135
+ this._TPElement = null
136
+ return
137
+ }
138
+
139
+ // Append the TP element to the container
140
+ const container = this._getContainer()
141
+ if (!document.body.contains(TPElement)) {
142
+ container.appendChild(TPElement)
143
+ }
144
+
145
+ // Reinitialize Popper
146
+ this._removePopper()
147
+ this._popperInstance = new Popper(this._targetElement, TPElement, this._getPopperConfig(placement, TPElement))
148
+
149
+ // Prep the transition complete handler
150
+ const _transitionCompleteHandler = () => {
151
+ if (this._config.animation) {
152
+ const initConfigAnimation = this._config.animation || false
153
+
154
+ if (getAttr(TPElement, 'x-placement') !== null) {
155
+ return
156
+ }
157
+
158
+ removeClass(TPElement, TP_STATE_CLASSES.FADE)
159
+ this._config.animation = false
160
+ this.hide()
161
+ this.show()
162
+ this._config.animation = initConfigAnimation
163
+ }
164
+
165
+ const prevHoverState = this._hoverState
166
+ this._hoverState = null
167
+
168
+ if (prevHoverState === TOOLTIP_HOVER_STATE_CLASSES.OUT) {
169
+ this._handleLeave(null)
170
+ }
171
+
172
+ const shownEvt = new CancelableEvent('shown', {
173
+ cancelable: false,
174
+ target: this._targetElement,
175
+ relatedTarget: TPElement
176
+ })
177
+
178
+ this._emitCustomEvent(shownEvt)
179
+ }
180
+
181
+ // Enable edge case listeners
182
+ this._handleEdgeCases(true)
183
+ addClass(TPElement, TP_STATE_CLASSES.SHOW)
184
+ this._transitionOnce(TPElement, _transitionCompleteHandler)
185
+ }
186
+
187
+ hide(callbackFn, force) {
188
+ const TPElement = this._TPElement
189
+
190
+ if (!TPElement) {
191
+ return
192
+ }
193
+
194
+ const hideEvent = new CancelableEvent('hide', {
195
+ cancelable: !force,
196
+ target: this._targetElement,
197
+ relatedTarget: TPElement
198
+ })
199
+
200
+ this._emitCustomEvent(hideEvent)
201
+
202
+ // Don't hide if the custom event is cancelled
203
+ if (hideEvent.defaultPrevented) {
204
+ return
205
+ }
206
+
207
+ // Disable edge case listeners
208
+ this._handleEdgeCases(false)
209
+
210
+ if (force) {
211
+ removeClass(TPElement, TP_STATE_CLASSES.FADE)
212
+ }
213
+
214
+ removeClass(TPElement, TP_STATE_CLASSES.SHOW)
215
+
216
+ // Update active trigger flags
217
+ this._activeTrigger.click = false
218
+ this._activeTrigger.focus = false
219
+ this._activeTrigger.hover = false
220
+
221
+ const _transitionCompleteHandler = () => {
222
+ if (this._hoverState !== TOOLTIP_HOVER_STATE_CLASSES.SHOW && TPElement.parentNode) {
223
+ TPElement.parentNode.removeChild(TPElement)
224
+
225
+ // Remove the `aria-describedby` attribute
226
+ let desc = getAttr(this._targetElement, 'aria-describedby') || ''
227
+ desc = desc.split(/\s+/).filter(d => d !== this._id).join(' ').trim()
228
+ desc ? setAttr(this._targetElement, 'aria-describedby', desc) : removeAttr(this._targetElement, 'aria-describedby')
229
+
230
+ // Remove Popper and unset TPElement
231
+ this._removePopper()
232
+ this._TPElement = null
233
+ }
234
+
235
+ // Run the callback function if any.
236
+ if (callbackFn) {
237
+ callbackFn()
238
+ }
239
+
240
+ // Prep and emit custom event
241
+ const _hiddenEvent = new CancelableEvent('hidden', {
242
+ cancelable: false,
243
+ target: this._targetElement,
244
+ relatedTarget: null
245
+ })
246
+
247
+ this._emitCustomEvent(_hiddenEvent)
248
+ }
249
+
250
+ this._transitionOnce(TPElement, _transitionCompleteHandler)
251
+ this._hoverState = ''
252
+ }
253
+
254
+ destroy() {
255
+ this._removeEventListeners()
256
+ this._handleEdgeCases(false)
257
+
258
+ clearTimeout(this._hoverTimeout)
259
+ clearTimeout(this._fadeTimeout)
260
+
261
+ if (this._popperInstance) {
262
+ this._popperInstance.destroy()
263
+ }
264
+
265
+ if (this._TPElement && this._TPElement.parentElement) {
266
+ this._TPElement.parentElement.removeChild(this._TPElement)
267
+ }
268
+
269
+ this._hoverTimeout = null
270
+ this._fadeTimeout = null
271
+ this._popperInstance = null
272
+ this._TPElement = null
273
+ this._id = null
274
+ this._$root = null
275
+ this._isEnabled = true
276
+ this._hoverState = null
277
+ this._activeTrigger = null
278
+ this._targetElement = null
279
+ }
280
+
281
+ setElementContent(container, content) {
282
+ if (!container) {
283
+ return
284
+ }
285
+
286
+ if (typeof content !== 'object' && !content.nodeType) {
287
+ container[this._config.html ? 'innerHTML' : 'innerText'] = content
288
+ return
289
+ }
290
+
291
+ if (this._config.html && content.parentElement !== container) {
292
+ container.innerHTML = ''
293
+ container.appendChild(content)
294
+ return
295
+ }
296
+
297
+ container.innerText = content.innerText
298
+ }
299
+
300
+ getTitle() {
301
+ let title = this._config.title || ''
302
+
303
+ // Fallback to attributes or empty string
304
+ if (!title) {
305
+ title = getAttr(this._targetElement, 'title')
306
+ || getAttr(this._targetElement, 'data-original-title')
307
+ || ''
308
+ }
309
+
310
+ switch (typeof title) {
311
+ case 'function':
312
+ title = title(this._targetElement)
313
+ break
314
+ case 'object':
315
+ if (title.nodeType && !title.innerHTML.trim()) {
316
+ title = ''
317
+ }
318
+ break
319
+ case 'string':
320
+ title = title.trim()
321
+ break
322
+ }
323
+
324
+ return title
325
+ }
326
+
327
+ handleEvent(e) {
328
+ if (isDisabled(this._targetElement) || !this._isEnabled) {
329
+ return
330
+ }
331
+
332
+ switch (e.type) {
333
+ case 'click':
334
+ this._handleToggle(e)
335
+ break
336
+ case 'focusout':
337
+ this._handleFocusOut(e)
338
+ break
339
+ case 'mouseleave':
340
+ this._handleLeave(e)
341
+ break
342
+ case 'focusin':
343
+ case 'mouseenter':
344
+ this._handleEnter(e)
345
+ break
346
+ }
347
+ }
348
+
349
+ /*--------------------------------------------------------------------------
350
+ /* PRIVATE
351
+ /*--------------------------------------------------------------------------*/
352
+
353
+ _addEventListeners() {
354
+ const triggers = this._config.trigger.trim().split(/\s+/)
355
+ const el = this._targetElement
356
+
357
+
358
+ triggers.forEach(trigger => {
359
+ switch (trigger) {
360
+ case 'click':
361
+ el.addEventListener('click', this)
362
+ break
363
+ case 'focus':
364
+ el.addEventListener('focusin', this)
365
+ el.addEventListener('focusout', this)
366
+ break
367
+ case 'blur':
368
+ el.addEventListener('focusout', this)
369
+ break
370
+ case 'hover':
371
+ el.addEventListener('mouseenter', this)
372
+ el.addEventListener('mouseleave', this)
373
+ }
374
+ }, this)
375
+ }
376
+
377
+ _removeEventListeners() {
378
+ ['click', 'focusin', 'focusout', 'mouseenter', 'mouseleave']
379
+ .forEach(e => this._targetElement.removeEventListener(e, this), this)
380
+ }
381
+
382
+ _handleFocusOut(e) {
383
+ // Don't trigger if the focus moves from trigger to TP element
384
+ if (
385
+ this._TPElement
386
+ && this._targetElement
387
+ && this._targetElement.contains(e.target)
388
+ && this._TPElement.contains(e.relatedTarget)
389
+ ) {
390
+ return
391
+ }
392
+
393
+ // Don't trigger if the focus moves from TP element to trigger
394
+ if (
395
+ this._TPElement
396
+ && this._targetElement
397
+ && this._TPElement.contains(e.target)
398
+ && this._targetElement.contains(e.relatedTarget)
399
+ ) {
400
+ return
401
+ }
402
+
403
+ // Don't trigger if the focus moves within the element
404
+ if (
405
+ this._TPElement
406
+ && this._TPElement.contains(e.target)
407
+ && this._TPElement.contains(e.relatedTarget)
408
+ ) {
409
+ return
410
+ }
411
+
412
+ this._handleLeave(e)
413
+ }
414
+
415
+ _getElement() {
416
+ let tpl = this._config.template;
417
+ tpl = (!tpl || typeof tpl !== 'string') ? this.constructor.Defaults.template : this._config.template
418
+
419
+ if (!this._TPElement) {
420
+ let div = document.createElement('div')
421
+ div.innerHTML = tpl.trim()
422
+ this._TPElement = div.firstElementChild ? div.removeChild(div.firstElementChild) : null
423
+ div = null
424
+ }
425
+
426
+ this._TPElement.tabIndex = -1
427
+
428
+ return this._TPElement
429
+ }
430
+
431
+ _forceHide() {
432
+ if (!this._TPElement || !hasClass(this._TPElement, TP_STATE_CLASSES.SHOW)) {
433
+ return
434
+ }
435
+
436
+ this._handleEdgeCases(false)
437
+ clearTimeout(this._hoverTimeout)
438
+ this._hoverTimeout = null
439
+ this._hoverState = ''
440
+ this.hide(null, true)
441
+ }
442
+
443
+ _handleToggle(event) {
444
+ if (!this._isEnabled) {
445
+ return
446
+ }
447
+
448
+ if (event) {
449
+ this._activeTrigger.click = !this._activeTrigger.click
450
+ this._hasActiveTrigger() ? this._handleEnter(null) : this._handleLeave(null)
451
+ return
452
+ }
453
+
454
+ hasClass(this._getElement(), TP_STATE_CLASSES.SHOW) ? this._handleLeave(null) : this._handleEnter(null)
455
+ }
456
+
457
+ _handleLeave(e) {
458
+ if (e) {
459
+ const trigger = e.type === 'focusout' ? 'focus' : 'hover'
460
+ this._activeTrigger[trigger] = false
461
+
462
+ if (e.type === 'focusout' && /blur/.test(this._config.trigger)) {
463
+ this._activeTrigger.click = false
464
+ this._activeTrigger.hover = false
465
+ }
466
+ }
467
+
468
+ if (this._hasActiveTrigger()) {
469
+ return
470
+ }
471
+
472
+ clearTimeout(this._hoverTimeout)
473
+
474
+ this._hoverState = TOOLTIP_HOVER_STATE_CLASSES.OUT
475
+
476
+ if (!this._config.delay || !this._config.delay.hide) {
477
+ this.hide()
478
+ return
479
+ }
480
+
481
+ this._hoverTimeout = setTimeout(() => {
482
+ if (this._hoverState === TOOLTIP_HOVER_STATE_CLASSES.OUT) {
483
+ this.hide()
484
+ }
485
+ }, this._config.delay.hide)
486
+ }
487
+
488
+
489
+ _hasActiveTrigger() {
490
+ for (const trigger in this._activeTrigger) {
491
+ if (this._activeTrigger[trigger]) {
492
+ return true
493
+ }
494
+ }
495
+
496
+ return false
497
+ }
498
+
499
+ _updateTitleAttributes() {
500
+ const el = this._targetElement
501
+ const titleType = typeof getAttr(el, 'data-original-title')
502
+ if (getAttr(el, 'title') || titleType !== 'string') {
503
+ setAttr(el, 'data-original-title', getAttr(el, 'title') || '')
504
+ setAttr(el, 'title', '')
505
+ }
506
+ }
507
+
508
+ _handleEnter(e) {
509
+ if (e) {
510
+ const trigger = e.type === 'focusin' ? focus : 'hover'
511
+ this._activeTrigger[trigger] = true
512
+ }
513
+
514
+ if (hasClass(this._getElement(), TP_STATE_CLASSES.SHOW) || this._hoverState === TP_STATE_CLASSES.SHOW) {
515
+ this._hoverState = TP_STATE_CLASSES.SHOW
516
+ return
517
+ }
518
+
519
+ clearTimeout(this._hoverTimeout)
520
+ this._hoverState = TP_STATE_CLASSES.SHOW
521
+
522
+ if (!this._config.delay || !this._config.delay.show) {
523
+ this.show()
524
+ return
525
+ }
526
+
527
+ this._hoverTimeout = setTimeout(() => {
528
+ if (this._hoverState === TP_STATE_CLASSES.SHOW) {
529
+ this.show()
530
+ }
531
+ }, this._config.delay.show)
532
+ }
533
+
534
+ _handleEdgeCases(on) {
535
+ if (this._TPElement === null) {
536
+ return
537
+ }
538
+
539
+ this._setModalListener(on)
540
+ this._visibleCheck(on)
541
+ this._setRouteWatcher(on)
542
+ this._setOnTouchStartListener(on)
543
+
544
+ if (on && /(focus|blur)/.test(this._config.trigger)) {
545
+ this._TPElement.addEventListener('focusout', this)
546
+ } else {
547
+ this._TPElement.removeEventListener('focusout', this)
548
+ }
549
+ }
550
+
551
+ _setModalListener (on) {
552
+ const modal = closest(MODAL_CLASS, this._targetElement)
553
+
554
+ if (!modal) {
555
+ return
556
+ }
557
+
558
+ if (this._$root) {
559
+ this._$root[on ? '$on' : '$off'](MODAL_EVENTS.HIDDEN, this._forceHide.bind(this))
560
+ }
561
+ }
562
+
563
+ _visibleCheck(on) {
564
+ clearInterval(this._visibleInterval)
565
+ this._visibleInterval = null
566
+
567
+ if (!on) {
568
+ return
569
+ }
570
+
571
+ this._visibleInterval = setInterval(() => {
572
+ const tip = this._getElement()
573
+ if (tip && !isVisible(this._targetElement) && hasClass(tip, TP_STATE_CLASSES.SHOW)) {
574
+ this._forceHide()
575
+ }
576
+ }, 100)
577
+ }
578
+
579
+ _setRouteWatcher(on) {
580
+ if (on) {
581
+ this._setRouteWatcher(false)
582
+ if (this._$root && Boolean(this._$root.route)) {
583
+ this._routeWatcher = this._$root.$watch('$route', (newVal, oldVal) => {
584
+ if (newVal === oldVal) {
585
+ return
586
+ }
587
+
588
+ this._forceHide()
589
+ })
590
+ }
591
+ } else {
592
+ if (this._routeWatcher) {
593
+ this._routeWatcher()
594
+ this._routeWatcher = null
595
+ }
596
+ }
597
+ }
598
+
599
+ _setOnTouchStartListener(on) {
600
+ if (!('ontouchstart' in document.documentElement)) {
601
+ return
602
+ }
603
+
604
+ Array.from(document.body.children).forEach(el => {
605
+ if (on) {
606
+ el.addEventListener('mouseover', () => {})
607
+ } else {
608
+ el.removeEventListener('mouseover', () => {})
609
+ }
610
+ })
611
+ }
612
+
613
+ _getPopperConfig(placement, tip) {
614
+ return {
615
+ placement: this.constructor.getPlacement(placement),
616
+ modifiers: {
617
+ offset: { offset: this._getOffset(placement, tip) },
618
+ flip: { behavior: this._config.fallbackPlacement },
619
+ arrow: { element: '.arrow' },
620
+ preventOverflow: { boundariesElement: this._config.boundary }
621
+ },
622
+ onCreate: data => {
623
+ if (data.originalPlacement !== data.placement) {
624
+ this._handlePopperPlacementChange(data)
625
+ }
626
+ },
627
+ onUpdate: data => {
628
+ this._handlePopperPlacementChange(data)
629
+ }
630
+ }
631
+ }
632
+
633
+ _getOffset(placement, tip) {
634
+ if (this._config.offset) {
635
+ return this._config.offset
636
+ }
637
+
638
+ const arrow = selectElement(TOOLTIP_SELECTORS.ARROW, tip)
639
+ const arrowOffset = parseFloat(getComputedStyles(arrow).width) + parseFloat(this._config.arrowPadding)
640
+ switch (TP_OFFSET_MAP[placement.toUpperCase()]) {
641
+ case +1:
642
+ return `+50%p - ${arrowOffset}px`
643
+ case -1:
644
+ return `-50%p + ${arrowOffset}px`
645
+ default:
646
+ return 0
647
+ }
648
+ }
649
+
650
+ _handlePopperPlacementChange(data) {
651
+ const TPElement = this._getElement()
652
+ const tabClass = TPElement.className.match(new RegExp(`\\b${this.constructor.ClassPrefix}\\S+`, 'g'))
653
+
654
+ if (tabClass === null && !tabClass.length) {
655
+ return
656
+ }
657
+
658
+ tabClass.forEach(className => removeClass(TPElement, className))
659
+ this._addPlacementClass(this.constructor.getPlacement(data.placement))
660
+ }
661
+
662
+ _removePopper() {
663
+ if (this._popperInstance) {
664
+ this._popperInstance.destroy()
665
+ }
666
+
667
+ this._popperInstance = null
668
+ }
669
+
670
+ _getContainer() {
671
+ const container = this._config.container
672
+ const body = document.body
673
+ return container === false ? (closest(MODAL_CLASS, this._targetElement) || body) : (selectElement(container, body) || body)
674
+ }
675
+
676
+ _emitCustomEvent(event) {
677
+ const eventName = event.type
678
+
679
+ if (this._$root && this._$root.$emit) {
680
+ this._$root.$emit(`dr:${this.constructor.Name}:${eventName}`, event)
681
+ }
682
+
683
+ const callbacks = this._config.callbacks || {}
684
+
685
+ if (typeof callbacks[eventName] === 'function') {
686
+ callbacks[eventName]()
687
+ }
688
+ }
689
+
690
+ _transitionOnce(TPElement, completeHandlerFn) {
691
+ const transEvents = this._getTransitionEndEvents()
692
+ let called = false
693
+ clearTimeout(this._fadeTimeout)
694
+ this._fadeTimeout = null
695
+
696
+ const fnOnce = () => {
697
+ if (called) {
698
+ return
699
+ }
700
+
701
+ called = true
702
+ clearTimeout(this._fadeTimeout)
703
+ this._fadeTimeout = null
704
+ transEvents.forEach(eventName => TPElement.removeEventListener(eventName, fnOnce))
705
+ completeHandlerFn()
706
+ }
707
+
708
+ if (hasClass(TPElement, TP_STATE_CLASSES.FADE)) {
709
+ transEvents.forEach(eventName => TPElement.addEventListener(eventName, fnOnce))
710
+ this._fadeTimeout = setTimeout(fnOnce, 150)
711
+ } else {
712
+ fnOnce()
713
+ }
714
+ }
715
+
716
+ _getTransitionEndEvents() {
717
+ for (const name in TransitionEndEvents) {
718
+ if (this._targetElement.style[name] !== undefined) {
719
+ return TransitionEndEvents[name]
720
+ }
721
+ }
722
+
723
+ return []
724
+ }
725
+
726
+ _addPlacementClass(placement) {
727
+ const Popover = this._getElement()
728
+ addClass(Popover, `${this.constructor.ClassPrefix}-${placement}`)
729
+ }
730
+ }