From f898404157e1aebe802dc028422ab96478f8e97f Mon Sep 17 00:00:00 2001 From: Yuchao Date: Wed, 28 Jun 2023 04:42:41 +1000 Subject: [PATCH] fix(VInfiniteScroll): persist load logic until is intersected (#17475) fixes #17358 Co-authored-by: John Leider --- .../labs/VInfiniteScroll/VInfiniteScroll.tsx | 31 ++++++++++++++++--- .../__tests__/VInfiniteScroll.spec.cy.tsx | 26 ++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/packages/vuetify/src/labs/VInfiniteScroll/VInfiniteScroll.tsx b/packages/vuetify/src/labs/VInfiniteScroll/VInfiniteScroll.tsx index b3f7d7b3e2c..37fa8dfce77 100644 --- a/packages/vuetify/src/labs/VInfiniteScroll/VInfiniteScroll.tsx +++ b/packages/vuetify/src/labs/VInfiniteScroll/VInfiniteScroll.tsx @@ -78,7 +78,7 @@ export const VInfiniteScrollIntersect = defineComponent({ }, emits: { - intersect: (side: InfiniteScrollSide) => true, + intersect: (side: InfiniteScrollSide, isIntersecting: boolean) => true, }, setup (props, { emit }) { @@ -89,7 +89,7 @@ export const VInfiniteScrollIntersect = defineComponent({ } : undefined) watch(isIntersecting, async val => { - if (val) emit('intersect', props.side) + emit('intersect', props.side, val) }) useRender(() => ( @@ -114,6 +114,7 @@ export const VInfiniteScroll = genericComponent()({ const startStatus = ref('ok') const endStatus = ref('ok') const margin = computed(() => convertToUnit(props.margin)) + const isIntersecting = ref(false) function setScrollAmount (amount: number) { if (!rootEl.value) return @@ -166,7 +167,16 @@ export const VInfiniteScroll = genericComponent()({ } let previousScrollSize = 0 - function handleIntersect (side: InfiniteScrollSide) { + function handleIntersect (side: InfiniteScrollSide, _isIntersecting: boolean) { + isIntersecting.value = _isIntersecting + if (isIntersecting.value) { + intersecting(side) + } + } + + function intersecting (side: InfiniteScrollSide) { + if (props.mode !== 'manual' && !isIntersecting.value) return + const status = getStatus(side) if (!rootEl.value || status === 'loading') return @@ -177,9 +187,22 @@ export const VInfiniteScroll = genericComponent()({ setStatus(side, status) nextTick(() => { + if (status === 'empty' || status === 'error') return + if (status === 'ok' && side === 'start') { setScrollAmount(getScrollSize() - previousScrollSize + getScrollAmount()) } + if (props.mode !== 'manual') { + nextTick(() => { + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + intersecting(side) + }) + }) + }) + }) + } }) } @@ -191,7 +214,7 @@ export const VInfiniteScroll = genericComponent()({ function renderSide (side: InfiniteScrollSide, status: InfiniteScrollStatus) { if (props.side !== side && props.side !== 'both') return - const onClick = () => handleIntersect(side) + const onClick = () => intersecting(side) const slotProps = { side, props: { onClick, color: props.color } } if (status === 'error') return slots.error?.(slotProps) diff --git a/packages/vuetify/src/labs/VInfiniteScroll/__tests__/VInfiniteScroll.spec.cy.tsx b/packages/vuetify/src/labs/VInfiniteScroll/__tests__/VInfiniteScroll.spec.cy.tsx index 3272e7bd25e..91d8fadab54 100644 --- a/packages/vuetify/src/labs/VInfiniteScroll/__tests__/VInfiniteScroll.spec.cy.tsx +++ b/packages/vuetify/src/labs/VInfiniteScroll/__tests__/VInfiniteScroll.spec.cy.tsx @@ -4,6 +4,7 @@ import { VInfiniteScroll } from '../VInfiniteScroll' // Utilities +import { ref } from 'vue' import { createRange } from '@/util' // Constants @@ -76,4 +77,29 @@ describe('VInfiniteScroll', () => { .get('.v-infinite-scroll .v-progress-circular').should('exist') .get('@load').should('have.been.calledOnce') }) + + // https://github.com/vuetifyjs/vuetify/issues/17358 + it('should keep triggering load logic until VInfiniteScrollIntersect disappears', () => { + const loadTracker = cy.spy().as('loadTracker') + const items = ref(Array.from({ length: 3 }, (k, v) => v + 1)) + + const load = async ({ done }: any) => { + setTimeout(() => { + items.value.push(...Array.from({ length: 3 }, (k, v) => v + items.value.at(-1)! + 1)) + loadTracker() + done('ok') + }, 100) + } + + cy.viewport(400, 200) + .mount(() => ( + + { items.value.map(item => ( +
Item #{ item }
+ ))} +
+ )) + .get('.v-infinite-scroll .v-progress-circular').should('exist') + .get('@loadTracker').should('have.been.calledTwice') + }) })