UNPKG

@fesjs/fes-design

Version:
195 lines (191 loc) 6.1 kB
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 };