@gitlab/ui 88.6.0 → 89.0.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.
- package/CHANGELOG.md +12 -0
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.js +0 -2
- package/dist/vendor/bootstrap-vue/src/components/index.js +0 -2
- package/dist/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-tooltip.js +1 -1
- package/dist/vendor/bootstrap-vue/src/constants/components.js +1 -3
- package/dist/vendor/bootstrap-vue/src/index.js +0 -3
- package/package.json +1 -1
- package/src/index.js +0 -2
- package/src/scss/bootstrap.scss +0 -1
- package/src/scss/components.scss +0 -1
- package/src/vendor/bootstrap-vue/nuxt/index.js +0 -1
- package/src/vendor/bootstrap-vue/package.json +0 -1
- package/src/vendor/bootstrap-vue/src/components/index.d.ts +0 -1
- package/src/vendor/bootstrap-vue/src/components/index.js +0 -2
- package/src/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-tooltip.js +1 -1
- package/src/vendor/bootstrap-vue/src/constants/components.js +0 -2
- package/src/vendor/bootstrap-vue/src/index.js +0 -5
- package/dist/components/base/carousel/carousel.js +0 -48
- package/dist/components/base/carousel/carousel_slide.js +0 -47
- package/dist/vendor/bootstrap-vue/src/components/carousel/carousel-slide.js +0 -123
- package/dist/vendor/bootstrap-vue/src/components/carousel/carousel.js +0 -617
- package/dist/vendor/bootstrap-vue/src/components/carousel/index.js +0 -14
- package/src/components/base/carousel/carousel.md +0 -3
- package/src/components/base/carousel/carousel.scss +0 -29
- package/src/components/base/carousel/carousel.vue +0 -19
- package/src/components/base/carousel/carousel_slide.vue +0 -18
- package/src/vendor/bootstrap-vue/src/components/carousel/README.md +0 -320
- package/src/vendor/bootstrap-vue/src/components/carousel/carousel-slide.js +0 -132
- package/src/vendor/bootstrap-vue/src/components/carousel/carousel-slide.spec.js +0 -276
- package/src/vendor/bootstrap-vue/src/components/carousel/carousel.js +0 -655
- package/src/vendor/bootstrap-vue/src/components/carousel/carousel.spec.js +0 -1069
- package/src/vendor/bootstrap-vue/src/components/carousel/index.d.ts +0 -20
- package/src/vendor/bootstrap-vue/src/components/carousel/index.js +0 -12
- package/src/vendor/bootstrap-vue/src/components/carousel/package.json +0 -185
|
@@ -1,655 +0,0 @@
|
|
|
1
|
-
import { extend } from '../../vue'
|
|
2
|
-
import { NAME_CAROUSEL } from '../../constants/components'
|
|
3
|
-
import { IS_BROWSER, HAS_POINTER_EVENT_SUPPORT, HAS_TOUCH_SUPPORT } from '../../constants/env'
|
|
4
|
-
import {
|
|
5
|
-
EVENT_NAME_PAUSED,
|
|
6
|
-
EVENT_NAME_SLIDING_END,
|
|
7
|
-
EVENT_NAME_SLIDING_START,
|
|
8
|
-
EVENT_NAME_UNPAUSED,
|
|
9
|
-
EVENT_OPTIONS_NO_CAPTURE
|
|
10
|
-
} from '../../constants/events'
|
|
11
|
-
import { CODE_ENTER, CODE_LEFT, CODE_RIGHT, CODE_SPACE } from '../../constants/key-codes'
|
|
12
|
-
import {
|
|
13
|
-
PROP_TYPE_BOOLEAN,
|
|
14
|
-
PROP_TYPE_NUMBER,
|
|
15
|
-
PROP_TYPE_NUMBER_STRING,
|
|
16
|
-
PROP_TYPE_STRING
|
|
17
|
-
} from '../../constants/props'
|
|
18
|
-
import {
|
|
19
|
-
addClass,
|
|
20
|
-
getActiveElement,
|
|
21
|
-
reflow,
|
|
22
|
-
removeClass,
|
|
23
|
-
requestAF,
|
|
24
|
-
selectAll,
|
|
25
|
-
setAttr
|
|
26
|
-
} from '../../utils/dom'
|
|
27
|
-
import { eventOn, eventOff, stopEvent } from '../../utils/events'
|
|
28
|
-
import { isUndefined } from '../../utils/inspect'
|
|
29
|
-
import { mathAbs, mathFloor, mathMax, mathMin } from '../../utils/math'
|
|
30
|
-
import { makeModelMixin } from '../../utils/model'
|
|
31
|
-
import { toInteger } from '../../utils/number'
|
|
32
|
-
import { noop } from '../../utils/noop'
|
|
33
|
-
import { sortKeys } from '../../utils/object'
|
|
34
|
-
import { observeDom } from '../../utils/observe-dom'
|
|
35
|
-
import { makeProp, makePropsConfigurable } from '../../utils/props'
|
|
36
|
-
import { idMixin, props as idProps } from '../../mixins/id'
|
|
37
|
-
import { normalizeSlotMixin } from '../../mixins/normalize-slot'
|
|
38
|
-
|
|
39
|
-
// --- Constants ---
|
|
40
|
-
|
|
41
|
-
const {
|
|
42
|
-
mixin: modelMixin,
|
|
43
|
-
props: modelProps,
|
|
44
|
-
prop: MODEL_PROP_NAME,
|
|
45
|
-
event: MODEL_EVENT_NAME
|
|
46
|
-
} = makeModelMixin('value', {
|
|
47
|
-
type: PROP_TYPE_NUMBER,
|
|
48
|
-
defaultValue: 0
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
// Slide directional classes
|
|
52
|
-
const DIRECTION = {
|
|
53
|
-
next: {
|
|
54
|
-
dirClass: 'carousel-item-left',
|
|
55
|
-
overlayClass: 'carousel-item-next'
|
|
56
|
-
},
|
|
57
|
-
prev: {
|
|
58
|
-
dirClass: 'carousel-item-right',
|
|
59
|
-
overlayClass: 'carousel-item-prev'
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Fallback Transition duration (with a little buffer) in ms
|
|
64
|
-
const TRANS_DURATION = 600 + 50
|
|
65
|
-
|
|
66
|
-
// Time for mouse compat events to fire after touch
|
|
67
|
-
const TOUCH_EVENT_COMPAT_WAIT = 500
|
|
68
|
-
|
|
69
|
-
// Number of pixels to consider touch move a swipe
|
|
70
|
-
const SWIPE_THRESHOLD = 40
|
|
71
|
-
|
|
72
|
-
// PointerEvent pointer types
|
|
73
|
-
const PointerType = {
|
|
74
|
-
TOUCH: 'touch',
|
|
75
|
-
PEN: 'pen'
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Transition Event names
|
|
79
|
-
const TransitionEndEvents = {
|
|
80
|
-
WebkitTransition: 'webkitTransitionEnd',
|
|
81
|
-
MozTransition: 'transitionend',
|
|
82
|
-
OTransition: 'otransitionend oTransitionEnd',
|
|
83
|
-
transition: 'transitionend'
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// --- Helper methods ---
|
|
87
|
-
|
|
88
|
-
// Return the browser specific transitionEnd event name
|
|
89
|
-
const getTransitionEndEvent = el => {
|
|
90
|
-
for (const name in TransitionEndEvents) {
|
|
91
|
-
if (!isUndefined(el.style[name])) {
|
|
92
|
-
return TransitionEndEvents[name]
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// Fallback
|
|
96
|
-
/* istanbul ignore next */
|
|
97
|
-
return null
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// --- Props ---
|
|
101
|
-
|
|
102
|
-
export const props = makePropsConfigurable(
|
|
103
|
-
sortKeys({
|
|
104
|
-
...idProps,
|
|
105
|
-
...modelProps,
|
|
106
|
-
background: makeProp(PROP_TYPE_STRING),
|
|
107
|
-
controls: makeProp(PROP_TYPE_BOOLEAN, false),
|
|
108
|
-
// Enable cross-fade animation instead of slide animation
|
|
109
|
-
fade: makeProp(PROP_TYPE_BOOLEAN, false),
|
|
110
|
-
// Sniffed by carousel-slide
|
|
111
|
-
imgHeight: makeProp(PROP_TYPE_NUMBER_STRING),
|
|
112
|
-
// Sniffed by carousel-slide
|
|
113
|
-
imgWidth: makeProp(PROP_TYPE_NUMBER_STRING),
|
|
114
|
-
indicators: makeProp(PROP_TYPE_BOOLEAN, false),
|
|
115
|
-
interval: makeProp(PROP_TYPE_NUMBER, 5000),
|
|
116
|
-
labelGotoSlide: makeProp(PROP_TYPE_STRING, 'Goto slide'),
|
|
117
|
-
labelIndicators: makeProp(PROP_TYPE_STRING, 'Select a slide to display'),
|
|
118
|
-
labelNext: makeProp(PROP_TYPE_STRING, 'Next slide'),
|
|
119
|
-
labelPrev: makeProp(PROP_TYPE_STRING, 'Previous slide'),
|
|
120
|
-
// Disable slide/fade animation
|
|
121
|
-
noAnimation: makeProp(PROP_TYPE_BOOLEAN, false),
|
|
122
|
-
// Disable pause on hover
|
|
123
|
-
noHoverPause: makeProp(PROP_TYPE_BOOLEAN, false),
|
|
124
|
-
// Sniffed by carousel-slide
|
|
125
|
-
noTouch: makeProp(PROP_TYPE_BOOLEAN, false),
|
|
126
|
-
// Disable wrapping/looping when start/end is reached
|
|
127
|
-
noWrap: makeProp(PROP_TYPE_BOOLEAN, false)
|
|
128
|
-
}),
|
|
129
|
-
NAME_CAROUSEL
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
// --- Main component ---
|
|
133
|
-
|
|
134
|
-
// @vue/component
|
|
135
|
-
export const BCarousel = /*#__PURE__*/ extend({
|
|
136
|
-
name: NAME_CAROUSEL,
|
|
137
|
-
mixins: [idMixin, modelMixin, normalizeSlotMixin],
|
|
138
|
-
provide() {
|
|
139
|
-
return { getBvCarousel: () => this }
|
|
140
|
-
},
|
|
141
|
-
props,
|
|
142
|
-
data() {
|
|
143
|
-
return {
|
|
144
|
-
index: this[MODEL_PROP_NAME] || 0,
|
|
145
|
-
isSliding: false,
|
|
146
|
-
transitionEndEvent: null,
|
|
147
|
-
slides: [],
|
|
148
|
-
direction: null,
|
|
149
|
-
isPaused: !(toInteger(this.interval, 0) > 0),
|
|
150
|
-
// Touch event handling values
|
|
151
|
-
touchStartX: 0,
|
|
152
|
-
touchDeltaX: 0
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
computed: {
|
|
156
|
-
numSlides() {
|
|
157
|
-
return this.slides.length
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
watch: {
|
|
161
|
-
[MODEL_PROP_NAME](newValue, oldValue) {
|
|
162
|
-
if (newValue !== oldValue) {
|
|
163
|
-
this.setSlide(toInteger(newValue, 0))
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
interval(newValue, oldValue) {
|
|
167
|
-
/* istanbul ignore next */
|
|
168
|
-
if (newValue === oldValue) {
|
|
169
|
-
return
|
|
170
|
-
}
|
|
171
|
-
if (!newValue) {
|
|
172
|
-
// Pausing slide show
|
|
173
|
-
this.pause(false)
|
|
174
|
-
} else {
|
|
175
|
-
// Restarting or Changing interval
|
|
176
|
-
this.pause(true)
|
|
177
|
-
this.start(false)
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
isPaused(newValue, oldValue) {
|
|
181
|
-
if (newValue !== oldValue) {
|
|
182
|
-
this.$emit(newValue ? EVENT_NAME_PAUSED : EVENT_NAME_UNPAUSED)
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
index(to, from) {
|
|
186
|
-
/* istanbul ignore next */
|
|
187
|
-
if (to === from || this.isSliding) {
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
this.doSlide(to, from)
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
created() {
|
|
194
|
-
// Create private non-reactive props
|
|
195
|
-
this.$_interval = null
|
|
196
|
-
this.$_animationTimeout = null
|
|
197
|
-
this.$_touchTimeout = null
|
|
198
|
-
this.$_observer = null
|
|
199
|
-
// Set initial paused state
|
|
200
|
-
this.isPaused = !(toInteger(this.interval, 0) > 0)
|
|
201
|
-
},
|
|
202
|
-
mounted() {
|
|
203
|
-
// Cache current browser transitionend event name
|
|
204
|
-
this.transitionEndEvent = getTransitionEndEvent(this.$el) || null
|
|
205
|
-
// Get all slides
|
|
206
|
-
this.updateSlides()
|
|
207
|
-
// Observe child changes so we can update slide list
|
|
208
|
-
this.setObserver(true)
|
|
209
|
-
},
|
|
210
|
-
beforeDestroy() {
|
|
211
|
-
this.clearInterval()
|
|
212
|
-
this.clearAnimationTimeout()
|
|
213
|
-
this.clearTouchTimeout()
|
|
214
|
-
this.setObserver(false)
|
|
215
|
-
},
|
|
216
|
-
methods: {
|
|
217
|
-
clearInterval() {
|
|
218
|
-
clearInterval(this.$_interval)
|
|
219
|
-
this.$_interval = null
|
|
220
|
-
},
|
|
221
|
-
clearAnimationTimeout() {
|
|
222
|
-
clearTimeout(this.$_animationTimeout)
|
|
223
|
-
this.$_animationTimeout = null
|
|
224
|
-
},
|
|
225
|
-
clearTouchTimeout() {
|
|
226
|
-
clearTimeout(this.$_touchTimeout)
|
|
227
|
-
this.$_touchTimeout = null
|
|
228
|
-
},
|
|
229
|
-
setObserver(on = false) {
|
|
230
|
-
this.$_observer && this.$_observer.disconnect()
|
|
231
|
-
this.$_observer = null
|
|
232
|
-
if (on) {
|
|
233
|
-
this.$_observer = observeDom(this.$refs.inner, this.updateSlides.bind(this), {
|
|
234
|
-
subtree: false,
|
|
235
|
-
childList: true,
|
|
236
|
-
attributes: true,
|
|
237
|
-
attributeFilter: ['id']
|
|
238
|
-
})
|
|
239
|
-
}
|
|
240
|
-
},
|
|
241
|
-
// Set slide
|
|
242
|
-
setSlide(slide, direction = null) {
|
|
243
|
-
// Don't animate when page is not visible
|
|
244
|
-
/* istanbul ignore if: difficult to test */
|
|
245
|
-
if (IS_BROWSER && document.visibilityState && document.hidden) {
|
|
246
|
-
return
|
|
247
|
-
}
|
|
248
|
-
const noWrap = this.noWrap
|
|
249
|
-
const numSlides = this.numSlides
|
|
250
|
-
// Make sure we have an integer (you never know!)
|
|
251
|
-
slide = mathFloor(slide)
|
|
252
|
-
// Don't do anything if nothing to slide to
|
|
253
|
-
if (numSlides === 0) {
|
|
254
|
-
return
|
|
255
|
-
}
|
|
256
|
-
// Don't change slide while transitioning, wait until transition is done
|
|
257
|
-
if (this.isSliding) {
|
|
258
|
-
// Schedule slide after sliding complete
|
|
259
|
-
this.$once(EVENT_NAME_SLIDING_END, () => {
|
|
260
|
-
// Wrap in `requestAF()` to allow the slide to properly finish to avoid glitching
|
|
261
|
-
requestAF(() => this.setSlide(slide, direction))
|
|
262
|
-
})
|
|
263
|
-
return
|
|
264
|
-
}
|
|
265
|
-
this.direction = direction
|
|
266
|
-
// Set new slide index
|
|
267
|
-
// Wrap around if necessary (if no-wrap not enabled)
|
|
268
|
-
this.index =
|
|
269
|
-
slide >= numSlides
|
|
270
|
-
? noWrap
|
|
271
|
-
? numSlides - 1
|
|
272
|
-
: 0
|
|
273
|
-
: slide < 0
|
|
274
|
-
? noWrap
|
|
275
|
-
? 0
|
|
276
|
-
: numSlides - 1
|
|
277
|
-
: slide
|
|
278
|
-
// Ensure the v-model is synched up if no-wrap is enabled
|
|
279
|
-
// and user tried to slide pass either ends
|
|
280
|
-
if (noWrap && this.index !== slide && this.index !== this[MODEL_PROP_NAME]) {
|
|
281
|
-
this.$emit(MODEL_EVENT_NAME, this.index)
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
// Previous slide
|
|
285
|
-
prev() {
|
|
286
|
-
this.setSlide(this.index - 1, 'prev')
|
|
287
|
-
},
|
|
288
|
-
// Next slide
|
|
289
|
-
next() {
|
|
290
|
-
this.setSlide(this.index + 1, 'next')
|
|
291
|
-
},
|
|
292
|
-
// Pause auto rotation
|
|
293
|
-
pause(event) {
|
|
294
|
-
if (!event) {
|
|
295
|
-
this.isPaused = true
|
|
296
|
-
}
|
|
297
|
-
this.clearInterval()
|
|
298
|
-
},
|
|
299
|
-
// Start auto rotate slides
|
|
300
|
-
start(event) {
|
|
301
|
-
if (!event) {
|
|
302
|
-
this.isPaused = false
|
|
303
|
-
}
|
|
304
|
-
/* istanbul ignore next: most likely will never happen, but just in case */
|
|
305
|
-
this.clearInterval()
|
|
306
|
-
// Don't start if no interval, or less than 2 slides
|
|
307
|
-
if (this.interval && this.numSlides > 1) {
|
|
308
|
-
this.$_interval = setInterval(this.next, mathMax(1000, this.interval))
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
// Restart auto rotate slides when focus/hover leaves the carousel
|
|
312
|
-
/* istanbul ignore next */
|
|
313
|
-
restart() {
|
|
314
|
-
if (!this.$el.contains(getActiveElement())) {
|
|
315
|
-
this.start()
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
doSlide(to, from) {
|
|
319
|
-
const isCycling = Boolean(this.interval)
|
|
320
|
-
// Determine sliding direction
|
|
321
|
-
const direction = this.calcDirection(this.direction, from, to)
|
|
322
|
-
const overlayClass = direction.overlayClass
|
|
323
|
-
const dirClass = direction.dirClass
|
|
324
|
-
// Determine current and next slides
|
|
325
|
-
const currentSlide = this.slides[from]
|
|
326
|
-
const nextSlide = this.slides[to]
|
|
327
|
-
// Don't do anything if there aren't any slides to slide to
|
|
328
|
-
if (!currentSlide || !nextSlide) {
|
|
329
|
-
/* istanbul ignore next */
|
|
330
|
-
return
|
|
331
|
-
}
|
|
332
|
-
// Start animating
|
|
333
|
-
this.isSliding = true
|
|
334
|
-
if (isCycling) {
|
|
335
|
-
this.pause(false)
|
|
336
|
-
}
|
|
337
|
-
this.$emit(EVENT_NAME_SLIDING_START, to)
|
|
338
|
-
// Update v-model
|
|
339
|
-
this.$emit(MODEL_EVENT_NAME, this.index)
|
|
340
|
-
if (this.noAnimation) {
|
|
341
|
-
addClass(nextSlide, 'active')
|
|
342
|
-
removeClass(currentSlide, 'active')
|
|
343
|
-
this.isSliding = false
|
|
344
|
-
// Notify ourselves that we're done sliding (slid)
|
|
345
|
-
this.$nextTick(() => this.$emit(EVENT_NAME_SLIDING_END, to))
|
|
346
|
-
} else {
|
|
347
|
-
addClass(nextSlide, overlayClass)
|
|
348
|
-
// Trigger a reflow of next slide
|
|
349
|
-
reflow(nextSlide)
|
|
350
|
-
addClass(currentSlide, dirClass)
|
|
351
|
-
addClass(nextSlide, dirClass)
|
|
352
|
-
// Transition End handler
|
|
353
|
-
let called = false
|
|
354
|
-
/* istanbul ignore next: difficult to test */
|
|
355
|
-
const onceTransEnd = () => {
|
|
356
|
-
if (called) {
|
|
357
|
-
return
|
|
358
|
-
}
|
|
359
|
-
called = true
|
|
360
|
-
/* istanbul ignore if: transition events cant be tested in JSDOM */
|
|
361
|
-
if (this.transitionEndEvent) {
|
|
362
|
-
const events = this.transitionEndEvent.split(/\s+/)
|
|
363
|
-
events.forEach(event =>
|
|
364
|
-
eventOff(nextSlide, event, onceTransEnd, EVENT_OPTIONS_NO_CAPTURE)
|
|
365
|
-
)
|
|
366
|
-
}
|
|
367
|
-
this.clearAnimationTimeout()
|
|
368
|
-
removeClass(nextSlide, dirClass)
|
|
369
|
-
removeClass(nextSlide, overlayClass)
|
|
370
|
-
addClass(nextSlide, 'active')
|
|
371
|
-
removeClass(currentSlide, 'active')
|
|
372
|
-
removeClass(currentSlide, dirClass)
|
|
373
|
-
removeClass(currentSlide, overlayClass)
|
|
374
|
-
setAttr(currentSlide, 'aria-current', 'false')
|
|
375
|
-
setAttr(nextSlide, 'aria-current', 'true')
|
|
376
|
-
setAttr(currentSlide, 'aria-hidden', 'true')
|
|
377
|
-
setAttr(nextSlide, 'aria-hidden', 'false')
|
|
378
|
-
this.isSliding = false
|
|
379
|
-
this.direction = null
|
|
380
|
-
// Notify ourselves that we're done sliding (slid)
|
|
381
|
-
this.$nextTick(() => this.$emit(EVENT_NAME_SLIDING_END, to))
|
|
382
|
-
}
|
|
383
|
-
// Set up transitionend handler
|
|
384
|
-
/* istanbul ignore if: transition events cant be tested in JSDOM */
|
|
385
|
-
if (this.transitionEndEvent) {
|
|
386
|
-
const events = this.transitionEndEvent.split(/\s+/)
|
|
387
|
-
events.forEach(event => eventOn(nextSlide, event, onceTransEnd, EVENT_OPTIONS_NO_CAPTURE))
|
|
388
|
-
}
|
|
389
|
-
// Fallback to setTimeout()
|
|
390
|
-
this.$_animationTimeout = setTimeout(onceTransEnd, TRANS_DURATION)
|
|
391
|
-
}
|
|
392
|
-
if (isCycling) {
|
|
393
|
-
this.start(false)
|
|
394
|
-
}
|
|
395
|
-
},
|
|
396
|
-
// Update slide list
|
|
397
|
-
updateSlides() {
|
|
398
|
-
this.pause(true)
|
|
399
|
-
// Get all slides as DOM elements
|
|
400
|
-
this.slides = selectAll('.carousel-item', this.$refs.inner)
|
|
401
|
-
const numSlides = this.slides.length
|
|
402
|
-
// Keep slide number in range
|
|
403
|
-
const index = mathMax(0, mathMin(mathFloor(this.index), numSlides - 1))
|
|
404
|
-
this.slides.forEach((slide, idx) => {
|
|
405
|
-
const n = idx + 1
|
|
406
|
-
if (idx === index) {
|
|
407
|
-
addClass(slide, 'active')
|
|
408
|
-
setAttr(slide, 'aria-current', 'true')
|
|
409
|
-
} else {
|
|
410
|
-
removeClass(slide, 'active')
|
|
411
|
-
setAttr(slide, 'aria-current', 'false')
|
|
412
|
-
}
|
|
413
|
-
setAttr(slide, 'aria-posinset', String(n))
|
|
414
|
-
setAttr(slide, 'aria-setsize', String(numSlides))
|
|
415
|
-
})
|
|
416
|
-
// Set slide as active
|
|
417
|
-
this.setSlide(index)
|
|
418
|
-
this.start(this.isPaused)
|
|
419
|
-
},
|
|
420
|
-
calcDirection(direction = null, curIndex = 0, nextIndex = 0) {
|
|
421
|
-
if (!direction) {
|
|
422
|
-
return nextIndex > curIndex ? DIRECTION.next : DIRECTION.prev
|
|
423
|
-
}
|
|
424
|
-
return DIRECTION[direction]
|
|
425
|
-
},
|
|
426
|
-
handleClick(event, fn) {
|
|
427
|
-
const keyCode = event.keyCode
|
|
428
|
-
if (event.type === 'click' || keyCode === CODE_SPACE || keyCode === CODE_ENTER) {
|
|
429
|
-
stopEvent(event)
|
|
430
|
-
fn()
|
|
431
|
-
}
|
|
432
|
-
},
|
|
433
|
-
/* istanbul ignore next: JSDOM doesn't support touch events */
|
|
434
|
-
handleSwipe() {
|
|
435
|
-
const absDeltaX = mathAbs(this.touchDeltaX)
|
|
436
|
-
if (absDeltaX <= SWIPE_THRESHOLD) {
|
|
437
|
-
return
|
|
438
|
-
}
|
|
439
|
-
const direction = absDeltaX / this.touchDeltaX
|
|
440
|
-
// Reset touch delta X
|
|
441
|
-
// https://github.com/twbs/bootstrap/pull/28558
|
|
442
|
-
this.touchDeltaX = 0
|
|
443
|
-
if (direction > 0) {
|
|
444
|
-
// Swipe left
|
|
445
|
-
this.prev()
|
|
446
|
-
} else if (direction < 0) {
|
|
447
|
-
// Swipe right
|
|
448
|
-
this.next()
|
|
449
|
-
}
|
|
450
|
-
},
|
|
451
|
-
/* istanbul ignore next: JSDOM doesn't support touch events */
|
|
452
|
-
touchStart(event) {
|
|
453
|
-
if (HAS_POINTER_EVENT_SUPPORT && PointerType[event.pointerType.toUpperCase()]) {
|
|
454
|
-
this.touchStartX = event.clientX
|
|
455
|
-
} else if (!HAS_POINTER_EVENT_SUPPORT) {
|
|
456
|
-
this.touchStartX = event.touches[0].clientX
|
|
457
|
-
}
|
|
458
|
-
},
|
|
459
|
-
/* istanbul ignore next: JSDOM doesn't support touch events */
|
|
460
|
-
touchMove(event) {
|
|
461
|
-
// Ensure swiping with one touch and not pinching
|
|
462
|
-
if (event.touches && event.touches.length > 1) {
|
|
463
|
-
this.touchDeltaX = 0
|
|
464
|
-
} else {
|
|
465
|
-
this.touchDeltaX = event.touches[0].clientX - this.touchStartX
|
|
466
|
-
}
|
|
467
|
-
},
|
|
468
|
-
/* istanbul ignore next: JSDOM doesn't support touch events */
|
|
469
|
-
touchEnd(event) {
|
|
470
|
-
if (HAS_POINTER_EVENT_SUPPORT && PointerType[event.pointerType.toUpperCase()]) {
|
|
471
|
-
this.touchDeltaX = event.clientX - this.touchStartX
|
|
472
|
-
}
|
|
473
|
-
this.handleSwipe()
|
|
474
|
-
// If it's a touch-enabled device, mouseenter/leave are fired as
|
|
475
|
-
// part of the mouse compatibility events on first tap - the carousel
|
|
476
|
-
// would stop cycling until user tapped out of it;
|
|
477
|
-
// here, we listen for touchend, explicitly pause the carousel
|
|
478
|
-
// (as if it's the second time we tap on it, mouseenter compat event
|
|
479
|
-
// is NOT fired) and after a timeout (to allow for mouse compatibility
|
|
480
|
-
// events to fire) we explicitly restart cycling
|
|
481
|
-
this.pause(false)
|
|
482
|
-
this.clearTouchTimeout()
|
|
483
|
-
this.$_touchTimeout = setTimeout(
|
|
484
|
-
this.start,
|
|
485
|
-
TOUCH_EVENT_COMPAT_WAIT + mathMax(1000, this.interval)
|
|
486
|
-
)
|
|
487
|
-
}
|
|
488
|
-
},
|
|
489
|
-
render(h) {
|
|
490
|
-
const {
|
|
491
|
-
indicators,
|
|
492
|
-
background,
|
|
493
|
-
noAnimation,
|
|
494
|
-
noHoverPause,
|
|
495
|
-
noTouch,
|
|
496
|
-
index,
|
|
497
|
-
isSliding,
|
|
498
|
-
pause,
|
|
499
|
-
restart,
|
|
500
|
-
touchStart,
|
|
501
|
-
touchEnd
|
|
502
|
-
} = this
|
|
503
|
-
const idInner = this.safeId('__BV_inner_')
|
|
504
|
-
|
|
505
|
-
// Wrapper for slides
|
|
506
|
-
const $inner = h(
|
|
507
|
-
'div',
|
|
508
|
-
{
|
|
509
|
-
staticClass: 'carousel-inner',
|
|
510
|
-
attrs: {
|
|
511
|
-
id: idInner,
|
|
512
|
-
role: 'list'
|
|
513
|
-
},
|
|
514
|
-
ref: 'inner'
|
|
515
|
-
},
|
|
516
|
-
[this.normalizeSlot()]
|
|
517
|
-
)
|
|
518
|
-
|
|
519
|
-
// Prev and next controls
|
|
520
|
-
let $controls = h()
|
|
521
|
-
if (this.controls) {
|
|
522
|
-
const makeControl = (direction, label, handler) => {
|
|
523
|
-
const handlerWrapper = event => {
|
|
524
|
-
/* istanbul ignore next */
|
|
525
|
-
if (!isSliding) {
|
|
526
|
-
this.handleClick(event, handler)
|
|
527
|
-
} else {
|
|
528
|
-
stopEvent(event, { propagation: false })
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
return h(
|
|
533
|
-
'a',
|
|
534
|
-
{
|
|
535
|
-
staticClass: `carousel-control-${direction}`,
|
|
536
|
-
attrs: {
|
|
537
|
-
href: '#',
|
|
538
|
-
role: 'button',
|
|
539
|
-
'aria-controls': idInner,
|
|
540
|
-
'aria-disabled': isSliding ? 'true' : null
|
|
541
|
-
},
|
|
542
|
-
on: {
|
|
543
|
-
click: handlerWrapper,
|
|
544
|
-
keydown: handlerWrapper
|
|
545
|
-
}
|
|
546
|
-
},
|
|
547
|
-
[
|
|
548
|
-
h('span', {
|
|
549
|
-
staticClass: `carousel-control-${direction}-icon`,
|
|
550
|
-
attrs: { 'aria-hidden': 'true' }
|
|
551
|
-
}),
|
|
552
|
-
h('span', { class: 'sr-only' }, [label])
|
|
553
|
-
]
|
|
554
|
-
)
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
$controls = [
|
|
558
|
-
makeControl('prev', this.labelPrev, this.prev),
|
|
559
|
-
makeControl('next', this.labelNext, this.next)
|
|
560
|
-
]
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// Indicators
|
|
564
|
-
const $indicators = h(
|
|
565
|
-
'ol',
|
|
566
|
-
{
|
|
567
|
-
staticClass: 'carousel-indicators',
|
|
568
|
-
directives: [{ name: 'show', value: indicators }],
|
|
569
|
-
attrs: {
|
|
570
|
-
id: this.safeId('__BV_indicators_'),
|
|
571
|
-
'aria-hidden': indicators ? 'false' : 'true',
|
|
572
|
-
'aria-label': this.labelIndicators,
|
|
573
|
-
'aria-owns': idInner
|
|
574
|
-
}
|
|
575
|
-
},
|
|
576
|
-
this.slides.map((slide, i) => {
|
|
577
|
-
const handler = event => {
|
|
578
|
-
this.handleClick(event, () => {
|
|
579
|
-
this.setSlide(i)
|
|
580
|
-
})
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
return h('li', {
|
|
584
|
-
class: { active: i === index },
|
|
585
|
-
attrs: {
|
|
586
|
-
role: 'button',
|
|
587
|
-
id: this.safeId(`__BV_indicator_${i + 1}_`),
|
|
588
|
-
tabindex: indicators ? '0' : '-1',
|
|
589
|
-
'aria-current': i === index ? 'true' : 'false',
|
|
590
|
-
'aria-label': `${this.labelGotoSlide} ${i + 1}`,
|
|
591
|
-
'aria-describedby': slide.id || null,
|
|
592
|
-
'aria-controls': idInner
|
|
593
|
-
},
|
|
594
|
-
on: {
|
|
595
|
-
click: handler,
|
|
596
|
-
keydown: handler
|
|
597
|
-
},
|
|
598
|
-
key: `slide_${i}`
|
|
599
|
-
})
|
|
600
|
-
})
|
|
601
|
-
)
|
|
602
|
-
|
|
603
|
-
const on = {
|
|
604
|
-
mouseenter: noHoverPause ? noop : pause,
|
|
605
|
-
mouseleave: noHoverPause ? noop : restart,
|
|
606
|
-
focusin: pause,
|
|
607
|
-
focusout: restart,
|
|
608
|
-
keydown: event => {
|
|
609
|
-
/* istanbul ignore next */
|
|
610
|
-
if (/input|textarea/i.test(event.target.tagName)) {
|
|
611
|
-
return
|
|
612
|
-
}
|
|
613
|
-
const { keyCode } = event
|
|
614
|
-
if (keyCode === CODE_LEFT || keyCode === CODE_RIGHT) {
|
|
615
|
-
stopEvent(event)
|
|
616
|
-
this[keyCode === CODE_LEFT ? 'prev' : 'next']()
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
// Touch support event handlers for environment
|
|
621
|
-
if (HAS_TOUCH_SUPPORT && !noTouch) {
|
|
622
|
-
// Attach appropriate listeners (prepend event name with '&' for passive mode)
|
|
623
|
-
/* istanbul ignore next: JSDOM doesn't support touch events */
|
|
624
|
-
if (HAS_POINTER_EVENT_SUPPORT) {
|
|
625
|
-
on['&pointerdown'] = touchStart
|
|
626
|
-
on['&pointerup'] = touchEnd
|
|
627
|
-
} else {
|
|
628
|
-
on['&touchstart'] = touchStart
|
|
629
|
-
on['&touchmove'] = this.touchMove
|
|
630
|
-
on['&touchend'] = touchEnd
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// Return the carousel
|
|
635
|
-
return h(
|
|
636
|
-
'div',
|
|
637
|
-
{
|
|
638
|
-
staticClass: 'carousel',
|
|
639
|
-
class: {
|
|
640
|
-
slide: !noAnimation,
|
|
641
|
-
'carousel-fade': !noAnimation && this.fade,
|
|
642
|
-
'pointer-event': HAS_TOUCH_SUPPORT && HAS_POINTER_EVENT_SUPPORT && !noTouch
|
|
643
|
-
},
|
|
644
|
-
style: { background },
|
|
645
|
-
attrs: {
|
|
646
|
-
role: 'region',
|
|
647
|
-
id: this.safeId(),
|
|
648
|
-
'aria-busy': isSliding ? 'true' : 'false'
|
|
649
|
-
},
|
|
650
|
-
on
|
|
651
|
-
},
|
|
652
|
-
[$inner, $controls, $indicators]
|
|
653
|
-
)
|
|
654
|
-
}
|
|
655
|
-
})
|