diff --git a/packages/core/useInfiniteScroll/index.ts b/packages/core/useInfiniteScroll/index.ts index deeefb2bafd..3451d2c313a 100644 --- a/packages/core/useInfiniteScroll/index.ts +++ b/packages/core/useInfiniteScroll/index.ts @@ -1,5 +1,5 @@ import type { UnwrapNestedRefs } from 'vue-demi' -import { reactive, watch } from 'vue-demi' +import { nextTick, reactive, unref, watch } from 'vue-demi' import type { MaybeRef } from '@vueuse/shared' import type { UseScrollOptions } from '../useScroll' import { useScroll } from '../useScroll' @@ -11,6 +11,20 @@ export interface UseInfiniteScrollOptions extends UseScrollOptions { * @default 0 */ distance?: number + + /** + * The direction in which to listen the scroll. + * + * @default 'bottom' + */ + direction?: 'top' | 'bottom' + + /** + * Whether to preserve the current scroll position when loading more items. + * + * @default false + */ + preserveScrollPosition?: boolean } /** @@ -20,25 +34,42 @@ export interface UseInfiniteScrollOptions extends UseScrollOptions { */ export function useInfiniteScroll( element: MaybeRef, - onLoadMore: (state: UnwrapNestedRefs>) => void, + onLoadMore: (state: UnwrapNestedRefs>) => void | Promise, options: UseInfiniteScrollOptions = {}, ) { + const direction = options.direction ?? 'bottom' const state = reactive(useScroll( element, { ...options, offset: { - bottom: options.distance ?? 0, + [direction]: options.distance ?? 0, ...options.offset, }, }, )) watch( - () => state.arrivedState.bottom, - (v) => { - if (v) - onLoadMore(state) + () => state.arrivedState[direction], + async(v) => { + if (v) { + const elem = unref(element) as Element + const previous = { + height: elem?.scrollHeight ?? 0, + width: elem?.scrollWidth ?? 0, + } + + await onLoadMore(state) + + if (options.preserveScrollPosition && elem) { + nextTick(() => { + elem.scrollTo({ + top: elem.scrollHeight - previous.height, + left: elem.scrollWidth - previous.width, + }) + }) + } + } }, ) }