@fesjs/fes-design
Version:
fes-design for PC
195 lines (191 loc) • 6.1 kB
JavaScript
import { defineComponent, ref, computed, createVNode, mergeProps } from 'vue';
import { Virtualizer } from 'virtua/vue';
import FScrollbar from '../scrollbar/scrollbar.js';
const virtualScrollerProps = {
scrollbarProps: Object,
dataSources: {
type: Array,
default: () => []
},
keeps: {
type: Number,
default: 30
},
direction: {
type: String,
default: 'vertical' // the other value is horizontal
},
wrapTag: {
type: String,
default: 'div'
},
wrapClass: {
type: String,
default: ''
},
wrapStyle: {
type: Object
},
itemSize: {
type: Number,
required: true
},
itemProps: Function,
itemTag: {
type: String,
default: 'div'
},
renderItemList: {
type: Function
},
topThreshold: {
type: Number,
default: 0
},
bottomThreshold: {
type: Number,
default: 0
}
};
var virtualScroller = defineComponent({
name: 'FVirtualScroller',
props: virtualScrollerProps,
emits: ['toTop', 'toBottom', 'scroll'],
setup(props, _ref) {
let {
slots,
expose,
emit
} = _ref;
const scrollRef = ref();
const scrollContainerRef = ref();
const virtualRef = ref();
const isHorizontal = computed(() => {
return props.direction === 'horizontal';
});
const directionKey = computed(() => {
return isHorizontal.value ? 'scrollLeft' : 'scrollTop';
});
const getOffset = () => {
const root = scrollContainerRef.value;
return root ? Math.ceil(root[directionKey.value]) : 0;
};
const getScrollSize = () => {
const key = isHorizontal.value ? 'scrollWidth' : 'scrollHeight';
const root = scrollContainerRef.value;
return root ? Math.ceil(root[key]) : 0;
};
// return client viewport size
const getClientSize = () => {
const key = isHorizontal.value ? 'clientWidth' : 'clientHeight';
const root = scrollContainerRef.value;
return root ? Math.ceil(root[key]) : 0;
};
// set current scroll position to bottom
const scrollToBottom = () => {
const root = scrollContainerRef.value;
if (root) {
var _virtualRef$value;
(_virtualRef$value = virtualRef.value) === null || _virtualRef$value === void 0 || _virtualRef$value.scrollTo(getScrollSize());
// check if it's really scrolled to the bottom
// maybe list doesn't render and calculate to last range
// so we need retry in next event loop until it really at bottom
setTimeout(() => {
if (getOffset() + getClientSize() < getScrollSize()) {
scrollToBottom();
}
}, 10);
}
};
expose({
scrollRef,
virtualRef,
scrollToIndex: (index, opt) => {
var _virtualRef$value2;
(_virtualRef$value2 = virtualRef.value) === null || _virtualRef$value2 === void 0 || _virtualRef$value2.scrollToIndex(index, opt);
},
scrollToBottom,
scrollTo: offset => {
var _virtualRef$value3;
(_virtualRef$value3 = virtualRef.value) === null || _virtualRef$value3 === void 0 || _virtualRef$value3.scrollTo(offset);
},
scrollBy: offset => {
var _virtualRef$value4;
(_virtualRef$value4 = virtualRef.value) === null || _virtualRef$value4 === void 0 || _virtualRef$value4.scrollBy(offset);
},
getItemOffset: index => {
var _virtualRef$value5;
return (_virtualRef$value5 = virtualRef.value) === null || _virtualRef$value5 === void 0 ? void 0 : _virtualRef$value5.getItemOffset(index);
},
getItemSize: index => {
var _virtualRef$value6;
return (_virtualRef$value6 = virtualRef.value) === null || _virtualRef$value6 === void 0 ? void 0 : _virtualRef$value6.getItemSize(index);
},
findStartIndex: () => {
var _virtualRef$value7;
(_virtualRef$value7 = virtualRef.value) === null || _virtualRef$value7 === void 0 || _virtualRef$value7.findStartIndex();
},
findEndIndex: () => {
var _virtualRef$value8;
(_virtualRef$value8 = virtualRef.value) === null || _virtualRef$value8 === void 0 || _virtualRef$value8.findEndIndex();
},
getOffset,
getScrollSize,
getClientSize
});
let lastScrollTop = 0;
const onScroll = e => {
emit('scroll', e);
const currentScrollTop = e.target.scrollTop;
const isScrollUp = currentScrollTop < lastScrollTop;
lastScrollTop = currentScrollTop;
const offset = getOffset();
const scrollSize = getScrollSize();
const clientSize = getClientSize();
if (isScrollUp && offset <= props.topThreshold) {
emit('toTop');
}
if (!isScrollUp && offset + clientSize >= scrollSize - props.bottomThreshold) {
emit('toBottom');
}
};
return () => {
const virtualizer = createVNode(Virtualizer, {
"ref": virtualRef,
"data": props.dataSources,
"scrollRef": scrollContainerRef.value,
"horizontal": isHorizontal.value,
"as": props.wrapTag,
"class": props.wrapClass,
"style": props.wrapStyle,
"itemSize": props.itemSize,
"itemProps": props.itemProps,
"item": props.itemTag,
"overscan": Math.ceil(props.keeps / 3)
}, {
default: _ref2 => {
var _slots$default;
let {
item,
index
} = _ref2;
return (_slots$default = slots.default) === null || _slots$default === void 0 ? void 0 : _slots$default.call(slots, {
source: item,
index
});
}
});
return createVNode(FScrollbar, mergeProps({
"ref": e => {
scrollRef.value = e;
scrollContainerRef.value = e === null || e === void 0 ? void 0 : e.containerRef;
}
}, props.scrollbarProps, {
"onScroll": onScroll
}), {
default: () => [props.renderItemList ? props.renderItemList(virtualizer) : virtualizer]
});
};
}
});
export { virtualScroller as default, virtualScrollerProps };