Skip to content

Commit

Permalink
fix: upward scroll fix works in window scroll mode
Browse files Browse the repository at this point in the history
  • Loading branch information
petyosi committed Jan 2, 2022
1 parent de2bed3 commit 628eef6
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 32 deletions.
81 changes: 80 additions & 1 deletion examples/window-scroller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,93 @@ export default function App() {
const ref = React.useRef<VirtuosoHandle>(null)
return (
<div style={{ padding: '100px' }}>
<ol>
<li>Scroll down manually, new items should appear.</li>
<li>
Click on foo, then scroll upwards. the paragraphs should stay correctly, see{' '}
<a href="https://github.com/petyosi/react-virtuoso/issues/524">For details</a>
</li>
</ol>
<button onClick={() => ref.current!.scrollToIndex(20)}>Scroll</button>
<a href="#foo">Go to foo</a>
<Virtuoso
ref={ref}
totalCount={100}
itemContent={(index) => <div style={{ height: 30 }}>Item {index}</div>}
itemContent={(index) => <div style={{ height: index % 2 ? 50 : 20 }}>Item {index}</div>}
style={{ border: '1px solid red', height: '300px' }}
useWindowScroll={true}
/>
<div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris rhoncus magna nec interdum consectetur. Suspendisse consectetur
quis tortor at scelerisque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Curabitur
ultrices imperdiet quam vitae gravida. Aenean aliquam porttitor arcu. Ut volutpat velit at risus ultrices vulputate. Nulla semper
tortor ac purus rhoncus, ut fermentum orci feugiat. Quisque venenatis suscipit consectetur. Cras sed risus enim. Sed non ex
ultricies, malesuada neque quis, volutpat massa. Sed sit amet orci non ex feugiat porttitor vel quis magna. Nullam accumsan justo
nec ipsum maximus placerat. Integer et dui ut metus mattis vestibulum. Aliquam pretium mollis magna, nec rhoncus mi tristique non.
Sed vitae ligula augue. Donec rutrum, mi efficitur maximus volutpat, diam neque condimentum risus, vel fringilla tortor lorem vel
risus. Cras venenatis ipsum ac suscipit faucibus. Donec at arcu nec leo sagittis vulputate vitae sed massa. Nam ullamcorper
hendrerit nunc eu vestibulum. Sed et feugiat sem. Sed iaculis, augue non porta cursus, tortor ante venenatis ipsum, nec sodales
leo lectus et nunc. Nunc pharetra ipsum sit amet felis viverra, et egestas felis suscipit. Etiam bibendum lacinia nisi semper
finibus. Aliquam feugiat ultrices est eu viverra. Quisque luctus aliquet lacus. In hac habitasse platea dictumst. Fusce volutpat
tincidunt finibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut ac mattis nunc.
Integer lorem dui, facilisis eget lacus id, tempus euismod tortor. Duis facilisis eu quam ac bibendum. Morbi pulvinar feugiat
tortor id imperdiet. Fusce nec odio ut lorem accumsan dignissim. Fusce vestibulum condimentum eros, in ornare augue sodales nec.
Pellentesque ut lorem nibh. Nunc suscipit interdum purus quis mattis. Nulla mollis ac diam id sagittis. Maecenas non nibh non arcu
aliquet ultrices eget et diam. Quisque interdum nulla sed arcu eleifend posuere. Donec hendrerit tincidunt placerat. Maecenas
pulvinar ligula eu scelerisque maximus. Nullam a magna ex. Aliquam elementum augue id malesuada scelerisque. Curabitur quis lectus
augue. Morbi id blandit nunc. Donec tellus justo, volutpat ac enim non, tincidunt porttitor dui. Nam ac sodales purus. Sed rhoncus
auctor urna, non consectetur lorem condimentum sed. Suspendisse vel posuere erat. Maecenas viverra iaculis commodo. Maecenas et
tellus quis sem congue consequat. Sed eget feugiat eros. Suspendisse viverra augue et risus aliquam feugiat. Quisque consectetur
vel dolor quis auctor. Pellentesque commodo et nunc eu elementum. Nullam vitae vulputate augue.
</p>
<p id="foo">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris rhoncus magna nec interdum consectetur. Suspendisse consectetur
quis tortor at scelerisque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Curabitur
ultrices imperdiet quam vitae gravida. Aenean aliquam porttitor arcu. Ut volutpat velit at risus ultrices vulputate. Nulla semper
tortor ac purus rhoncus, ut fermentum orci feugiat. Quisque venenatis suscipit consectetur. Cras sed risus enim. Sed non ex
ultricies, malesuada neque quis, volutpat massa. Sed sit amet orci non ex feugiat porttitor vel quis magna. Nullam accumsan justo
nec ipsum maximus placerat. Integer et dui ut metus mattis vestibulum. Aliquam pretium mollis magna, nec rhoncus mi tristique non.
Sed vitae ligula augue. Donec rutrum, mi efficitur maximus volutpat, diam neque condimentum risus, vel fringilla tortor lorem vel
risus. Cras venenatis ipsum ac suscipit faucibus. Donec at arcu nec leo sagittis vulputate vitae sed massa. Nam ullamcorper
hendrerit nunc eu vestibulum. Sed et feugiat sem. Sed iaculis, augue non porta cursus, tortor ante venenatis ipsum, nec sodales
leo lectus et nunc. Nunc pharetra ipsum sit amet felis viverra, et egestas felis suscipit. Etiam bibendum lacinia nisi semper
finibus. Aliquam feugiat ultrices est eu viverra. Quisque luctus aliquet lacus. In hac habitasse platea dictumst. Fusce volutpat
tincidunt finibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut ac mattis nunc.
Integer lorem dui, facilisis eget lacus id, tempus euismod tortor. Duis facilisis eu quam ac bibendum. Morbi pulvinar feugiat
tortor id imperdiet. Fusce nec odio ut lorem accumsan dignissim. Fusce vestibulum condimentum eros, in ornare augue sodales nec.
Pellentesque ut lorem nibh. Nunc suscipit interdum purus quis mattis. Nulla mollis ac diam id sagittis. Maecenas non nibh non arcu
aliquet ultrices eget et diam. Quisque interdum nulla sed arcu eleifend posuere. Donec hendrerit tincidunt placerat. Maecenas
pulvinar ligula eu scelerisque maximus. Nullam a magna ex. Aliquam elementum augue id malesuada scelerisque. Curabitur quis lectus
augue. Morbi id blandit nunc. Donec tellus justo, volutpat ac enim non, tincidunt porttitor dui. Nam ac sodales purus. Sed rhoncus
auctor urna, non consectetur lorem condimentum sed. Suspendisse vel posuere erat. Maecenas viverra iaculis commodo. Maecenas et
tellus quis sem congue consequat. Sed eget feugiat eros. Suspendisse viverra augue et risus aliquam feugiat. Quisque consectetur
vel dolor quis auctor. Pellentesque commodo et nunc eu elementum. Nullam vitae vulputate augue.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris rhoncus magna nec interdum consectetur. Suspendisse consectetur
quis tortor at scelerisque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Curabitur
ultrices imperdiet quam vitae gravida. Aenean aliquam porttitor arcu. Ut volutpat velit at risus ultrices vulputate. Nulla semper
tortor ac purus rhoncus, ut fermentum orci feugiat. Quisque venenatis suscipit consectetur. Cras sed risus enim. Sed non ex
ultricies, malesuada neque quis, volutpat massa. Sed sit amet orci non ex feugiat porttitor vel quis magna. Nullam accumsan justo
nec ipsum maximus placerat. Integer et dui ut metus mattis vestibulum. Aliquam pretium mollis magna, nec rhoncus mi tristique non.
Sed vitae ligula augue. Donec rutrum, mi efficitur maximus volutpat, diam neque condimentum risus, vel fringilla tortor lorem vel
risus. Cras venenatis ipsum ac suscipit faucibus. Donec at arcu nec leo sagittis vulputate vitae sed massa. Nam ullamcorper
hendrerit nunc eu vestibulum. Sed et feugiat sem. Sed iaculis, augue non porta cursus, tortor ante venenatis ipsum, nec sodales
leo lectus et nunc. Nunc pharetra ipsum sit amet felis viverra, et egestas felis suscipit. Etiam bibendum lacinia nisi semper
finibus. Aliquam feugiat ultrices est eu viverra. Quisque luctus aliquet lacus. In hac habitasse platea dictumst. Fusce volutpat
tincidunt finibus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut ac mattis nunc.
Integer lorem dui, facilisis eget lacus id, tempus euismod tortor. Duis facilisis eu quam ac bibendum. Morbi pulvinar feugiat
tortor id imperdiet. Fusce nec odio ut lorem accumsan dignissim. Fusce vestibulum condimentum eros, in ornare augue sodales nec.
Pellentesque ut lorem nibh. Nunc suscipit interdum purus quis mattis. Nulla mollis ac diam id sagittis. Maecenas non nibh non arcu
aliquet ultrices eget et diam. Quisque interdum nulla sed arcu eleifend posuere. Donec hendrerit tincidunt placerat. Maecenas
pulvinar ligula eu scelerisque maximus. Nullam a magna ex. Aliquam elementum augue id malesuada scelerisque. Curabitur quis lectus
augue. Morbi id blandit nunc. Donec tellus justo, volutpat ac enim non, tincidunt porttitor dui. Nam ac sodales purus. Sed rhoncus
auctor urna, non consectetur lorem condimentum sed. Suspendisse vel posuere erat. Maecenas viverra iaculis commodo. Maecenas et
tellus quis sem congue consequat. Sed eget feugiat eros. Suspendisse viverra augue et risus aliquam feugiat. Quisque consectetur
vel dolor quis auctor. Pellentesque commodo et nunc eu elementum. Nullam vitae vulputate augue.
</p>
</div>
</div>
)
}
5 changes: 2 additions & 3 deletions src/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,13 @@ export const Items = React.memo(function VirtuosoItems({ showTopList = false }:
const scrolledToInitialItem = useEmitterValue('scrolledToInitialItem')
const firstItemIndex = useEmitterValue('firstItemIndex')

// const calculatedHeight = listState.offsetBottom + listState.bottom
const containerStyle: CSSProperties = showTopList
? {}
: {
boxSizing: 'border-box',
paddingTop: listState.offsetTop + paddingTopAddition,
paddingBottom: listState.offsetBottom,
marginTop: deviation,
// height: calculatedHeight,
}

if (!showTopList && listState.items.length === 0 && EmptyPlaceholder && scrolledToInitialItem) {
Expand Down Expand Up @@ -330,6 +328,7 @@ export function buildWindowScroller({ usePublisher, useEmitter, useEmitterValue
const ScrollerComponent = useEmitterValue('ScrollerComponent')!
const smoothScrollTargetReached = usePublisher('smoothScrollTargetReached')
const totalListHeight = useEmitterValue('totalListHeight')
const deviation = useEmitterValue('deviation')
const { scrollerRef, scrollByCallback, scrollToCallback } = useScrollTop(
scrollContainerStateCallback,
smoothScrollTargetReached,
Expand All @@ -349,7 +348,7 @@ export function buildWindowScroller({ usePublisher, useEmitter, useEmitterValue
return createElement(
ScrollerComponent,
{
style: { position: 'relative', ...style, ...(totalListHeight !== 0 ? { height: totalListHeight } : {}) },
style: { position: 'relative', ...style, ...(totalListHeight !== 0 ? { height: totalListHeight + deviation } : {}) },
...props,
},
children
Expand Down
6 changes: 4 additions & 2 deletions src/gridSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const gridSystem = u.system(
stateFlags,
scrollSeek,
{ propsReady, didMount },
{ windowViewportRect, windowScrollTo, useWindowScroll, windowScrollTop },
{ windowViewportRect, windowScrollTo, useWindowScroll, windowScrollContainerState },
]) => {
const totalCount = u.statefulStream(0)
const initialItemCount = u.statefulStream(0)
Expand All @@ -75,6 +75,7 @@ export const gridSystem = u.system(
const itemDimensions = u.statefulStream<ElementDimensions>({ height: 0, width: 0 })
const scrollToIndex = u.stream<IndexLocation>()
const scrollHeight = u.stream<number>()
const deviation = u.statefulStream(0)

u.connect(
u.pipe(
Expand Down Expand Up @@ -257,7 +258,8 @@ export const gridSystem = u.system(
windowViewportRect,
windowScrollTo,
useWindowScroll,
windowScrollTop,
windowScrollContainerState,
deviation,
scrollContainerState,
initialItemCount,
...scrollSeek,
Expand Down
28 changes: 5 additions & 23 deletions src/windowScrollerSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,20 @@ import * as u from '@virtuoso.dev/urx'
import { domIOSystem } from './domIOSystem'
import { WindowViewportInfo } from './interfaces'

export const windowScrollerSystem = u.system(([{ scrollTop, scrollTo, scrollHeight }]) => {
export const windowScrollerSystem = u.system(([{ scrollTo, scrollContainerState }]) => {
const windowScrollContainerState = u.stream<[number, number]>()
const windowViewportRect = u.stream<WindowViewportInfo>()
const windowScrollTop = u.stream<number>()
const windowScrollTo = u.stream<ScrollToOptions>()
const useWindowScroll = u.statefulStream(false)

u.connect(
u.pipe(
windowScrollContainerState,
u.map(([scrollTop]) => scrollTop)
),
windowScrollTop
)

u.connect(
u.pipe(
windowScrollContainerState,
u.map(([, scrollHeight]) => scrollHeight)
),
scrollHeight
)

u.connect(
u.pipe(
u.combineLatest(windowScrollTop, windowViewportRect),
u.map(([windowScrollTop, { offsetTop }]) => {
return Math.max(0, windowScrollTop - offsetTop)
u.combineLatest(windowScrollContainerState, windowViewportRect),
u.map(([[windowScrollTop, scrollHeight], { offsetTop }]) => {
return [Math.max(0, windowScrollTop - offsetTop), scrollHeight]
})
),
scrollTop
scrollContainerState
)

u.connect(
Expand All @@ -55,7 +38,6 @@ export const windowScrollerSystem = u.system(([{ scrollTop, scrollTo, scrollHeig

// input
windowScrollContainerState,
windowScrollTop,
windowViewportRect,

// signals
Expand Down
6 changes: 3 additions & 3 deletions test/windowScrollerSystem.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { listSystem } from '../src/listSystem'

describe('window scroller system', () => {
it('offsets the window scroll top with the element offset top', () => {
const { windowViewportRect, scrollTop, windowScrollTop } = u.init(listSystem)
const { windowViewportRect, scrollTop, windowScrollContainerState } = u.init(listSystem)
const sub = jest.fn()
u.subscribe(scrollTop, sub)
u.publish(windowViewportRect, { offsetTop: 100, visibleHeight: 1000 })
u.publish(windowScrollTop, 0)
u.publish(windowScrollContainerState, [0, 1000])
expect(sub).toHaveBeenCalledWith(0)
u.publish(windowScrollTop, 200)
u.publish(windowScrollContainerState, [200, 1000])
expect(sub).toHaveBeenCalledWith(100)
})
it('offsets the scrollTo calls with offsetTop', () => {
Expand Down

0 comments on commit 628eef6

Please sign in to comment.