-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(useVirtualList): List sometimes missing elements #2477
Changes from 1 commit
d33b811
386874c
7f89099
a143c6d
7c3dcd3
d3d2f49
7561fb8
511667b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import { ref } from 'vue' | ||
import { useVirtualList } from '.' | ||
|
||
describe('useVirtualList', () => { | ||
it('should be defined', () => { | ||
expect(useVirtualList).toBeDefined() | ||
}) | ||
}) | ||
|
||
describe('useVirtualList, vertical', () => { | ||
it('returns all original items if they fit the container', () => { | ||
const { | ||
list, | ||
containerProps: { ref: containerRef }, | ||
scrollTo, | ||
} = useVirtualList(ref(['a', 'b', 'c']), { itemHeight: () => 50, overscan: 0 }) | ||
const div = { ...document.createElement('div'), clientHeight: 120 } | ||
|
||
containerRef.value = div | ||
scrollTo(0) | ||
expect(list.value.length).toEqual(2) | ||
|
||
containerRef.value = { ...div, clientHeight: 150 } | ||
scrollTo(0) | ||
expect(list.value.length).toEqual(3) | ||
|
||
containerRef.value = { ...div, clientHeight: 160 } | ||
scrollTo(0) | ||
expect(list.value.length).toEqual(3) | ||
}) | ||
|
||
it('returns the current visible window of items if there are too many for the container', () => { | ||
const { | ||
list, | ||
containerProps: { ref: containerRef }, | ||
scrollTo, | ||
} = useVirtualList(ref(['a', 'b', 'c', 'd', 'e']), { itemHeight: () => 50, overscan: 0 }) | ||
const div = { ...document.createElement('div'), clientHeight: 150 } | ||
|
||
containerRef.value = div | ||
|
||
scrollTo(0) | ||
expect(list.value.map(i => i.data)).toEqual(['a', 'b', 'c']) | ||
|
||
scrollTo(1) | ||
expect(list.value.map(i => i.data)).toEqual(['b', 'c', 'd']) | ||
|
||
scrollTo(2) | ||
expect(list.value.map(i => i.data)).toEqual(['c', 'd', 'e']) | ||
}) | ||
|
||
it('returns window with overscan', () => { | ||
const { | ||
list, | ||
containerProps: { ref: containerRef }, | ||
scrollTo, | ||
} = useVirtualList(ref(['a', 'b', 'c', 'd', 'e', 'f']), { itemHeight: () => 50, overscan: 1 }) | ||
const div = { ...document.createElement('div'), clientHeight: 100 } | ||
|
||
containerRef.value = div | ||
|
||
scrollTo(0) | ||
expect(list.value.map(i => i.data)).toEqual(['a', 'b', 'c']) | ||
|
||
scrollTo(1) | ||
expect(list.value.map(i => i.data)).toEqual(['a', 'b', 'c', 'd']) | ||
|
||
scrollTo(2) | ||
expect(list.value.map(i => i.data)).toEqual(['b', 'c', 'd', 'e']) | ||
|
||
scrollTo(3) | ||
expect(list.value.map(i => i.data)).toEqual(['c', 'd', 'e', 'f']) | ||
}) | ||
}) | ||
|
||
describe('useVirtualList, horizontal', () => { | ||
it('returns all original items if they fit the container', () => { | ||
const { | ||
list, | ||
containerProps: { ref: containerRef }, | ||
scrollTo, | ||
} = useVirtualList(ref(['a', 'b', 'c']), { itemWidth: () => 50, overscan: 0 }) | ||
const div = { | ||
...document.createElement('div'), | ||
clientWidth: 120, | ||
} | ||
|
||
containerRef.value = div | ||
scrollTo(0) | ||
expect(list.value.length).toEqual(2) | ||
|
||
containerRef.value = { ...div, clientWidth: 150 } | ||
scrollTo(0) | ||
expect(list.value.length).toEqual(3) | ||
|
||
containerRef.value = { ...div, clientWidth: 160 } | ||
scrollTo(0) | ||
expect(list.value.length).toEqual(3) | ||
}) | ||
|
||
it('returns the current visible window of items if there are too many for the container', () => { | ||
const { | ||
list, | ||
containerProps: { ref: containerRef }, | ||
scrollTo, | ||
} = useVirtualList(ref(['a', 'b', 'c', 'd', 'e']), { itemWidth: () => 50, overscan: 0 }) | ||
const div = { ...document.createElement('div'), clientWidth: 150 } | ||
|
||
containerRef.value = div | ||
|
||
scrollTo(0) | ||
expect(list.value.map(i => i.data)).toEqual(['a', 'b', 'c']) | ||
|
||
scrollTo(1) | ||
expect(list.value.map(i => i.data)).toEqual(['b', 'c', 'd']) | ||
|
||
scrollTo(2) | ||
expect(list.value.map(i => i.data)).toEqual(['c', 'd', 'e']) | ||
}) | ||
|
||
it('returns window with overscan', () => { | ||
const { | ||
list, | ||
containerProps: { ref: containerRef }, | ||
scrollTo, | ||
} = useVirtualList(ref(['a', 'b', 'c', 'd', 'e', 'f']), { itemWidth: () => 50, overscan: 1 }) | ||
const div = { ...document.createElement('div'), clientWidth: 100 } | ||
|
||
containerRef.value = div | ||
|
||
scrollTo(0) | ||
expect(list.value.map(i => i.data)).toEqual(['a', 'b', 'c']) | ||
|
||
scrollTo(1) | ||
expect(list.value.map(i => i.data)).toEqual(['a', 'b', 'c', 'd']) | ||
|
||
scrollTo(2) | ||
expect(list.value.map(i => i.data)).toEqual(['b', 'c', 'd', 'e']) | ||
|
||
scrollTo(3) | ||
expect(list.value.map(i => i.data)).toEqual(['c', 'd', 'e', 'f']) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -119,21 +119,24 @@ function useVirtualListResourses<T>(list: MaybeRef<T[]>): UseVirtualListResource | |
return { state, source, currentList, size, containerRef } | ||
} | ||
|
||
function createGetViewCapacity<T>(state: UseVirtualListResources<T>['state'], source: UseVirtualListResources<T>['source'], itemSize: UseVirtualListItemSize) { | ||
return (containerHeight: number) => { | ||
function createGetViewCapacity<T>( | ||
state: UseVirtualListResources<T>['state'], | ||
source: UseVirtualListResources<T>['source'], | ||
itemSize: UseVirtualListItemSize, | ||
) { | ||
return (containerSize: number) => { | ||
if (typeof itemSize === 'number') | ||
return Math.ceil(containerHeight / itemSize) | ||
return Math.ceil(containerSize / itemSize) | ||
|
||
const { start = 0 } = state.value | ||
let sum = 0 | ||
let capacity = 0 | ||
for (let i = start; i < source.value.length; i++) { | ||
const height = itemSize(i) | ||
sum += height | ||
if (sum >= containerHeight) { | ||
capacity = i | ||
for (let i = start; i < source.value.length + 1; i++) { | ||
const size = itemSize(i) | ||
sum += size | ||
capacity = i | ||
if (sum > containerSize) | ||
Comment on lines
+133
to
+134
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving capacity out of the if is the fix. Without this, if the original list size is smaller than what could fit in the view, we end up with a capacity of 1 + overscan There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't the same thing be done with 'offset = i' in function 'createGetOffset'? |
||
break | ||
} | ||
} | ||
return capacity - start | ||
} | ||
|
@@ -155,7 +158,7 @@ function createGetOffset<T>(source: UseVirtualListResources<T>['source'], itemSi | |
break | ||
} | ||
} | ||
return offset + 1 | ||
return offset | ||
} | ||
} | ||
|
||
|
@@ -176,6 +179,7 @@ function createCalculateRange<T>(type: 'horizontal' | 'vertical', overscan: numb | |
? source.value.length | ||
: to, | ||
} | ||
|
||
currentList.value = source.value | ||
.slice(state.value.start, state.value.end) | ||
.map((ele, index) => ({ | ||
|
@@ -194,7 +198,7 @@ function createGetDistance<T>(itemSize: UseVirtualListItemSize, source: UseVirtu | |
} | ||
|
||
const size = source.value | ||
.slice(0, index) | ||
.slice(0, index + 1) | ||
.reduce((sum, _, i) => sum + itemSize(i), 0) | ||
|
||
return size | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Height naming was from when list was vertical only. Size is more appropriate, IMO