@tanstack/virtual-core 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["export type NoInfer<A extends any> = [A][A extends any ? 0 : never]\n\nexport type PartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\n\nexport function memo<TDeps extends readonly any[], TResult>(\n getDeps: () => [...TDeps],\n fn: (...args: NoInfer<[...TDeps]>) => TResult,\n opts: {\n key: false | string\n debug?: () => any\n onChange?: (result: TResult) => void\n initialDeps?: TDeps\n },\n) {\n let deps = opts.initialDeps ?? []\n let result: TResult | undefined\n\n return (): TResult => {\n let depTime: number\n if (opts.key && opts.debug?.()) depTime = Date.now()\n\n const newDeps = getDeps()\n\n const depsChanged =\n newDeps.length !== deps.length ||\n newDeps.some((dep: any, index: number) => deps[index] !== dep)\n\n if (!depsChanged) {\n return result!\n }\n\n deps = newDeps\n\n let resultTime: number\n if (opts.key && opts.debug?.()) resultTime = Date.now()\n\n result = fn(...newDeps)\n\n if (opts.key && opts.debug?.()) {\n const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100\n const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100\n const resultFpsPercentage = resultEndTime / 16\n\n const pad = (str: number | string, num: number) => {\n str = String(str)\n while (str.length < num) {\n str = ' ' + str\n }\n return str\n }\n\n console.info(\n `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,\n `\n font-size: .6rem;\n font-weight: bold;\n color: hsl(${Math.max(\n 0,\n Math.min(120 - 120 * resultFpsPercentage, 120),\n )}deg 100% 31%);`,\n opts?.key,\n )\n }\n\n opts?.onChange?.(result)\n\n return result!\n }\n}\n\nexport function notUndefined<T>(value: T | undefined, msg?: string): T {\n if (value === undefined) {\n throw new Error(`Unexpected undefined${msg ? `: ${msg}` : ''}`)\n } else {\n return value\n }\n}\n\nexport const approxEqual = (a: number, b: number) => Math.abs(a - b) < 1\n\nexport const debounce = (fn: Function, ms: number) => {\n let timeoutId: ReturnType<typeof setTimeout>\n return function (this: any, ...args: any[]) {\n clearTimeout(timeoutId)\n timeoutId = setTimeout(() => fn.apply(this, args), ms)\n }\n}\n"],"names":[],"mappings":";;AAIgB,SAAA,KACd,SACA,IACA,MAMA;AACI,MAAA,OAAO,KAAK,eAAe;AAC3B,MAAA;AAEJ,SAAO,MAAe;;AAChB,QAAA;AACA,QAAA,KAAK,SAAO,UAAK,UAAL;AAAgB,gBAAU,KAAK;AAE/C,UAAM,UAAU;AAEhB,UAAM,cACJ,QAAQ,WAAW,KAAK,UACxB,QAAQ,KAAK,CAAC,KAAU,UAAkB,KAAK,KAAK,MAAM,GAAG;AAE/D,QAAI,CAAC,aAAa;AACT,aAAA;AAAA,IACT;AAEO,WAAA;AAEH,QAAA;AACA,QAAA,KAAK,SAAO,UAAK,UAAL;AAAgB,mBAAa,KAAK;AAEzC,aAAA,GAAG,GAAG,OAAO;AAEtB,QAAI,KAAK,SAAO,UAAK,UAAL,gCAAgB;AACxB,YAAA,aAAa,KAAK,OAAO,KAAK,QAAQ,WAAY,GAAG,IAAI;AACzD,YAAA,gBAAgB,KAAK,OAAO,KAAK,QAAQ,cAAe,GAAG,IAAI;AACrE,YAAM,sBAAsB,gBAAgB;AAEtC,YAAA,MAAM,CAAC,KAAsB,QAAgB;AACjD,cAAM,OAAO,GAAG;AACT,eAAA,IAAI,SAAS,KAAK;AACvB,gBAAM,MAAM;AAAA,QACd;AACO,eAAA;AAAA,MAAA;AAGD,cAAA;AAAA,QACN,OAAO,IAAI,eAAe,CAAC,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC;AAAA,QACnD;AAAA;AAAA;AAAA,yBAGiB,KAAK;AAAA,UAChB;AAAA,UACA,KAAK,IAAI,MAAM,MAAM,qBAAqB,GAAG;AAAA,QAC9C,CAAA;AAAA,QACL,6BAAM;AAAA,MAAA;AAAA,IAEV;AAEA,uCAAM,aAAN,8BAAiB;AAEV,WAAA;AAAA,EAAA;AAEX;AAEgB,SAAA,aAAgB,OAAsB,KAAiB;AACrE,MAAI,UAAU,QAAW;AACjB,UAAA,IAAI,MAAM,uBAAuB,MAAM,KAAK,GAAG,KAAK,EAAE,EAAE;AAAA,EAAA,OACzD;AACE,WAAA;AAAA,EACT;AACF;AAEa,MAAA,cAAc,CAAC,GAAW,MAAc,KAAK,IAAI,IAAI,CAAC,IAAI;AAE1D,MAAA,WAAW,CAAC,IAAc,OAAe;AAChD,MAAA;AACJ,SAAO,YAAwB,MAAa;AAC1C,iBAAa,SAAS;AACtB,gBAAY,WAAW,MAAM,GAAG,MAAM,MAAM,IAAI,GAAG,EAAE;AAAA,EAAA;AAEzD;;;;;"}
1
+ {"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["export type NoInfer<A extends any> = [A][A extends any ? 0 : never]\n\nexport type PartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\n\nexport function memo<TDeps extends readonly any[], TResult>(\n getDeps: () => [...TDeps],\n fn: (...args: NoInfer<[...TDeps]>) => TResult,\n opts: {\n key: false | string\n debug?: () => any\n onChange?: (result: TResult) => void\n initialDeps?: TDeps\n },\n) {\n let deps = opts.initialDeps ?? []\n let result: TResult | undefined\n\n return (): TResult => {\n let depTime: number\n if (opts.key && opts.debug?.()) depTime = Date.now()\n\n const newDeps = getDeps()\n\n const depsChanged =\n newDeps.length !== deps.length ||\n newDeps.some((dep: any, index: number) => deps[index] !== dep)\n\n if (!depsChanged) {\n return result!\n }\n\n deps = newDeps\n\n let resultTime: number\n if (opts.key && opts.debug?.()) resultTime = Date.now()\n\n result = fn(...newDeps)\n\n if (opts.key && opts.debug?.()) {\n const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100\n const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100\n const resultFpsPercentage = resultEndTime / 16\n\n const pad = (str: number | string, num: number) => {\n str = String(str)\n while (str.length < num) {\n str = ' ' + str\n }\n return str\n }\n\n console.info(\n `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,\n `\n font-size: .6rem;\n font-weight: bold;\n color: hsl(${Math.max(\n 0,\n Math.min(120 - 120 * resultFpsPercentage, 120),\n )}deg 100% 31%);`,\n opts?.key,\n )\n }\n\n opts?.onChange?.(result)\n\n return result!\n }\n}\n\nexport function notUndefined<T>(value: T | undefined, msg?: string): T {\n if (value === undefined) {\n throw new Error(`Unexpected undefined${msg ? `: ${msg}` : ''}`)\n } else {\n return value\n }\n}\n\nexport const approxEqual = (a: number, b: number) => Math.abs(a - b) < 1\n\nexport const debounce = (\n targetWindow: Window & typeof globalThis,\n fn: Function,\n ms: number,\n) => {\n let timeoutId: number\n return function (this: any, ...args: any[]) {\n targetWindow.clearTimeout(timeoutId)\n timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms)\n }\n}\n"],"names":[],"mappings":";;AAIgB,SAAA,KACd,SACA,IACA,MAMA;AACI,MAAA,OAAO,KAAK,eAAe;AAC3B,MAAA;AAEJ,SAAO,MAAe;;AAChB,QAAA;AACA,QAAA,KAAK,SAAO,UAAK,UAAL;AAAgB,gBAAU,KAAK;AAE/C,UAAM,UAAU;AAEhB,UAAM,cACJ,QAAQ,WAAW,KAAK,UACxB,QAAQ,KAAK,CAAC,KAAU,UAAkB,KAAK,KAAK,MAAM,GAAG;AAE/D,QAAI,CAAC,aAAa;AACT,aAAA;AAAA,IACT;AAEO,WAAA;AAEH,QAAA;AACA,QAAA,KAAK,SAAO,UAAK,UAAL;AAAgB,mBAAa,KAAK;AAEzC,aAAA,GAAG,GAAG,OAAO;AAEtB,QAAI,KAAK,SAAO,UAAK,UAAL,gCAAgB;AACxB,YAAA,aAAa,KAAK,OAAO,KAAK,QAAQ,WAAY,GAAG,IAAI;AACzD,YAAA,gBAAgB,KAAK,OAAO,KAAK,QAAQ,cAAe,GAAG,IAAI;AACrE,YAAM,sBAAsB,gBAAgB;AAEtC,YAAA,MAAM,CAAC,KAAsB,QAAgB;AACjD,cAAM,OAAO,GAAG;AACT,eAAA,IAAI,SAAS,KAAK;AACvB,gBAAM,MAAM;AAAA,QACd;AACO,eAAA;AAAA,MAAA;AAGD,cAAA;AAAA,QACN,OAAO,IAAI,eAAe,CAAC,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC;AAAA,QACnD;AAAA;AAAA;AAAA,yBAGiB,KAAK;AAAA,UAChB;AAAA,UACA,KAAK,IAAI,MAAM,MAAM,qBAAqB,GAAG;AAAA,QAC9C,CAAA;AAAA,QACL,6BAAM;AAAA,MAAA;AAAA,IAEV;AAEA,uCAAM,aAAN,8BAAiB;AAEV,WAAA;AAAA,EAAA;AAEX;AAEgB,SAAA,aAAgB,OAAsB,KAAiB;AACrE,MAAI,UAAU,QAAW;AACjB,UAAA,IAAI,MAAM,uBAAuB,MAAM,KAAK,GAAG,KAAK,EAAE,EAAE;AAAA,EAAA,OACzD;AACE,WAAA;AAAA,EACT;AACF;AAEa,MAAA,cAAc,CAAC,GAAW,MAAc,KAAK,IAAI,IAAI,CAAC,IAAI;AAEhE,MAAM,WAAW,CACtB,cACA,IACA,OACG;AACC,MAAA;AACJ,SAAO,YAAwB,MAAa;AAC1C,iBAAa,aAAa,SAAS;AACvB,gBAAA,aAAa,WAAW,MAAM,GAAG,MAAM,MAAM,IAAI,GAAG,EAAE;AAAA,EAAA;AAEtE;;;;;"}
@@ -8,4 +8,4 @@ export declare function memo<TDeps extends readonly any[], TResult>(getDeps: ()
8
8
  }): () => TResult;
9
9
  export declare function notUndefined<T>(value: T | undefined, msg?: string): T;
10
10
  export declare const approxEqual: (a: number, b: number) => boolean;
11
- export declare const debounce: (fn: Function, ms: number) => (this: any, ...args: any[]) => void;
11
+ export declare const debounce: (targetWindow: Window & typeof globalThis, fn: Function, ms: number) => (this: any, ...args: any[]) => void;
@@ -71,18 +71,20 @@ export interface VirtualizerOptions<TScrollElement extends Element | Window, TIt
71
71
  initialMeasurementsCache?: VirtualItem[];
72
72
  lanes?: number;
73
73
  isScrollingResetDelay?: number;
74
+ enabled?: boolean;
74
75
  }
75
76
  export declare class Virtualizer<TScrollElement extends Element | Window, TItemElement extends Element> {
76
77
  private unsubs;
77
78
  options: Required<VirtualizerOptions<TScrollElement, TItemElement>>;
78
79
  scrollElement: TScrollElement | null;
80
+ targetWindow: (Window & typeof globalThis) | null;
79
81
  isScrolling: boolean;
80
82
  private scrollToIndexTimeoutId;
81
83
  measurementsCache: VirtualItem[];
82
84
  private itemSizeCache;
83
85
  private pendingMeasuredCacheIndexes;
84
- scrollRect: Rect;
85
- scrollOffset: number;
86
+ scrollRect: Rect | null;
87
+ scrollOffset: number | null;
86
88
  scrollDirection: ScrollDirection | null;
87
89
  private scrollAdjustments;
88
90
  shouldAdjustScrollPositionOnItemSizeChange: undefined | ((item: VirtualItem, delta: number, instance: Virtualizer<TScrollElement, TItemElement>) => boolean);
@@ -99,8 +101,9 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
99
101
  _didMount: () => () => void;
100
102
  _willUpdate: () => void;
101
103
  private getSize;
102
- private getMeasurementOptions;
104
+ private getScrollOffset;
103
105
  private getFurthestMeasurement;
106
+ private getMeasurementOptions;
104
107
  private getMeasurements;
105
108
  calculateRange: () => {
106
109
  startIndex: number;
@@ -112,9 +115,9 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
112
115
  resizeItem: (item: VirtualItem, size: number) => void;
113
116
  measureElement: (node: TItemElement | null) => void;
114
117
  getVirtualItems: () => VirtualItem[];
115
- getVirtualItemForOffset: (offset: number) => VirtualItem;
118
+ getVirtualItemForOffset: (offset: number) => VirtualItem | undefined;
116
119
  getOffsetForAlignment: (toOffset: number, align: ScrollAlignment) => number;
117
- getOffsetForIndex: (index: number, align?: ScrollAlignment) => readonly [number, "auto"] | readonly [number, "start" | "center" | "end"];
120
+ getOffsetForIndex: (index: number, align?: ScrollAlignment) => readonly [number, "auto"] | readonly [number, "start" | "center" | "end"] | undefined;
118
121
  private isDynamicMode;
119
122
  private cancelScrollToIndex;
120
123
  scrollToOffset: (toOffset: number, { align, behavior }?: ScrollToOffsetOptions) => void;
package/dist/esm/index.js CHANGED
@@ -14,16 +14,20 @@ const observeElementRect = (instance, cb) => {
14
14
  if (!element) {
15
15
  return;
16
16
  }
17
+ const targetWindow = instance.targetWindow;
18
+ if (!targetWindow) {
19
+ return;
20
+ }
17
21
  const handler = (rect) => {
18
22
  const { width, height } = rect;
19
23
  cb({ width: Math.round(width), height: Math.round(height) });
20
24
  };
21
25
  handler(element.getBoundingClientRect());
22
- if (typeof ResizeObserver === "undefined") {
26
+ if (!targetWindow.ResizeObserver) {
23
27
  return () => {
24
28
  };
25
29
  }
26
- const observer = new ResizeObserver((entries) => {
30
+ const observer = new targetWindow.ResizeObserver((entries) => {
27
31
  const entry = entries[0];
28
32
  if (entry == null ? void 0 : entry.borderBoxSize) {
29
33
  const box = entry.borderBoxSize[0];
@@ -62,10 +66,18 @@ const observeElementOffset = (instance, cb) => {
62
66
  if (!element) {
63
67
  return;
64
68
  }
69
+ const targetWindow = instance.targetWindow;
70
+ if (!targetWindow) {
71
+ return;
72
+ }
65
73
  let offset = 0;
66
- const fallback = supportsScrollend ? () => void 0 : debounce(() => {
67
- cb(offset, false);
68
- }, instance.options.isScrollingResetDelay);
74
+ const fallback = supportsScrollend ? () => void 0 : debounce(
75
+ targetWindow,
76
+ () => {
77
+ cb(offset, false);
78
+ },
79
+ instance.options.isScrollingResetDelay
80
+ );
69
81
  const createHandler = (isScrolling) => () => {
70
82
  offset = element[instance.options.horizontal ? "scrollLeft" : "scrollTop"];
71
83
  fallback();
@@ -86,10 +98,18 @@ const observeWindowOffset = (instance, cb) => {
86
98
  if (!element) {
87
99
  return;
88
100
  }
101
+ const targetWindow = instance.targetWindow;
102
+ if (!targetWindow) {
103
+ return;
104
+ }
89
105
  let offset = 0;
90
- const fallback = supportsScrollend ? () => void 0 : debounce(() => {
91
- cb(offset, false);
92
- }, instance.options.isScrollingResetDelay);
106
+ const fallback = supportsScrollend ? () => void 0 : debounce(
107
+ targetWindow,
108
+ () => {
109
+ cb(offset, false);
110
+ },
111
+ instance.options.isScrollingResetDelay
112
+ );
93
113
  const createHandler = (isScrolling) => () => {
94
114
  offset = element[instance.options.horizontal ? "scrollX" : "scrollY"];
95
115
  fallback();
@@ -145,11 +165,14 @@ class Virtualizer {
145
165
  constructor(opts) {
146
166
  this.unsubs = [];
147
167
  this.scrollElement = null;
168
+ this.targetWindow = null;
148
169
  this.isScrolling = false;
149
170
  this.scrollToIndexTimeoutId = null;
150
171
  this.measurementsCache = [];
151
172
  this.itemSizeCache = /* @__PURE__ */ new Map();
152
173
  this.pendingMeasuredCacheIndexes = [];
174
+ this.scrollRect = null;
175
+ this.scrollOffset = null;
153
176
  this.scrollDirection = null;
154
177
  this.scrollAdjustments = 0;
155
178
  this.measureElementCache = /* @__PURE__ */ new Map();
@@ -158,15 +181,15 @@ class Virtualizer {
158
181
  const get = () => {
159
182
  if (_ro) {
160
183
  return _ro;
161
- } else if (typeof ResizeObserver !== "undefined") {
162
- return _ro = new ResizeObserver((entries) => {
163
- entries.forEach((entry) => {
164
- this._measureElement(entry.target, entry);
165
- });
166
- });
167
- } else {
184
+ }
185
+ if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
168
186
  return null;
169
187
  }
188
+ return _ro = new this.targetWindow.ResizeObserver((entries) => {
189
+ entries.forEach((entry) => {
190
+ this._measureElement(entry.target, entry);
191
+ });
192
+ });
170
193
  };
171
194
  return {
172
195
  disconnect: () => {
@@ -210,6 +233,7 @@ class Virtualizer {
210
233
  initialMeasurementsCache: [],
211
234
  lanes: 1,
212
235
  isScrollingResetDelay: 150,
236
+ enabled: true,
213
237
  ...opts2
214
238
  };
215
239
  };
@@ -228,20 +252,31 @@ class Virtualizer {
228
252
  this.unsubs.filter(Boolean).forEach((d) => d());
229
253
  this.unsubs = [];
230
254
  this.scrollElement = null;
255
+ this.targetWindow = null;
256
+ this.observer.disconnect();
257
+ this.measureElementCache.clear();
231
258
  };
232
259
  this._didMount = () => {
233
- this.measureElementCache.forEach(this.observer.observe);
234
260
  return () => {
235
- this.observer.disconnect();
236
261
  this.cleanup();
237
262
  };
238
263
  };
239
264
  this._willUpdate = () => {
240
- const scrollElement = this.options.getScrollElement();
265
+ var _a;
266
+ const scrollElement = this.options.enabled ? this.options.getScrollElement() : null;
241
267
  if (this.scrollElement !== scrollElement) {
242
268
  this.cleanup();
269
+ if (!scrollElement) {
270
+ this.notify(false, false);
271
+ return;
272
+ }
243
273
  this.scrollElement = scrollElement;
244
- this._scrollToOffset(this.scrollOffset, {
274
+ if (this.scrollElement && "ownerDocument" in this.scrollElement) {
275
+ this.targetWindow = this.scrollElement.ownerDocument.defaultView;
276
+ } else {
277
+ this.targetWindow = ((_a = this.scrollElement) == null ? void 0 : _a.window) ?? null;
278
+ }
279
+ this._scrollToOffset(this.getScrollOffset(), {
245
280
  adjustments: void 0,
246
281
  behavior: void 0
247
282
  });
@@ -254,7 +289,7 @@ class Virtualizer {
254
289
  this.unsubs.push(
255
290
  this.options.observeElementOffset(this, (offset, isScrolling) => {
256
291
  this.scrollAdjustments = 0;
257
- this.scrollDirection = isScrolling ? this.scrollOffset < offset ? "forward" : "backward" : null;
292
+ this.scrollDirection = isScrolling ? this.getScrollOffset() < offset ? "forward" : "backward" : null;
258
293
  this.scrollOffset = offset;
259
294
  const prevIsScrolling = this.isScrolling;
260
295
  this.isScrolling = isScrolling;
@@ -264,28 +299,21 @@ class Virtualizer {
264
299
  }
265
300
  };
266
301
  this.getSize = () => {
302
+ if (!this.options.enabled) {
303
+ this.scrollRect = null;
304
+ return 0;
305
+ }
306
+ this.scrollRect = this.scrollRect ?? this.options.initialRect;
267
307
  return this.scrollRect[this.options.horizontal ? "width" : "height"];
268
308
  };
269
- this.getMeasurementOptions = memo(
270
- () => [
271
- this.options.count,
272
- this.options.paddingStart,
273
- this.options.scrollMargin,
274
- this.options.getItemKey
275
- ],
276
- (count, paddingStart, scrollMargin, getItemKey) => {
277
- this.pendingMeasuredCacheIndexes = [];
278
- return {
279
- count,
280
- paddingStart,
281
- scrollMargin,
282
- getItemKey
283
- };
284
- },
285
- {
286
- key: false
309
+ this.getScrollOffset = () => {
310
+ if (!this.options.enabled) {
311
+ this.scrollOffset = null;
312
+ return 0;
287
313
  }
288
- );
314
+ this.scrollOffset = this.scrollOffset ?? (typeof this.options.initialOffset === "function" ? this.options.initialOffset() : this.options.initialOffset);
315
+ return this.scrollOffset;
316
+ };
289
317
  this.getFurthestMeasurement = (measurements, index) => {
290
318
  const furthestMeasurementsFound = /* @__PURE__ */ new Map();
291
319
  const furthestMeasurements = /* @__PURE__ */ new Map();
@@ -313,9 +341,42 @@ class Virtualizer {
313
341
  return a.end - b.end;
314
342
  })[0] : void 0;
315
343
  };
344
+ this.getMeasurementOptions = memo(
345
+ () => [
346
+ this.options.count,
347
+ this.options.paddingStart,
348
+ this.options.scrollMargin,
349
+ this.options.getItemKey,
350
+ this.options.enabled
351
+ ],
352
+ (count, paddingStart, scrollMargin, getItemKey, enabled) => {
353
+ this.pendingMeasuredCacheIndexes = [];
354
+ return {
355
+ count,
356
+ paddingStart,
357
+ scrollMargin,
358
+ getItemKey,
359
+ enabled
360
+ };
361
+ },
362
+ {
363
+ key: false
364
+ }
365
+ );
316
366
  this.getMeasurements = memo(
317
367
  () => [this.getMeasurementOptions(), this.itemSizeCache],
318
- ({ count, paddingStart, scrollMargin, getItemKey }, itemSizeCache) => {
368
+ ({ count, paddingStart, scrollMargin, getItemKey, enabled }, itemSizeCache) => {
369
+ if (!enabled) {
370
+ this.measurementsCache = [];
371
+ this.itemSizeCache.clear();
372
+ return [];
373
+ }
374
+ if (this.measurementsCache.length === 0) {
375
+ this.measurementsCache = this.options.initialMeasurementsCache;
376
+ this.measurementsCache.forEach((item) => {
377
+ this.itemSizeCache.set(item.key, item.size);
378
+ });
379
+ }
319
380
  const min = this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
320
381
  this.pendingMeasuredCacheIndexes = [];
321
382
  const measurements = this.measurementsCache.slice(0, min);
@@ -345,7 +406,7 @@ class Virtualizer {
345
406
  }
346
407
  );
347
408
  this.calculateRange = memo(
348
- () => [this.getMeasurements(), this.getSize(), this.scrollOffset],
409
+ () => [this.getMeasurements(), this.getSize(), this.getScrollOffset()],
349
410
  (measurements, outerSize, scrollOffset) => {
350
411
  return this.range = measurements.length > 0 && outerSize > 0 ? calculateRange({
351
412
  measurements,
@@ -390,7 +451,7 @@ class Virtualizer {
390
451
  return parseInt(indexStr, 10);
391
452
  };
392
453
  this._measureElement = (node, entry) => {
393
- const item = this.measurementsCache[this.indexFromElement(node)];
454
+ const item = this.getMeasurements()[this.indexFromElement(node)];
394
455
  if (!item || !node.isConnected) {
395
456
  this.measureElementCache.forEach((cached, key) => {
396
457
  if (cached === node) {
@@ -415,11 +476,11 @@ class Virtualizer {
415
476
  const itemSize = this.itemSizeCache.get(item.key) ?? item.size;
416
477
  const delta = size - itemSize;
417
478
  if (delta !== 0) {
418
- if (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.scrollOffset + this.scrollAdjustments) {
479
+ if (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments) {
419
480
  if (process.env.NODE_ENV !== "production" && this.options.debug) {
420
481
  console.info("correction", delta);
421
482
  }
422
- this._scrollToOffset(this.scrollOffset, {
483
+ this._scrollToOffset(this.getScrollOffset(), {
423
484
  adjustments: this.scrollAdjustments += delta,
424
485
  behavior: void 0
425
486
  });
@@ -453,6 +514,9 @@ class Virtualizer {
453
514
  );
454
515
  this.getVirtualItemForOffset = (offset) => {
455
516
  const measurements = this.getMeasurements();
517
+ if (measurements.length === 0) {
518
+ return void 0;
519
+ }
456
520
  return notUndefined(
457
521
  measurements[findNearestBinarySearch(
458
522
  0,
@@ -464,10 +528,11 @@ class Virtualizer {
464
528
  };
465
529
  this.getOffsetForAlignment = (toOffset, align) => {
466
530
  const size = this.getSize();
531
+ const scrollOffset = this.getScrollOffset();
467
532
  if (align === "auto") {
468
- if (toOffset <= this.scrollOffset) {
533
+ if (toOffset <= scrollOffset) {
469
534
  align = "start";
470
- } else if (toOffset >= this.scrollOffset + size) {
535
+ } else if (toOffset >= scrollOffset + size) {
471
536
  align = "end";
472
537
  } else {
473
538
  align = "start";
@@ -482,28 +547,33 @@ class Virtualizer {
482
547
  }
483
548
  const scrollSizeProp = this.options.horizontal ? "scrollWidth" : "scrollHeight";
484
549
  const scrollSize = this.scrollElement ? "document" in this.scrollElement ? this.scrollElement.document.documentElement[scrollSizeProp] : this.scrollElement[scrollSizeProp] : 0;
485
- const maxOffset = scrollSize - this.getSize();
550
+ const maxOffset = scrollSize - size;
486
551
  return Math.max(Math.min(maxOffset, toOffset), 0);
487
552
  };
488
553
  this.getOffsetForIndex = (index, align = "auto") => {
489
554
  index = Math.max(0, Math.min(index, this.options.count - 1));
490
- const measurement = notUndefined(this.getMeasurements()[index]);
555
+ const item = this.getMeasurements()[index];
556
+ if (!item) {
557
+ return void 0;
558
+ }
559
+ const size = this.getSize();
560
+ const scrollOffset = this.getScrollOffset();
491
561
  if (align === "auto") {
492
- if (measurement.end >= this.scrollOffset + this.getSize() - this.options.scrollPaddingEnd) {
562
+ if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {
493
563
  align = "end";
494
- } else if (measurement.start <= this.scrollOffset + this.options.scrollPaddingStart) {
564
+ } else if (item.start <= scrollOffset + this.options.scrollPaddingStart) {
495
565
  align = "start";
496
566
  } else {
497
- return [this.scrollOffset, align];
567
+ return [scrollOffset, align];
498
568
  }
499
569
  }
500
- const toOffset = align === "end" ? measurement.end + this.options.scrollPaddingEnd : measurement.start - this.options.scrollPaddingStart;
570
+ const toOffset = align === "end" ? item.end + this.options.scrollPaddingEnd : item.start - this.options.scrollPaddingStart;
501
571
  return [this.getOffsetForAlignment(toOffset, align), align];
502
572
  };
503
573
  this.isDynamicMode = () => this.measureElementCache.size > 0;
504
574
  this.cancelScrollToIndex = () => {
505
- if (this.scrollToIndexTimeoutId !== null) {
506
- clearTimeout(this.scrollToIndexTimeoutId);
575
+ if (this.scrollToIndexTimeoutId !== null && this.targetWindow) {
576
+ this.targetWindow.clearTimeout(this.scrollToIndexTimeoutId);
507
577
  this.scrollToIndexTimeoutId = null;
508
578
  }
509
579
  };
@@ -527,17 +597,22 @@ class Virtualizer {
527
597
  "The `smooth` scroll behavior is not fully supported with dynamic size."
528
598
  );
529
599
  }
530
- const [toOffset, align] = this.getOffsetForIndex(index, initialAlign);
531
- this._scrollToOffset(toOffset, { adjustments: void 0, behavior });
532
- if (behavior !== "smooth" && this.isDynamicMode()) {
533
- this.scrollToIndexTimeoutId = setTimeout(() => {
600
+ const offsetAndAlign = this.getOffsetForIndex(index, initialAlign);
601
+ if (!offsetAndAlign)
602
+ return;
603
+ const [offset, align] = offsetAndAlign;
604
+ this._scrollToOffset(offset, { adjustments: void 0, behavior });
605
+ if (behavior !== "smooth" && this.isDynamicMode() && this.targetWindow) {
606
+ this.scrollToIndexTimeoutId = this.targetWindow.setTimeout(() => {
534
607
  this.scrollToIndexTimeoutId = null;
535
608
  const elementInDOM = this.measureElementCache.has(
536
609
  this.options.getItemKey(index)
537
610
  );
538
611
  if (elementInDOM) {
539
- const [toOffset2] = this.getOffsetForIndex(index, align);
540
- if (!approxEqual(toOffset2, this.scrollOffset)) {
612
+ const [latestOffset] = notUndefined(
613
+ this.getOffsetForIndex(index, align)
614
+ );
615
+ if (!approxEqual(latestOffset, this.getScrollOffset())) {
541
616
  this.scrollToIndex(index, { align, behavior });
542
617
  }
543
618
  } else {
@@ -553,7 +628,7 @@ class Virtualizer {
553
628
  "The `smooth` scroll behavior is not fully supported with dynamic size."
554
629
  );
555
630
  }
556
- this._scrollToOffset(this.scrollOffset + delta, {
631
+ this._scrollToOffset(this.getScrollOffset() + delta, {
557
632
  adjustments: void 0,
558
633
  behavior
559
634
  });
@@ -583,13 +658,6 @@ class Virtualizer {
583
658
  (_b = (_a = this.options).onChange) == null ? void 0 : _b.call(_a, this, false);
584
659
  };
585
660
  this.setOptions(opts);
586
- this.scrollRect = this.options.initialRect;
587
- this.scrollOffset = typeof this.options.initialOffset === "function" ? this.options.initialOffset() : this.options.initialOffset;
588
- this.measurementsCache = this.options.initialMeasurementsCache;
589
- this.measurementsCache.forEach((item) => {
590
- this.itemSizeCache.set(item.key, item.size);
591
- });
592
- this.notify(false, false);
593
661
  }
594
662
  }
595
663
  const findNearestBinarySearch = (low, high, getCurrentValue, value) => {