quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
256 lines (217 loc) • 7.25 kB
JavaScript
import Vue from 'vue'
import QBtn from '../btn/QBtn.js'
import DarkMixin from '../../mixins/dark.js'
import { PanelParentMixin } from '../../mixins/panel.js'
import FullscreenMixin from '../../mixins/fullscreen.js'
import { isNumber } from '../../utils/is.js'
import { mergeSlot } from '../../utils/slot.js'
import cache from '../../utils/cache.js'
export default Vue.extend({
name: 'QCarousel',
mixins: [ DarkMixin, PanelParentMixin, FullscreenMixin ],
props: {
height: String,
padding: Boolean,
controlType: {
type: String,
validator: v => [ 'regular', 'flat', 'outline', 'push', 'unelevated' ].includes(v),
default: 'flat'
},
controlColor: String,
controlTextColor: String,
autoplay: [ Number, Boolean ],
arrows: Boolean,
prevIcon: String,
nextIcon: String,
navigation: Boolean,
navigationPosition: {
type: String,
validator: v => [ 'top', 'right', 'bottom', 'left' ].includes(v)
},
navigationIcon: String,
navigationActiveIcon: String,
thumbnails: Boolean
},
computed: {
style () {
if (this.inFullscreen !== true && this.height !== void 0) {
return {
height: this.height
}
}
},
direction () {
return this.vertical === true ? 'vertical' : 'horizontal'
},
classes () {
return `q-carousel q-panel-parent q-carousel--with${this.padding === true ? '' : 'out'}-padding` +
(this.inFullscreen === true ? ' fullscreen' : '') +
(this.isDark === true ? ' q-carousel--dark q-dark' : '') +
(this.arrows === true ? ` q-carousel--arrows-${this.direction}` : '') +
(this.navigation === true ? ` q-carousel--navigation-${this.navigationPositionComputed}` : '')
},
arrowIcons () {
const ico = [
this.prevIcon || this.$q.iconSet.carousel[this.vertical === true ? 'up' : 'left'],
this.nextIcon || this.$q.iconSet.carousel[this.vertical === true ? 'down' : 'right']
]
return this.vertical === false && this.$q.lang.rtl === true
? ico.reverse()
: ico
},
navIcon () {
return this.navigationIcon || this.$q.iconSet.carousel.navigationIcon
},
navActiveIcon () {
return this.navigationActiveIcon || this.navIcon
},
navigationPositionComputed () {
return this.navigationPosition || (this.vertical === true ? 'right' : 'bottom')
},
controlProps () {
return {
color: this.controlColor,
textColor: this.controlTextColor,
round: true,
[ this.controlType ]: true,
dense: true
}
},
transitionPrevComputed () {
return this.transitionPrev || `fade`
},
transitionNextComputed () {
return this.transitionNext || `fade`
}
},
watch: {
value () {
if (this.autoplay) {
clearInterval(this.timer)
this.__startTimer()
}
},
autoplay (val) {
if (val) {
this.__startTimer()
}
else {
clearInterval(this.timer)
}
}
},
methods: {
__startTimer () {
this.timer = setTimeout(
this.next,
isNumber(this.autoplay) ? this.autoplay : 5000
)
},
__getNavigationContainer (h, type, mapping) {
return h('div', {
class: 'q-carousel__control q-carousel__navigation no-wrap absolute flex' +
` q-carousel__navigation--${type} q-carousel__navigation--${this.navigationPositionComputed}` +
(this.controlColor !== void 0 ? ` text-${this.controlColor}` : '')
}, [
h('div', {
staticClass: 'q-carousel__navigation-inner flex flex-center no-wrap'
}, this.__getEnabledPanels().map(mapping))
])
},
__getContent (h) {
const node = []
if (this.navigation === true) {
const fn = this.$scopedSlots['navigation-icon'] !== void 0
? this.$scopedSlots['navigation-icon']
: opts => h(QBtn, {
key: 'nav' + opts.name,
class: `q-carousel__navigation-icon q-carousel__navigation-icon--${opts.active === true ? '' : 'in'}active`,
props: opts.btnProps,
on: cache(this, 'nav#' + opts.name, { click: opts.onClick })
})
const maxIndex = this.panels.length - 1
node.push(
this.__getNavigationContainer(h, 'buttons', (panel, index) => {
const name = panel.componentOptions.propsData.name
const active = this.panelIndex === index
return fn({
index,
maxIndex,
name,
active,
btnProps: {
icon: active === true ? this.navActiveIcon : this.navIcon,
size: 'sm',
...this.controlProps
},
onClick: () => { this.goTo(name) }
})
})
)
}
else if (this.thumbnails === true) {
const color = this.controlColor !== void 0
? ` text-${this.controlColor}`
: ''
node.push(this.__getNavigationContainer(h, 'thumbnails', panel => {
const slide = panel.componentOptions.propsData
return h('img', {
class: `q-carousel__thumbnail q-carousel__thumbnail--${slide.name === this.value ? '' : 'in'}active` + color,
attrs: {
src: slide.imgSrc
},
key: 'tmb#' + slide.name,
on: cache(this, 'tmb#' + slide.name, { click: () => { this.goTo(slide.name) } })
})
}))
}
if (this.arrows === true && this.panelIndex >= 0) {
if (this.infinite === true || this.panelIndex > 0) {
node.push(
h('div', {
key: 'prev',
staticClass: `q-carousel__control q-carousel__arrow q-carousel__prev-arrow q-carousel__prev-arrow--${this.direction} absolute flex flex-center`
}, [
h(QBtn, {
props: { icon: this.arrowIcons[0], ...this.controlProps },
on: cache(this, 'prev', { click: this.previous })
})
])
)
}
if (this.infinite === true || this.panelIndex < this.panels.length - 1) {
node.push(
h('div', {
key: 'next',
staticClass: `q-carousel__control q-carousel__arrow q-carousel__next-arrow q-carousel__next-arrow--${this.direction} absolute flex flex-center`
}, [
h(QBtn, {
props: { icon: this.arrowIcons[1], ...this.controlProps },
on: cache(this, 'next', { click: this.next })
})
])
)
}
}
return mergeSlot(node, this, 'control')
},
__renderPanels (h) {
return h('div', {
style: this.style,
class: this.classes,
on: { ...this.qListeners }
}, [
h('div', {
staticClass: 'q-carousel__slides-container',
directives: this.panelDirectives
}, this.__getPanelContent(h))
].concat(this.__getContent(h)))
}
},
mounted () {
this.autoplay && this.__startTimer()
},
beforeDestroy () {
clearInterval(this.timer)
}
})