quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
236 lines (191 loc) • 6.5 kB
JavaScript
import { h, ref, computed, withDirectives, onBeforeUnmount, onBeforeUpdate, getCurrentInstance } from 'vue'
import TouchPan from '../../directives/TouchPan.js'
import useDark, { useDarkProps } from '../../composables/private/use-dark.js'
import useCache from '../../composables/private/use-cache.js'
import { createComponent } from '../../utils/private/create.js'
import { hSlot } from '../../utils/private/render.js'
const slotsDef = [
[ 'left', 'center', 'start', 'width' ],
[ 'right', 'center', 'end', 'width' ],
[ 'top', 'start', 'center', 'height' ],
[ 'bottom', 'end', 'center', 'height' ]
]
export default createComponent({
name: 'QSlideItem',
props: {
...useDarkProps,
leftColor: String,
rightColor: String,
topColor: String,
bottomColor: String,
onSlide: Function
},
emits: [ 'action', 'top', 'right', 'bottom', 'left' ],
setup (props, { slots, emit }) {
const { proxy } = getCurrentInstance()
const { $q } = proxy
const isDark = useDark(props, $q)
const { getCacheWithFn } = useCache()
const contentRef = ref(null)
let timer = null, pan = {}, dirRefs = {}, dirContentRefs = {}
const langDir = computed(() => (
$q.lang.rtl === true
? { left: 'right', right: 'left' }
: { left: 'left', right: 'right' }
))
const classes = computed(() =>
'q-slide-item q-item-type overflow-hidden'
+ (isDark.value === true ? ' q-slide-item--dark q-dark' : '')
)
function reset () {
contentRef.value.style.transform = 'translate(0,0)'
}
function emitSlide (side, ratio, isReset) {
props.onSlide !== void 0 && emit('slide', { side, ratio, isReset })
}
function onPan (evt) {
const node = contentRef.value
if (evt.isFirst) {
pan = {
dir: null,
size: { left: 0, right: 0, top: 0, bottom: 0 },
scale: 0
}
node.classList.add('no-transition')
slotsDef.forEach(slotName => {
if (slots[ slotName[ 0 ] ] !== void 0) {
const node = dirContentRefs[ slotName[ 0 ] ]
node.style.transform = 'scale(1)'
pan.size[ slotName[ 0 ] ] = node.getBoundingClientRect()[ slotName[ 3 ] ]
}
})
pan.axis = (evt.direction === 'up' || evt.direction === 'down')
? 'Y'
: 'X'
}
else if (evt.isFinal) {
node.classList.remove('no-transition')
if (pan.scale === 1) {
node.style.transform = `translate${ pan.axis }(${ pan.dir * 100 }%)`
timer !== null && clearTimeout(timer)
timer = setTimeout(() => {
timer = null
emit(pan.showing, { reset })
emit('action', { side: pan.showing, reset })
}, 230)
}
else {
node.style.transform = 'translate(0,0)'
emitSlide(pan.showing, 0, true)
}
return
}
else {
evt.direction = pan.axis === 'X'
? evt.offset.x < 0 ? 'left' : 'right'
: evt.offset.y < 0 ? 'up' : 'down'
}
if (
(slots.left === void 0 && evt.direction === langDir.value.right)
|| (slots.right === void 0 && evt.direction === langDir.value.left)
|| (slots.top === void 0 && evt.direction === 'down')
|| (slots.bottom === void 0 && evt.direction === 'up')
) {
node.style.transform = 'translate(0,0)'
return
}
let showing, dir, dist
if (pan.axis === 'X') {
dir = evt.direction === 'left' ? -1 : 1
showing = dir === 1 ? langDir.value.left : langDir.value.right
dist = evt.distance.x
}
else {
dir = evt.direction === 'up' ? -2 : 2
showing = dir === 2 ? 'top' : 'bottom'
dist = evt.distance.y
}
if (pan.dir !== null && Math.abs(dir) !== Math.abs(pan.dir)) {
return
}
if (pan.dir !== dir) {
[ 'left', 'right', 'top', 'bottom' ].forEach(d => {
if (dirRefs[ d ]) {
dirRefs[ d ].style.visibility = showing === d
? 'visible'
: 'hidden'
}
})
pan.showing = showing
pan.dir = dir
}
pan.scale = Math.max(0, Math.min(1, (dist - 40) / pan.size[ showing ]))
node.style.transform = `translate${ pan.axis }(${ dist * dir / Math.abs(dir) }px)`
dirContentRefs[ showing ].style.transform = `scale(${ pan.scale })`
emitSlide(showing, pan.scale, false)
}
onBeforeUpdate(() => {
dirRefs = {}
dirContentRefs = {}
})
onBeforeUnmount(() => {
timer !== null && clearTimeout(timer)
})
// expose public methods
Object.assign(proxy, { reset })
return () => {
const
content = [],
slotsList = {
left: slots[ langDir.value.right ] !== void 0,
right: slots[ langDir.value.left ] !== void 0,
up: slots.bottom !== void 0,
down: slots.top !== void 0
},
dirs = Object.keys(slotsList).filter(key => slotsList[ key ] === true)
slotsDef.forEach(slotName => {
const dir = slotName[ 0 ]
if (slots[ dir ] !== void 0) {
content.push(
h('div', {
ref: el => { dirRefs[ dir ] = el },
class: `q-slide-item__${ dir } absolute-full row no-wrap items-${ slotName[ 1 ] } justify-${ slotName[ 2 ] }`
+ (props[ dir + 'Color' ] !== void 0 ? ` bg-${ props[ dir + 'Color' ] }` : '')
}, [
h('div', { ref: el => { dirContentRefs[ dir ] = el } }, slots[ dir ]())
])
)
}
})
const node = h('div', {
key: `${ dirs.length === 0 ? 'only-' : '' } content`,
ref: contentRef,
class: 'q-slide-item__content'
}, hSlot(slots.default))
if (dirs.length === 0) {
content.push(node)
}
else {
content.push(
withDirectives(node, getCacheWithFn('dir#' + dirs.join(''), () => {
const modifiers = {
prevent: true,
stop: true,
mouse: true
}
dirs.forEach(dir => {
modifiers[ dir ] = true
})
return [ [
TouchPan,
onPan,
void 0,
modifiers
] ]
}))
)
}
return h('div', { class: classes.value }, content)
}
}
})