Skip to content
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

Observe element for size changes with ResizeObserver #405

Merged
merged 1 commit into from
Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions docs/api/virtual-item.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ title: VirtualItem
The `VirtualItem` object represents a single item returned by the virtualizer. It contains information you need to render the item in the cooredinate space within your virtualizer's scrollElement and other helpful properties/functions.

```tsx
export interface VirtualItem<TItemElement = unknown> {
export interface VirtualItem {
key: string | number
index: number
start: number
end: number
size: number
measureElement: (el: TItemElement | null) => void
}
```

Expand Down Expand Up @@ -56,11 +55,3 @@ size: number
```

The size of the item. This is usually mapped to a css property like `width/height`. Before an item is measured vit the `VirtualItem.measureElement` method, this will be the estimated size returned from your `estimateSize` virtualizer option. After an item is measured (if you choose to measure it at all), this value will be the number returned by your `measureElement` virtualizer option (which by default is configured to measure elements with `getBoundingClientRect()`).

### `measureElement`

```tsx
measureElement: (el: TItemElement | null) => void
```

Measures the element using your configured `measureElement` virtualizer option. You are repsonsible for calling this in your virtualizer markup when the component is rendered (eg. using something like React's ref callback prop). By default the `measureElement` virtualizer option is configured to measure elements with `getBoundingClientRect()`.
23 changes: 21 additions & 2 deletions docs/api/virtualizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ The current scrollElement for the virtualizer. This property is updated via your
### `getVirtualItems`

```tsx
type getVirtualItems = () => VirtualItem<TItemElement>[]
type getVirtualItems = () => VirtualItem[]
```

Returns the virtual items for the current state of the virtualizer.
Expand Down Expand Up @@ -263,4 +263,23 @@ Returns the total size in pixels for the virtualized items. This measurement wil
measure: () => void
```

Recomputes the virtualizer and resets any item measurements.
Resets any prev item measurements.

### `measureElement`

```tsx
measureElement: (el: TItemElement | null) => void
```

Measures the element using your configured `measureElement` virtualizer option. You are repsonsible for calling this in your virtualizer markup when the component is rendered (eg. using something like React's ref callback prop) also adding `data-index`

```tsx
<div
key={virtualRow.key}
data-index={virtualRow.index}
ref={virtualizer.measureElement}
style={...}
>...</div>
```

By default the `measureElement` virtualizer option is configured to measure elements with `getBoundingClientRect()`.
29 changes: 20 additions & 9 deletions examples/react/dynamic/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ function RowVirtualizerDynamic({ rows }) {
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
<div
key={virtualRow.index}
ref={virtualRow.measureElement}
key={virtualRow.key}
data-index={virtualRow.index}
ref={rowVirtualizer.measureElement}
className={virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'}
style={{
position: 'absolute',
Expand All @@ -91,7 +92,7 @@ function RowVirtualizerDynamic({ rows }) {
}

function ColumnVirtualizerDynamic({ columns }) {
const parentRef = React.useRef()
const parentRef = React.useRef<HTMLDivElement | null>(null)

const columnVirtualizer = useVirtualizer({
horizontal: true,
Expand Down Expand Up @@ -121,7 +122,8 @@ function ColumnVirtualizerDynamic({ columns }) {
{columnVirtualizer.getVirtualItems().map((virtualColumn) => (
<div
key={virtualColumn.key}
ref={virtualColumn.measureElement}
data-index={virtualColumn.index}
ref={columnVirtualizer.measureElement}
className={
virtualColumn.index % 2 ? 'ListItemOdd' : 'ListItemEven'
}
Expand All @@ -145,25 +147,35 @@ function ColumnVirtualizerDynamic({ columns }) {
}

function GridVirtualizerDynamic({ rows, columns }) {
const parentRef = React.useRef()
const parentRef = React.useRef<HTMLDivElement | null>(null)

const rowVirtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 125,
indexAttribute: 'data-row-index',
})

const columnVirtualizer = useVirtualizer({
horizontal: true,
count: columns.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 125,
indexAttribute: 'data-col-index',
})

const [show, setShow] = React.useState(true)

const halfWay = Math.floor(rows.length / 2)

const ref = React.useMemo(
() => (node: HTMLDivElement | null) => {
rowVirtualizer.measureElement(node)
columnVirtualizer.measureElement(node)
},
[rowVirtualizer.measureElement, columnVirtualizer.measureElement],
)

return (
<>
<div className="py-2 flex gap-2">
Expand Down Expand Up @@ -197,10 +209,9 @@ function GridVirtualizerDynamic({ rows, columns }) {
{columnVirtualizer.getVirtualItems().map((virtualColumn) => (
<div
key={virtualColumn.key}
ref={(el) => {
virtualRow.measureElement(el)
virtualColumn.measureElement(el)
}}
data-col-index={virtualColumn.index}
data-row-index={virtualRow.index}
ref={ref}
className={
virtualColumn.index % 2
? virtualRow.index % 2 === 0
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"prettier": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"resize-observer-polyfill": "^1.5.1",
"rollup": "^2.70.2",
"rollup-plugin-size": "^0.2.2",
"rollup-plugin-svelte": "^7.1.0",
Expand Down
7 changes: 5 additions & 2 deletions packages/react-virtual/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,17 @@ function List({
<div
data-testid={`item-${virtualRow.key}`}
key={virtualRow.key}
ref={(el) => (dynamic ? virtualRow.measureElement(el) : undefined)}
data-index={virtualRow.index}
ref={(el) =>
dynamic ? rowVirtualizer.measureElement(el) : undefined
}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualRow.start}px)`,
height: virtualRow.size,
height: itemSize,
}}
>
Row {virtualRow.index}
Expand Down
3 changes: 3 additions & 0 deletions packages/react-virtual/__tests__/jest.setup.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
import '@testing-library/jest-dom'
import ResizeObserver from 'resize-observer-polyfill'

global.ResizeObserver = ResizeObserver
6 changes: 3 additions & 3 deletions packages/react-virtual/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export * from '@tanstack/virtual-core'
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect

function useVirtualizerBase<TScrollElement, TItemElement = unknown>(
function useVirtualizerBase<TScrollElement, TItemElement extends Element>(
options: VirtualizerOptions<TScrollElement, TItemElement>,
): Virtualizer<TScrollElement, TItemElement> {
const rerender = React.useReducer(() => ({}), {})[1]
Expand Down Expand Up @@ -47,7 +47,7 @@ function useVirtualizerBase<TScrollElement, TItemElement = unknown>(
return instance
}

export function useVirtualizer<TScrollElement, TItemElement = unknown>(
export function useVirtualizer<TScrollElement, TItemElement extends Element>(
options: PartialKeys<
VirtualizerOptions<TScrollElement, TItemElement>,
'observeElementRect' | 'observeElementOffset' | 'scrollToFn'
Expand All @@ -61,7 +61,7 @@ export function useVirtualizer<TScrollElement, TItemElement = unknown>(
})
}

export function useWindowVirtualizer<TItemElement = unknown>(
export function useWindowVirtualizer<TItemElement extends Element>(
options: PartialKeys<
VirtualizerOptions<Window, TItemElement>,
| 'getScrollElement'
Expand Down
6 changes: 3 additions & 3 deletions packages/solid-virtual/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export * from '@tanstack/virtual-core';
import { createSignal, onMount, onCleanup, createComputed, mergeProps } from 'solid-js';
import { createStore, reconcile } from 'solid-js/store';

function createVirtualizerBase<TScrollElement, TItemElement = unknown>(
function createVirtualizerBase<TScrollElement, TItemElement extends Element>(
options: VirtualizerOptions<TScrollElement, TItemElement>
): Virtualizer<TScrollElement, TItemElement> {
const resolvedOptions: VirtualizerOptions<TScrollElement, TItemElement> = mergeProps(options);
Expand Down Expand Up @@ -71,7 +71,7 @@ function createVirtualizerBase<TScrollElement, TItemElement = unknown>(
return virtualizer;
}

export function createVirtualizer<TScrollElement, TItemElement = unknown>(
export function createVirtualizer<TScrollElement, TItemElement extends Element>(
options: PartialKeys<
VirtualizerOptions<TScrollElement, TItemElement>,
'observeElementRect' | 'observeElementOffset' | 'scrollToFn'
Expand All @@ -85,7 +85,7 @@ export function createVirtualizer<TScrollElement, TItemElement = unknown>(
}, options));
}

export function createWindowVirtualizer<TItemElement = unknown>(
export function createWindowVirtualizer<TItemElement extends Element>(
options: PartialKeys<
VirtualizerOptions<Window, TItemElement>,
| 'getScrollElement'
Expand Down
6 changes: 3 additions & 3 deletions packages/svelte-virtual/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export * from '@tanstack/virtual-core'

import { readable, Readable } from 'svelte/store'

function createVirtualizerBase<TScrollElement, TItemElement = unknown>(
function createVirtualizerBase<TScrollElement, TItemElement extends Element>(
options: VirtualizerOptions<TScrollElement, TItemElement>,
): Readable<Virtualizer<TScrollElement, TItemElement>> {
const virtualizer = new Virtualizer(options)
Expand All @@ -33,7 +33,7 @@ function createVirtualizerBase<TScrollElement, TItemElement = unknown>(
})
}

export function createVirtualizer<TScrollElement, TItemElement = unknown>(
export function createVirtualizer<TScrollElement, TItemElement extends Element>(
options: PartialKeys<
VirtualizerOptions<TScrollElement, TItemElement>,
'observeElementRect' | 'observeElementOffset' | 'scrollToFn'
Expand All @@ -47,7 +47,7 @@ export function createVirtualizer<TScrollElement, TItemElement = unknown>(
})
}

export function createWindowVirtualizer<TItemElement = unknown>(
export function createWindowVirtualizer<TItemElement extends Element>(
options: PartialKeys<
VirtualizerOptions<Window, TItemElement>,
| 'getScrollElement'
Expand Down
Loading