From 19b427282b5713a44893a2fe892bb82af4c92f8a Mon Sep 17 00:00:00 2001 From: Amber Date: Wed, 5 Jun 2024 11:49:40 +0200 Subject: [PATCH] fix(virtual-core): add support to multi window (#738) --- packages/virtual-core/src/index.ts | 69 +++++++++++++++++++++--------- packages/virtual-core/src/utils.ts | 12 ++++-- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/packages/virtual-core/src/index.ts b/packages/virtual-core/src/index.ts index a8012a19..ddddf9b0 100644 --- a/packages/virtual-core/src/index.ts +++ b/packages/virtual-core/src/index.ts @@ -67,6 +67,10 @@ export const observeElementRect = ( if (!element) { return } + const targetWindow = instance.targetWindow + if (!targetWindow) { + return + } const handler = (rect: Rect) => { const { width, height } = rect @@ -75,11 +79,11 @@ export const observeElementRect = ( 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] @@ -134,13 +138,21 @@ export const observeElementOffset = ( 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'] @@ -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'] @@ -307,8 +327,9 @@ export class Virtualizer< private unsubs: (void | (() => void))[] = [] options!: Required> scrollElement: TScrollElement | null = null + targetWindow: (Window & typeof globalThis) | null = null isScrolling: boolean = false - private scrollToIndexTimeoutId: ReturnType | null = null + private scrollToIndexTimeoutId: number | null = null measurementsCache: VirtualItem[] = [] private itemSizeCache = new Map() private pendingMeasuredCacheIndexes: number[] = [] @@ -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 { @@ -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, @@ -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 } } @@ -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( diff --git a/packages/virtual-core/src/utils.ts b/packages/virtual-core/src/utils.ts index f48116dd..41ee42a6 100644 --- a/packages/virtual-core/src/utils.ts +++ b/packages/virtual-core/src/utils.ts @@ -78,10 +78,14 @@ export function notUndefined(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 +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) } }