vue-glide-js
Version:
Vue component on top of the Glide.js
332 lines (307 loc) • 7.2 kB
JavaScript
import Glide from '@glidejs/glide'
import '@glidejs/glide/dist/css/glide.core.min.css'
import events from './events'
export default {
name: 'VueGlide',
model: {
prop: 'active',
event: 'change'
},
props: {
type: {
type: String,
default: 'slider'
},
startAt: {
type: Number,
default: 0
},
perView: {
type: Number,
default: 3
},
focusAt: {
type: [String, Number],
default: 0
},
gap: {
type: Number,
default: 10
},
autoplay: {
type: [Number, Boolean],
default: false
},
hoverpause: {
type: Boolean,
default: true
},
keyboard: {
type: Boolean,
default: true
},
bound: {
type: Boolean,
default: false
},
swipeThreshold: {
type: [Number, Boolean],
default: 80
},
dragThreshold: {
type: [Number, Boolean],
default: 120
},
perTouch: {
type: [Number, Boolean],
default: false
},
touchRatio: {
type: Number,
default: 0.5
},
touchAngle: {
type: Number,
default: 45
},
animationDuration: {
type: Number,
default: 400
},
rewind: {
type: Boolean,
default: true
},
rewindDuration: {
type: Number,
default: 800
},
animationTimingFunc: {
type: String,
default: 'cubic-bezier(0.165, 0.840, 0.440, 1.000)'
},
direction: {
type: String,
default: 'ltr'
},
peek: {
type: [Number, Object],
default: 0
},
breakpoints: {
type: Object,
default: () => {}
},
classes: {
type: Object,
default: () => {}
},
throttle: {
type: Number,
default: 25
},
toSlideByClick: {
type: Boolean,
default: false
},
bullet: {
type: Boolean,
default: false
},
options: {
type: Object,
default: () => {}
},
active: {
type: Number,
default: null
}
},
data () {
return {
glide: undefined
}
},
render (h) {
let control
let bullet
let buttons = []
// Pass only vue-glide-slide
let slides = this.$slots.default.filter(
c => {
let isVueGlideTag = false
if (c.componentOptions) {
isVueGlideTag =
c.componentOptions.tag === 'VueGlideSlide' ||
c.componentOptions.tag === 'vue-glide-slide'
}
return isVueGlideTag
}
)
if (this.$slots.control && this.$slots.control.length) {
control = <div data-glide-el="controls">{this.$slots.control}</div>
}
if (this.bullet) {
for (let i = 0; i < this.slidesCount; i++) {
buttons.push(
h('button', {
key: i,
attrs: {
'data-glide-dir': '=' + i,
class: 'glide__bullet'
}
})
)
}
bullet = (
<div class="glide__bullets" data-glide-el="controls[nav]">
{buttons}
</div>
)
}
return (
<div class="glide">
<div class="glide__track" data-glide-el="track">
<ul class="glide__slides">{slides}</ul>
</div>
{control}
{bullet}
</div>
)
},
watch: {
active () {
this.changeSlideByModel()
}
},
computed: {
currentSlide () {
return this.glide.index
},
slidesCount () {
return this.$slots.default.filter(
c => c.componentOptions && c.componentOptions.tag === 'vue-glide-slide'
).length
}
},
mounted () {
this.init()
},
methods: {
/**
* Initialization glide
*/
init () {
const defaultClasses = {
direction: {
ltr: 'glide--ltr',
rtl: 'glide--rtl'
},
slider: 'glide--slider',
carousel: 'glide--carousel',
swipeable: 'glide--swipeable',
dragging: 'glide--dragging',
cloneSlide: 'glide__slide--clone',
activeNav: 'glide__bullet--active',
activeSlide: 'glide__slide--active',
disabledArrow: 'glide__arrow--disabled'
}
const initOptions = Object.assign({}, this.$props)
// Remove additional props 'options'
delete initOptions.options
initOptions.classes = Object.assign(defaultClasses, this.classes)
const mergedOptions = Object.assign(initOptions, this.options)
if (this.toSlideByClick) {
this.goToSlideByClick()
}
this.glide = new Glide(this.$el, mergedOptions)
this.eventConnector(events)
this.glide.mount()
this.addEventListenerToSlide()
this.bindModel()
this.changeSlideByModel()
},
/**
* Go to the slide
* @param {string} pattern - special format glide.js api
* Available pattern:
* * > - Move one forward
* * < - Move one backward
* * ={i} - Go to {i} zero-based slide (eq. '=1', will go to second slide)
* * >> - Rewinds to end (last slide)
* * << - Rewinds to start (first slide)
*/
go (pattern) {
this.glide.go(pattern)
},
/**
* Go to the slide by click on slide
*/
goToSlideByClick () {
this.$on('glide:slide-click', e => this.go(`=${e}`))
},
/**
* Pass glide events to Vue events
* @param {array} - glide events
*/
eventConnector (events) {
events.map(event => {
this.glide.on(event, e => {
const emitter = event.replace(/\./, '-')
this.$emit(`glide:${emitter}`, e)
})
})
},
/**
* Bind v-model
*/
bindModel () {
this.$on('glide:move', () => {
this.$emit('change', this.currentSlide)
})
},
/**
* Change slide by v-model
*/
changeSlideByModel () {
if (this.active === null) return
if (this.active > this.slidesCount - 1) {
return this.go(`=${this.slidesCount - 1}`)
}
if (this.active < 0) {
return this.go('=0')
}
this.go(`=${this.active}`)
},
/**
* Adding an event handler for slides, including DOM cloned slider elements
* When type is 'carousel', glide.js clones DOM slides
* @returns {number} - index of slide
*/
addEventListenerToSlide () {
let slides = document.querySelectorAll('.glide__slide')
slides = Array.from(slides)
slides.forEach(el => {
el.addEventListener('click', e => {
// Recursive bubbling from nested elements to find '.glide__slide'
const recursive = el => {
const parent = el.parentNode
const contain = parent.classList.contains('glide__slide')
if (contain) {
return this.$emit(
'glide:slide-click',
Number(parent.dataset.glideIndex)
)
} else {
recursive(parent)
}
}
if (!e.target.classList.contains('glide__slide')) {
recursive(e.target)
}
this.$emit('glide:slide-click', Number(e.target.dataset.glideIndex))
})
})
}
}
}