quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
171 lines (135 loc) • 4.48 kB
JavaScript
import { h, ref, computed, watch, onBeforeMount, onMounted, onBeforeUnmount, onActivated, onDeactivated } from 'vue'
import QList from '../item/QList.js'
import QMarkupTable from '../markup-table/QMarkupTable.js'
import getTableMiddle from '../table/get-table-middle.js'
import { useVirtualScroll, useVirtualScrollProps } from './use-virtual-scroll.js'
import { createComponent } from '../../utils/private/create.js'
import { getScrollTarget } from '../../utils/scroll.js'
import { listenOpts } from '../../utils/event.js'
import { hMergeSlot } from '../../utils/private/render.js'
const comps = {
list: QList,
table: QMarkupTable
}
const typeOptions = [ 'list', 'table', '__qtable' ]
export default createComponent({
name: 'QVirtualScroll',
props: {
...useVirtualScrollProps,
type: {
type: String,
default: 'list',
validator: v => typeOptions.includes(v)
},
items: {
type: Array,
default: () => []
},
itemsFn: Function,
itemsSize: Number,
scrollTarget: {
default: void 0
}
},
setup (props, { slots, attrs }) {
let localScrollTarget
const rootRef = ref(null)
const virtualScrollLength = computed(() => (
props.itemsSize >= 0 && props.itemsFn !== void 0
? parseInt(props.itemsSize, 10)
: (Array.isArray(props.items) ? props.items.length : 0)
))
const {
virtualScrollSliceRange,
localResetVirtualScroll,
padVirtualScroll,
onVirtualScrollEvt
} = useVirtualScroll({
virtualScrollLength, getVirtualScrollTarget, getVirtualScrollEl
})
const virtualScrollScope = computed(() => {
if (virtualScrollLength.value === 0) {
return []
}
const mapFn = (item, i) => ({
index: virtualScrollSliceRange.value.from + i,
item
})
return props.itemsFn === void 0
? props.items.slice(virtualScrollSliceRange.value.from, virtualScrollSliceRange.value.to).map(mapFn)
: props.itemsFn(virtualScrollSliceRange.value.from, virtualScrollSliceRange.value.to - virtualScrollSliceRange.value.from).map(mapFn)
})
const classes = computed(() =>
'q-virtual-scroll q-virtual-scroll' + (props.virtualScrollHorizontal === true ? '--horizontal' : '--vertical')
+ (props.scrollTarget !== void 0 ? '' : ' scroll')
)
const attributes = computed(() => (
props.scrollTarget !== void 0 ? {} : { tabindex: 0 }
))
watch(virtualScrollLength, () => {
localResetVirtualScroll()
})
watch(() => props.scrollTarget, () => {
unconfigureScrollTarget()
configureScrollTarget()
})
function getVirtualScrollEl () {
return rootRef.value.$el || rootRef.value
}
function getVirtualScrollTarget () {
return localScrollTarget
}
function configureScrollTarget () {
localScrollTarget = getScrollTarget(getVirtualScrollEl(), props.scrollTarget)
localScrollTarget.addEventListener('scroll', onVirtualScrollEvt, listenOpts.passive)
}
function unconfigureScrollTarget () {
if (localScrollTarget !== void 0) {
localScrollTarget.removeEventListener('scroll', onVirtualScrollEvt, listenOpts.passive)
localScrollTarget = void 0
}
}
function __getVirtualChildren () {
let child = padVirtualScroll(
props.type === 'list' ? 'div' : 'tbody',
virtualScrollScope.value.map(slots.default)
)
if (slots.before !== void 0) {
child = slots.before().concat(child)
}
return hMergeSlot(slots.after, child)
}
onBeforeMount(() => {
localResetVirtualScroll()
})
onMounted(() => {
configureScrollTarget()
})
onActivated(() => {
configureScrollTarget()
})
onDeactivated(() => {
unconfigureScrollTarget()
})
onBeforeUnmount(() => {
unconfigureScrollTarget()
})
return () => {
if (slots.default === void 0) {
console.error('QVirtualScroll: default scoped slot is required for rendering')
return
}
return props.type === '__qtable'
? getTableMiddle(
{ ref: rootRef, class: 'q-table__middle ' + classes.value },
__getVirtualChildren()
)
: h(comps[ props.type ], {
...attrs,
ref: rootRef,
class: [ attrs.class, classes.value ],
...attributes.value
}, __getVirtualChildren)
}
}
})