From 94c470e469800366234fa2e60404a1cf3a2b63b4 Mon Sep 17 00:00:00 2001 From: Petyo Ivanov Date: Tue, 17 Aug 2021 21:03:23 +0300 Subject: [PATCH] fix(): use getBoundingClientRect consistently --- src/Grid.tsx | 10 ++-------- src/List.tsx | 8 ++++---- src/components.tsx | 2 +- src/hooks/useScrollTop.ts | 7 ++++--- src/sizeSystem.ts | 3 ++- src/utils/correctItemSize.ts | 3 +++ test/List.test.tsx | 10 +++++----- 7 files changed, 21 insertions(+), 22 deletions(-) create mode 100644 src/utils/correctItemSize.ts diff --git a/src/Grid.tsx b/src/Grid.tsx index 93e7436ae..b1227a5fc 100644 --- a/src/Grid.tsx +++ b/src/Grid.tsx @@ -102,10 +102,7 @@ const GridItems: FC = React.memo(function GridItems() { const listRef = useSize((el) => { const firstItem = el.firstChild as HTMLElement if (firstItem) { - itemDimensions({ - width: firstItem.offsetWidth, - height: firstItem.offsetHeight, - }) + itemDimensions(firstItem.getBoundingClientRect()) } }) @@ -125,10 +122,7 @@ const Viewport: FC = ({ children }) => { const viewportDimensions = usePublisher('viewportDimensions') const viewportRef = useSize((el) => { - viewportDimensions({ - width: el.offsetWidth, - height: el.offsetHeight, - }) + viewportDimensions(el.getBoundingClientRect()) }) return ( diff --git a/src/List.tsx b/src/List.tsx index 4f76ee7fe..396d5bab4 100644 --- a/src/List.tsx +++ b/src/List.tsx @@ -5,7 +5,6 @@ import { getValue, map, pipe, - prop, publish, statefulStream, stream, @@ -28,6 +27,7 @@ import { Components, ComputeItemKey, GroupContent, GroupItemContent, ItemContent import { listSystem } from './listSystem' import { positionStickyCssValue } from './utils/positionStickyCssValue' import useWindowViewportRectRef from './hooks/useWindowViewportRect' +import { correctItemSize } from './utils/correctItemSize' export function identity(value: T) { return value @@ -268,7 +268,7 @@ const Header: FC = React.memo(function VirtuosoHeader() { const Header = useEmitterValue('HeaderComponent') const headerHeight = usePublisher('headerHeight') const headerFooterTag = useEmitterValue('headerFooterTag') - const ref = useSize((el) => headerHeight(el.offsetHeight)) + const ref = useSize((el) => headerHeight(correctItemSize(el, 'height'))) return Header ? createElement(headerFooterTag, { ref }, createElement(Header)) : null }) @@ -276,7 +276,7 @@ const Footer: FC = React.memo(function VirtuosoFooter() { const Footer = useEmitterValue('FooterComponent') const footerHeight = usePublisher('footerHeight') const headerFooterTag = useEmitterValue('headerFooterTag') - const ref = useSize((el) => footerHeight(el.offsetHeight)) + const ref = useSize((el) => footerHeight(correctItemSize(el, 'height'))) return Footer ? createElement(headerFooterTag, { ref }, createElement(Footer)) : null }) @@ -351,7 +351,7 @@ export function buildWindowScroller({ usePublisher, useEmitter, useEmitterValue const Viewport: FC = ({ children }) => { const viewportHeight = usePublisher('viewportHeight') - const viewportRef = useSize(compose(viewportHeight, prop('offsetHeight'))) + const viewportRef = useSize(compose(viewportHeight, (el) => correctItemSize(el, 'height'))) return (
diff --git a/src/components.tsx b/src/components.tsx index 0ee7523a5..7fe69c7f6 100644 --- a/src/components.tsx +++ b/src/components.tsx @@ -90,7 +90,7 @@ export interface VirtuosoProps extends Omit number diff --git a/src/hooks/useScrollTop.ts b/src/hooks/useScrollTop.ts index 9521cb5ba..f4611a396 100644 --- a/src/hooks/useScrollTop.ts +++ b/src/hooks/useScrollTop.ts @@ -1,5 +1,6 @@ import { useRef, useCallback, useEffect } from 'react' import * as u from '@virtuoso.dev/urx' +import { correctItemSize } from '../utils/correctItemSize' export type ScrollerRef = Window | HTMLElement | null @@ -21,7 +22,7 @@ export default function useScrollTop( scrollTopCallback(Math.max(scrollTop, 0)) if (scrollTopTarget.current !== null) { - if (scrollTop === scrollTopTarget.current || scrollTop <= 0 || scrollTop === el.scrollHeight - el.offsetHeight) { + if (scrollTop === scrollTopTarget.current || scrollTop <= 0 || scrollTop === el.scrollHeight - correctItemSize(el, 'height')) { scrollTopTarget.current = null smoothScrollTargetReached(true) if (timeoutRef.current) { @@ -61,12 +62,12 @@ export default function useScrollTop( if (scrollerElement === window) { // this is not a mistake - scrollHeight = Math.max(document.documentElement.offsetHeight, document.documentElement.scrollHeight) + scrollHeight = Math.max(correctItemSize(document.documentElement, 'height'), document.documentElement.scrollHeight) offsetHeight = window.innerHeight scrollTop = document.documentElement.scrollTop } else { scrollHeight = (scrollerElement as HTMLElement).scrollHeight - offsetHeight = (scrollerElement as HTMLElement).offsetHeight + offsetHeight = correctItemSize(scrollerElement as HTMLElement, 'height') scrollTop = (scrollerElement as HTMLElement).scrollTop } diff --git a/src/sizeSystem.ts b/src/sizeSystem.ts index 6d26f44a6..9d848cb17 100644 --- a/src/sizeSystem.ts +++ b/src/sizeSystem.ts @@ -1,6 +1,7 @@ import * as u from '@virtuoso.dev/urx' import { arrayToRanges, AANode, empty, findMaxKeyValue, insert, newTree, Range, rangesWithin, remove, walk } from './AATree' import * as arrayBinarySearch from './utils/binaryArraySearch' +import { correctItemSize } from './utils/correctItemSize' export interface SizeRange { startIndex: number @@ -245,7 +246,7 @@ export const sizeSystem = u.system( const groupIndices = u.statefulStream([] as number[]) const fixedItemSize = u.statefulStream(undefined) const defaultItemSize = u.statefulStream(undefined) - const itemSize = u.statefulStream((el, field) => el.getBoundingClientRect()[SIZE_MAP[field]]) + const itemSize = u.statefulStream((el, field) => correctItemSize(el, SIZE_MAP[field])) const data = u.statefulStream(undefined) const initial = initialSizeState() diff --git a/src/utils/correctItemSize.ts b/src/utils/correctItemSize.ts new file mode 100644 index 000000000..fed2bf229 --- /dev/null +++ b/src/utils/correctItemSize.ts @@ -0,0 +1,3 @@ +export function correctItemSize(el: HTMLElement, dimension: 'height' | 'width') { + return el.getBoundingClientRect()[dimension] +} diff --git a/test/List.test.tsx b/test/List.test.tsx index 4583b1b65..6166ea68c 100644 --- a/test/List.test.tsx +++ b/test/List.test.tsx @@ -34,7 +34,7 @@ describe('List', () => { act(() => { scroller.triggerScroll(0) - viewport.triggerResize({ offsetHeight: 700 }) + viewport.triggerResize({ getBoundingClientRect: () => ({ height: 700 }) }) }) expect(listParent.children).toHaveLength(1) @@ -57,7 +57,7 @@ describe('List', () => { act(() => { scroller.triggerScroll(0) - viewport.triggerResize({ offsetHeight: 700 }) + viewport.triggerResize({ getBoundingClientRect: () => ({ height: 700 }) }) listParent.triggerChangedChildSizes([{ startIndex: 0, endIndex: 0, size: 30 }]) }) }) @@ -99,7 +99,7 @@ describe('List', () => { act(() => { scroller.triggerScroll(0) - viewport.triggerResize({ offsetHeight: 100 }) + viewport.triggerResize({ getBoundingClientRect: () => ({ height: 700 }) }) listParent.triggerChangedChildSizes([{ startIndex: 0, endIndex: 0, size: 10 }]) }) }) @@ -131,7 +131,7 @@ describe('List', () => { act(() => { scroller.triggerScroll(0) - viewport.triggerResize({ offsetHeight: 100 }) + viewport.triggerResize({ getBoundingClientRect: () => ({ height: 100 }) }) listParent.triggerChangedChildSizes([{ startIndex: 0, endIndex: 0, size: 10 }]) container.querySelector('button').dispatchEvent(new MouseEvent('click', { bubbles: true })) }) @@ -166,7 +166,7 @@ describe('List', () => { act(() => { scroller.triggerScroll(0) - viewport.triggerResize({ offsetHeight: 100 }) + viewport.triggerResize({ getBoundingClientRect: () => ({ height: 100 }) }) listParent.triggerChangedChildSizes([{ startIndex: 0, endIndex: 0, size: 10 }]) container.querySelector('button').dispatchEvent(new MouseEvent('click', { bubbles: true })) })