Skip to content

Commit

Permalink
fix(virtual-core): add support to multi window (TanStack#738)
Browse files Browse the repository at this point in the history
  • Loading branch information
YuanboXue-Amber authored Jun 5, 2024
1 parent c3410c2 commit 19b4272
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 24 deletions.
69 changes: 49 additions & 20 deletions packages/virtual-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export const observeElementRect = <T extends Element>(
if (!element) {
return
}
const targetWindow = instance.targetWindow
if (!targetWindow) {
return
}

const handler = (rect: Rect) => {
const { width, height } = rect
Expand All @@ -75,11 +79,11 @@ export const observeElementRect = <T extends Element>(

handler(element.getBoundingClientRect())

if (typeof ResizeObserver === 'undefined') {
if (!targetWindow.ResizeObserver) {
return () => {}
}

const observer = new ResizeObserver((entries) => {
const observer = new targetWindow.ResizeObserver((entries) => {
const entry = entries[0]
if (entry?.borderBoxSize) {
const box = entry.borderBoxSize[0]
Expand Down Expand Up @@ -134,13 +138,21 @@ export const observeElementOffset = <T extends Element>(
if (!element) {
return
}
const targetWindow = instance.targetWindow
if (!targetWindow) {
return
}

let offset = 0
const fallback = supportsScrollend
? () => undefined
: debounce(() => {
cb(offset, false)
}, instance.options.isScrollingResetDelay)
: debounce(
targetWindow,
() => {
cb(offset, false)
},
instance.options.isScrollingResetDelay,
)

const createHandler = (isScrolling: boolean) => () => {
offset = element[instance.options.horizontal ? 'scrollLeft' : 'scrollTop']
Expand Down Expand Up @@ -168,13 +180,21 @@ export const observeWindowOffset = (
if (!element) {
return
}
const targetWindow = instance.targetWindow
if (!targetWindow) {
return
}

let offset = 0
const fallback = supportsScrollend
? () => undefined
: debounce(() => {
cb(offset, false)
}, instance.options.isScrollingResetDelay)
: debounce(
targetWindow,
() => {
cb(offset, false)
},
instance.options.isScrollingResetDelay,
)

const createHandler = (isScrolling: boolean) => () => {
offset = element[instance.options.horizontal ? 'scrollX' : 'scrollY']
Expand Down Expand Up @@ -307,8 +327,9 @@ export class Virtualizer<
private unsubs: (void | (() => void))[] = []
options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>
scrollElement: TScrollElement | null = null
targetWindow: (Window & typeof globalThis) | null = null
isScrolling: boolean = false
private scrollToIndexTimeoutId: ReturnType<typeof setTimeout> | null = null
private scrollToIndexTimeoutId: number | null = null
measurementsCache: VirtualItem[] = []
private itemSizeCache = new Map<Key, number>()
private pendingMeasuredCacheIndexes: number[] = []
Expand All @@ -330,15 +351,17 @@ export class Virtualizer<
const get = () => {
if (_ro) {
return _ro
} else if (typeof ResizeObserver !== 'undefined') {
return (_ro = new ResizeObserver((entries) => {
entries.forEach((entry) => {
this._measureElement(entry.target as TItemElement, entry)
})
}))
} else {
}

if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
return null
}

return (_ro = new this.targetWindow.ResizeObserver((entries) => {
entries.forEach((entry) => {
this._measureElement(entry.target as TItemElement, entry)
})
}))
}

return {
Expand Down Expand Up @@ -432,6 +455,12 @@ export class Virtualizer<

this.scrollElement = scrollElement

if (this.scrollElement && 'ownerDocument' in this.scrollElement) {
this.targetWindow = this.scrollElement.ownerDocument.defaultView
} else {
this.targetWindow = this.scrollElement?.window ?? null
}

this._scrollToOffset(this.scrollOffset, {
adjustments: undefined,
behavior: undefined,
Expand Down Expand Up @@ -807,8 +836,8 @@ export class Virtualizer<
private isDynamicMode = () => this.measureElementCache.size > 0

private cancelScrollToIndex = () => {
if (this.scrollToIndexTimeoutId !== null) {
clearTimeout(this.scrollToIndexTimeoutId)
if (this.scrollToIndexTimeoutId !== null && this.targetWindow) {
this.targetWindow.clearTimeout(this.scrollToIndexTimeoutId)
this.scrollToIndexTimeoutId = null
}
}
Expand Down Expand Up @@ -849,8 +878,8 @@ export class Virtualizer<

this._scrollToOffset(toOffset, { adjustments: undefined, behavior })

if (behavior !== 'smooth' && this.isDynamicMode()) {
this.scrollToIndexTimeoutId = setTimeout(() => {
if (behavior !== 'smooth' && this.isDynamicMode() && this.targetWindow) {
this.scrollToIndexTimeoutId = this.targetWindow.setTimeout(() => {
this.scrollToIndexTimeoutId = null

const elementInDOM = this.measureElementCache.has(
Expand Down
12 changes: 8 additions & 4 deletions packages/virtual-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,14 @@ export function notUndefined<T>(value: T | undefined, msg?: string): T {

export const approxEqual = (a: number, b: number) => Math.abs(a - b) < 1

export const debounce = (fn: Function, ms: number) => {
let timeoutId: ReturnType<typeof setTimeout>
export const debounce = (
targetWindow: Window & typeof globalThis,
fn: Function,
ms: number,
) => {
let timeoutId: number
return function (this: any, ...args: any[]) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn.apply(this, args), ms)
targetWindow.clearTimeout(timeoutId)
timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms)
}
}

0 comments on commit 19b4272

Please sign in to comment.