quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
265 lines (220 loc) • 6.88 kB
JavaScript
import {
h,
ref,
computed,
withDirectives,
onBeforeUnmount,
onBeforeUpdate,
getCurrentInstance
} from 'vue'
import TouchPan from '../../directives/touch-pan/TouchPan.js'
import useDark, {
useDarkProps
} from '../../composables/private.use-dark/use-dark.js'
import useRenderCache from '../../composables/use-render-cache/use-render-cache.js'
import { createComponent } from '../../utils/private.create/create.js'
import { hSlot } from '../../utils/private.render/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 { getCache } = useRenderCache()
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) {
if (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 localNode = dirContentRefs[slotName[0]]
localNode.style.transform = 'scale(1)'
pan.size[slotName[0]] =
localNode.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}%)`
if (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(() => {
if (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',
{
key: dir,
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,
getCache('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)
}
}
})