react-virtuoso 0.0.0-development → 0.0.0-semantic-release

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.
package/dist/index.mjs CHANGED
@@ -34,6 +34,9 @@ function joinProc(...procs) {
34
34
  procs.map(call);
35
35
  };
36
36
  }
37
+ function isDefined(arg) {
38
+ return arg !== void 0;
39
+ }
37
40
  function noop() {
38
41
  }
39
42
  function subscribe(emitter, subscription) {
@@ -184,7 +187,7 @@ function skip(times) {
184
187
  };
185
188
  }
186
189
  function throttleTime(interval) {
187
- let currentValue;
190
+ let currentValue = null;
188
191
  let timeout;
189
192
  return (done) => (value) => {
190
193
  currentValue = value;
@@ -392,7 +395,22 @@ function systemToComponent(systemSpec, map2, Root) {
392
395
  const usePublisher2 = (key) => {
393
396
  return React.useCallback(curry2to1(publish, React.useContext(Context)[key]), [key]);
394
397
  };
395
- const useEmitterValue2 = (key) => {
398
+ const useEmitterValue18 = (key) => {
399
+ const system2 = React.useContext(Context);
400
+ const source = system2[key];
401
+ const cb = React.useCallback(
402
+ (c) => {
403
+ return subscribe(source, c);
404
+ },
405
+ [source]
406
+ );
407
+ return React.useSyncExternalStore(
408
+ cb,
409
+ () => getValue(source),
410
+ () => getValue(source)
411
+ );
412
+ };
413
+ const useEmitterValueLegacy = (key) => {
396
414
  const system2 = React.useContext(Context);
397
415
  const source = system2[key];
398
416
  const [value, setValue] = React.useState(curry1to0(getValue, source));
@@ -406,6 +424,7 @@ function systemToComponent(systemSpec, map2, Root) {
406
424
  );
407
425
  return value;
408
426
  };
427
+ const useEmitterValue2 = React.version.startsWith("18") ? useEmitterValue18 : useEmitterValueLegacy;
409
428
  const useEmitter2 = (key, callback) => {
410
429
  const context = React.useContext(Context);
411
430
  const source = context[key];
@@ -428,17 +447,33 @@ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
428
447
  return LogLevel2;
429
448
  })(LogLevel || {});
430
449
  const CONSOLE_METHOD_MAP = {
431
- [0]: "debug",
432
- [1]: "log",
433
- [2]: "warn",
434
- [3]: "error"
450
+ [
451
+ 0
452
+ /* DEBUG */
453
+ ]: "debug",
454
+ [
455
+ 1
456
+ /* INFO */
457
+ ]: "log",
458
+ [
459
+ 2
460
+ /* WARN */
461
+ ]: "warn",
462
+ [
463
+ 3
464
+ /* ERROR */
465
+ ]: "error"
435
466
  };
436
467
  const getGlobalThis = () => typeof globalThis === "undefined" ? window : globalThis;
437
468
  const loggerSystem = system(
438
469
  () => {
439
- const logLevel = statefulStream(3);
470
+ const logLevel = statefulStream(
471
+ 3
472
+ /* ERROR */
473
+ );
440
474
  const log = statefulStream((label, message, level = 1) => {
441
- const currentLevel = getGlobalThis()["VIRTUOSO_LOG_LEVEL"] ?? getValue(logLevel);
475
+ var _a;
476
+ const currentLevel = (_a = getGlobalThis()["VIRTUOSO_LOG_LEVEL"]) != null ? _a : getValue(logLevel);
442
477
  if (level >= currentLevel) {
443
478
  console[CONSOLE_METHOD_MAP[level]](
444
479
  "%creact-virtuoso: %c%s %o",
@@ -462,12 +497,16 @@ function useSizeWithElRef(callback, enabled = true) {
462
497
  let callbackRef = (_el) => {
463
498
  };
464
499
  if (typeof ResizeObserver !== "undefined") {
465
- const observer = new ResizeObserver((entries) => {
466
- const element = entries[0].target;
467
- if (element.offsetParent !== null) {
468
- callback(element);
469
- }
470
- });
500
+ const observer = React.useMemo(() => {
501
+ return new ResizeObserver((entries) => {
502
+ requestAnimationFrame(() => {
503
+ const element = entries[0].target;
504
+ if (element.offsetParent !== null) {
505
+ callback(element);
506
+ }
507
+ });
508
+ });
509
+ }, [callback]);
471
510
  callbackRef = (elRef) => {
472
511
  if (elRef && enabled) {
473
512
  observer.observe(elRef);
@@ -485,27 +524,33 @@ function useSizeWithElRef(callback, enabled = true) {
485
524
  function useSize(callback, enabled = true) {
486
525
  return useSizeWithElRef(callback, enabled).callbackRef;
487
526
  }
488
- function useChangedListContentsSizes(callback, itemSize, enabled, scrollContainerStateCallback, log, gap, customScrollParent) {
489
- return useSizeWithElRef((el) => {
490
- const ranges = getChangedChildSizes(el.children, itemSize, "offsetHeight", log);
491
- let scrollableElement = el.parentElement;
492
- while (!scrollableElement.dataset["virtuosoScroller"]) {
493
- scrollableElement = scrollableElement.parentElement;
494
- }
495
- const windowScrolling = scrollableElement.lastElementChild.dataset["viewportType"] === "window";
496
- const scrollTop = customScrollParent ? customScrollParent.scrollTop : windowScrolling ? window.pageYOffset || document.documentElement.scrollTop : scrollableElement.scrollTop;
497
- const scrollHeight = customScrollParent ? customScrollParent.scrollHeight : windowScrolling ? document.documentElement.scrollHeight : scrollableElement.scrollHeight;
498
- const viewportHeight = customScrollParent ? customScrollParent.offsetHeight : windowScrolling ? window.innerHeight : scrollableElement.offsetHeight;
499
- scrollContainerStateCallback({
500
- scrollTop: Math.max(scrollTop, 0),
501
- scrollHeight,
502
- viewportHeight
503
- });
504
- gap == null ? void 0 : gap(resolveGapValue$1("row-gap", getComputedStyle(el).rowGap, log));
505
- if (ranges !== null) {
506
- callback(ranges);
507
- }
508
- }, enabled);
527
+ function useChangedListContentsSizes(callback, itemSize, enabled, scrollContainerStateCallback, log, gap, customScrollParent, horizontalDirection) {
528
+ const memoedCallback = React.useCallback(
529
+ (el) => {
530
+ const ranges = getChangedChildSizes(el.children, itemSize, horizontalDirection ? "offsetWidth" : "offsetHeight", log);
531
+ let scrollableElement = el.parentElement;
532
+ while (!scrollableElement.dataset["virtuosoScroller"]) {
533
+ scrollableElement = scrollableElement.parentElement;
534
+ }
535
+ const windowScrolling = scrollableElement.lastElementChild.dataset["viewportType"] === "window";
536
+ const scrollTop = customScrollParent ? horizontalDirection ? customScrollParent.scrollLeft : customScrollParent.scrollTop : windowScrolling ? horizontalDirection ? window.pageXOffset || document.documentElement.scrollLeft : window.pageYOffset || document.documentElement.scrollTop : horizontalDirection ? scrollableElement.scrollLeft : scrollableElement.scrollTop;
537
+ const scrollHeight = customScrollParent ? horizontalDirection ? customScrollParent.scrollWidth : customScrollParent.scrollHeight : windowScrolling ? horizontalDirection ? document.documentElement.scrollWidth : document.documentElement.scrollHeight : horizontalDirection ? scrollableElement.scrollWidth : scrollableElement.scrollHeight;
538
+ const viewportHeight = customScrollParent ? horizontalDirection ? customScrollParent.offsetWidth : customScrollParent.offsetHeight : windowScrolling ? horizontalDirection ? window.innerWidth : window.innerHeight : horizontalDirection ? scrollableElement.offsetWidth : scrollableElement.offsetHeight;
539
+ scrollContainerStateCallback({
540
+ scrollTop: Math.max(scrollTop, 0),
541
+ scrollHeight,
542
+ viewportHeight
543
+ });
544
+ gap == null ? void 0 : gap(
545
+ horizontalDirection ? resolveGapValue$1("column-gap", getComputedStyle(el).columnGap, log) : resolveGapValue$1("row-gap", getComputedStyle(el).rowGap, log)
546
+ );
547
+ if (ranges !== null) {
548
+ callback(ranges);
549
+ }
550
+ },
551
+ [callback, itemSize, log, gap, customScrollParent, scrollContainerStateCallback]
552
+ );
553
+ return useSizeWithElRef(memoedCallback, enabled);
509
554
  }
510
555
  function getChangedChildSizes(children, itemSize, field, log) {
511
556
  const length = children.length;
@@ -543,7 +588,7 @@ function resolveGapValue$1(property, value, log) {
543
588
  if (value === "normal") {
544
589
  return 0;
545
590
  }
546
- return parseInt(value ?? "0", 10);
591
+ return parseInt(value != null ? value : "0", 10);
547
592
  }
548
593
  function correctItemSize(el, dimension) {
549
594
  return Math.round(el.getBoundingClientRect()[dimension]);
@@ -551,7 +596,7 @@ function correctItemSize(el, dimension) {
551
596
  function approximatelyEqual(num1, num2) {
552
597
  return Math.abs(num1 - num2) < 1.01;
553
598
  }
554
- function useScrollTop(scrollContainerStateCallback, smoothScrollTargetReached, scrollerElement, scrollerRefCallback = noop, customScrollParent) {
599
+ function useScrollTop(scrollContainerStateCallback, smoothScrollTargetReached, scrollerElement, scrollerRefCallback = noop, customScrollParent, horizontalDirection) {
555
600
  const scrollerRef = React.useRef(null);
556
601
  const scrollTopTarget = React.useRef(null);
557
602
  const timeoutRef = React.useRef(null);
@@ -559,9 +604,9 @@ function useScrollTop(scrollContainerStateCallback, smoothScrollTargetReached, s
559
604
  (ev) => {
560
605
  const el = ev.target;
561
606
  const windowScroll = el === window || el === document;
562
- const scrollTop = windowScroll ? window.pageYOffset || document.documentElement.scrollTop : el.scrollTop;
563
- const scrollHeight = windowScroll ? document.documentElement.scrollHeight : el.scrollHeight;
564
- const viewportHeight = windowScroll ? window.innerHeight : el.offsetHeight;
607
+ const scrollTop = horizontalDirection ? windowScroll ? window.pageXOffset || document.documentElement.scrollLeft : el.scrollLeft : windowScroll ? window.pageYOffset || document.documentElement.scrollTop : el.scrollTop;
608
+ const scrollHeight = horizontalDirection ? windowScroll ? document.documentElement.scrollWidth : el.scrollWidth : windowScroll ? document.documentElement.scrollHeight : el.scrollHeight;
609
+ const viewportHeight = horizontalDirection ? windowScroll ? window.innerWidth : el.offsetWidth : windowScroll ? window.innerHeight : el.offsetHeight;
565
610
  const call2 = () => {
566
611
  scrollContainerStateCallback({
567
612
  scrollTop: Math.max(scrollTop, 0),
@@ -599,7 +644,7 @@ function useScrollTop(scrollContainerStateCallback, smoothScrollTargetReached, s
599
644
  }, [scrollerRef, handler, scrollerElement, scrollerRefCallback, customScrollParent]);
600
645
  function scrollToCallback(location) {
601
646
  const scrollerElement2 = scrollerRef.current;
602
- if (!scrollerElement2 || "offsetHeight" in scrollerElement2 && scrollerElement2.offsetHeight === 0) {
647
+ if (!scrollerElement2 || (horizontalDirection ? "offsetWidth" in scrollerElement2 && scrollerElement2.offsetWidth === 0 : "offsetHeight" in scrollerElement2 && scrollerElement2.offsetHeight === 0)) {
603
648
  return;
604
649
  }
605
650
  const isSmooth = location.behavior === "smooth";
@@ -607,13 +652,16 @@ function useScrollTop(scrollContainerStateCallback, smoothScrollTargetReached, s
607
652
  let scrollHeight;
608
653
  let scrollTop;
609
654
  if (scrollerElement2 === window) {
610
- scrollHeight = Math.max(correctItemSize(document.documentElement, "height"), document.documentElement.scrollHeight);
611
- offsetHeight = window.innerHeight;
612
- scrollTop = document.documentElement.scrollTop;
655
+ scrollHeight = Math.max(
656
+ correctItemSize(document.documentElement, horizontalDirection ? "width" : "height"),
657
+ horizontalDirection ? document.documentElement.scrollWidth : document.documentElement.scrollHeight
658
+ );
659
+ offsetHeight = horizontalDirection ? window.innerWidth : window.innerHeight;
660
+ scrollTop = horizontalDirection ? document.documentElement.scrollLeft : document.documentElement.scrollTop;
613
661
  } else {
614
- scrollHeight = scrollerElement2.scrollHeight;
615
- offsetHeight = correctItemSize(scrollerElement2, "height");
616
- scrollTop = scrollerElement2.scrollTop;
662
+ scrollHeight = scrollerElement2[horizontalDirection ? "scrollWidth" : "scrollHeight"];
663
+ offsetHeight = correctItemSize(scrollerElement2, horizontalDirection ? "width" : "height");
664
+ scrollTop = scrollerElement2[horizontalDirection ? "scrollLeft" : "scrollTop"];
617
665
  }
618
666
  const maxScrollTop = scrollHeight - offsetHeight;
619
667
  location.top = Math.ceil(Math.max(Math.min(maxScrollTop, location.top), 0));
@@ -637,9 +685,15 @@ function useScrollTop(scrollContainerStateCallback, smoothScrollTargetReached, s
637
685
  } else {
638
686
  scrollTopTarget.current = null;
639
687
  }
688
+ if (horizontalDirection) {
689
+ location = { left: location.top, behavior: location.behavior };
690
+ }
640
691
  scrollerElement2.scrollTo(location);
641
692
  }
642
693
  function scrollByCallback(location) {
694
+ if (horizontalDirection) {
695
+ location = { left: location.top, behavior: location.behavior };
696
+ }
643
697
  scrollerRef.current.scrollBy(location);
644
698
  }
645
699
  return { scrollerRef, scrollByCallback, scrollToCallback };
@@ -660,6 +714,7 @@ const domIOSystem = system(
660
714
  const scrollTo = stream();
661
715
  const scrollBy = stream();
662
716
  const scrollingInProgress = statefulStream(false);
717
+ const horizontalDirection = statefulStream(false);
663
718
  connect(
664
719
  pipe(
665
720
  scrollContainerState,
@@ -676,6 +731,7 @@ const domIOSystem = system(
676
731
  );
677
732
  connect(scrollTop, statefulScrollTop);
678
733
  return {
734
+ // input
679
735
  scrollContainerState,
680
736
  scrollTop,
681
737
  viewportHeight,
@@ -685,8 +741,11 @@ const domIOSystem = system(
685
741
  footerHeight,
686
742
  scrollHeight,
687
743
  smoothScrollTargetReached,
744
+ horizontalDirection,
745
+ // signals
688
746
  scrollTo,
689
747
  scrollBy,
748
+ // state
690
749
  statefulScrollTop,
691
750
  deviation,
692
751
  scrollingInProgress
@@ -724,14 +783,26 @@ function remove(node, key) {
724
783
  return adjust(clone(node, { r: remove(r, key) }));
725
784
  }
726
785
  }
786
+ function find(node, key) {
787
+ if (empty(node)) {
788
+ return;
789
+ }
790
+ if (key === node.k) {
791
+ return node.v;
792
+ } else if (key < node.k) {
793
+ return find(node.l, key);
794
+ } else {
795
+ return find(node.r, key);
796
+ }
797
+ }
727
798
  function findMaxKeyValue(node, value, field = "k") {
728
799
  if (empty(node)) {
729
800
  return [-Infinity, void 0];
730
801
  }
731
- if (node[field] === value) {
802
+ if (Number(node[field]) === value) {
732
803
  return [node.k, node.v];
733
804
  }
734
- if (node[field] < value) {
805
+ if (Number(node[field]) < value) {
735
806
  const r = findMaxKeyValue(node.r, value, field);
736
807
  if (r[0] === -Infinity) {
737
808
  return [node.k, node.v];
@@ -918,6 +989,16 @@ function rangeIncludes(refRange) {
918
989
  return range.start === startIndex && (range.end === endIndex || range.end === Infinity) && range.value === size;
919
990
  };
920
991
  }
992
+ function affectedGroupCount(offset, groupIndices) {
993
+ let recognizedOffsetItems = 0;
994
+ let groupIndex = 0;
995
+ while (recognizedOffsetItems < offset) {
996
+ recognizedOffsetItems += groupIndices[groupIndex + 1] - groupIndices[groupIndex] - 1;
997
+ groupIndex++;
998
+ }
999
+ const offsetIsExact = recognizedOffsetItems === offset;
1000
+ return groupIndex - (offsetIsExact ? 0 : 1);
1001
+ }
921
1002
  function insertRanges(sizeTree, ranges) {
922
1003
  let syncStart = empty(sizeTree) ? 0 : Infinity;
923
1004
  for (const range of ranges) {
@@ -1086,6 +1167,13 @@ function originalIndexFromItemIndex(itemIndex, sizes) {
1086
1167
  function hasGroups(sizes) {
1087
1168
  return !empty(sizes.groupOffsetTree);
1088
1169
  }
1170
+ function sizeTreeToRanges(sizeTree) {
1171
+ return walk(sizeTree).map(({ k: startIndex, v: size }, index, sizeArray) => {
1172
+ const nextSize = sizeArray[index + 1];
1173
+ const endIndex = nextSize ? nextSize.k - 1 : Infinity;
1174
+ return { startIndex, endIndex, size };
1175
+ });
1176
+ }
1089
1177
  const SIZE_MAP = {
1090
1178
  offsetHeight: "height",
1091
1179
  offsetWidth: "width"
@@ -1109,6 +1197,18 @@ const sizeSystem = system(
1109
1197
  pipe(sizeRanges, withLatestFrom(groupIndices, log, gap), scan(sizeStateReducer, initial), distinctUntilChanged()),
1110
1198
  initial
1111
1199
  );
1200
+ const prevGroupIndices = statefulStreamFromEmitter(
1201
+ pipe(
1202
+ groupIndices,
1203
+ distinctUntilChanged(),
1204
+ scan((prev, curr) => ({ prev: prev.current, current: curr }), {
1205
+ prev: [],
1206
+ current: []
1207
+ }),
1208
+ map(({ prev }) => prev)
1209
+ ),
1210
+ []
1211
+ );
1112
1212
  connect(
1113
1213
  pipe(
1114
1214
  groupIndices,
@@ -1192,10 +1292,15 @@ const sizeSystem = system(
1192
1292
  map((val) => val.diff)
1193
1293
  ),
1194
1294
  (offset) => {
1295
+ const { groupIndices: groupIndices2 } = getValue(sizes);
1195
1296
  if (offset > 0) {
1196
1297
  publish(recalcInProgress, true);
1197
- publish(unshiftWith, offset);
1298
+ publish(unshiftWith, offset + affectedGroupCount(offset, groupIndices2));
1198
1299
  } else if (offset < 0) {
1300
+ const prevGroupIndicesValue = getValue(prevGroupIndices);
1301
+ if (prevGroupIndicesValue.length > 0) {
1302
+ offset -= affectedGroupCount(-offset, prevGroupIndicesValue);
1303
+ }
1199
1304
  publish(shiftWith, offset);
1200
1305
  }
1201
1306
  }
@@ -1215,8 +1320,59 @@ const sizeSystem = system(
1215
1320
  unshiftWith,
1216
1321
  withLatestFrom(sizes),
1217
1322
  map(([unshiftWith2, sizes2]) => {
1218
- if (sizes2.groupIndices.length > 0) {
1219
- throw new Error("Virtuoso: prepending items does not work with groups");
1323
+ const groupedMode = sizes2.groupIndices.length > 0;
1324
+ const initialRanges = [];
1325
+ const defaultSize = sizes2.lastSize;
1326
+ if (groupedMode) {
1327
+ const firstGroupSize = find(sizes2.sizeTree, 0);
1328
+ let prependedGroupItemsCount = 0;
1329
+ let groupIndex = 0;
1330
+ while (prependedGroupItemsCount < unshiftWith2) {
1331
+ const theGroupIndex = sizes2.groupIndices[groupIndex];
1332
+ const groupItemCount = sizes2.groupIndices.length === groupIndex + 1 ? Infinity : sizes2.groupIndices[groupIndex + 1] - theGroupIndex - 1;
1333
+ initialRanges.push({
1334
+ startIndex: theGroupIndex,
1335
+ endIndex: theGroupIndex,
1336
+ size: firstGroupSize
1337
+ });
1338
+ initialRanges.push({
1339
+ startIndex: theGroupIndex + 1,
1340
+ endIndex: theGroupIndex + 1 + groupItemCount - 1,
1341
+ size: defaultSize
1342
+ });
1343
+ groupIndex++;
1344
+ prependedGroupItemsCount += groupItemCount + 1;
1345
+ }
1346
+ const sizeTreeKV = walk(sizes2.sizeTree);
1347
+ const firstGroupIsExpanded = prependedGroupItemsCount !== unshiftWith2;
1348
+ if (firstGroupIsExpanded) {
1349
+ sizeTreeKV.shift();
1350
+ }
1351
+ return sizeTreeKV.reduce(
1352
+ (acc, { k: index, v: size }) => {
1353
+ let ranges = acc.ranges;
1354
+ if (acc.prevSize !== 0) {
1355
+ ranges = [
1356
+ ...acc.ranges,
1357
+ {
1358
+ startIndex: acc.prevIndex,
1359
+ endIndex: index + unshiftWith2 - 1,
1360
+ size: acc.prevSize
1361
+ }
1362
+ ];
1363
+ }
1364
+ return {
1365
+ ranges,
1366
+ prevIndex: index + unshiftWith2,
1367
+ prevSize: size
1368
+ };
1369
+ },
1370
+ {
1371
+ ranges: initialRanges,
1372
+ prevIndex: unshiftWith2,
1373
+ prevSize: 0
1374
+ }
1375
+ ).ranges;
1220
1376
  }
1221
1377
  return walk(sizes2.sizeTree).reduce(
1222
1378
  (acc, { k: index, v: size }) => {
@@ -1229,7 +1385,7 @@ const sizeSystem = system(
1229
1385
  {
1230
1386
  ranges: [],
1231
1387
  prevIndex: 0,
1232
- prevSize: sizes2.lastSize
1388
+ prevSize: defaultSize
1233
1389
  }
1234
1390
  ).ranges;
1235
1391
  })
@@ -1251,22 +1407,53 @@ const sizeSystem = system(
1251
1407
  shiftWith,
1252
1408
  withLatestFrom(sizes, gap),
1253
1409
  map(([shiftWith2, sizes2, gap2]) => {
1254
- if (sizes2.groupIndices.length > 0) {
1255
- throw new Error("Virtuoso: shifting items does not work with groups");
1410
+ const groupedMode = sizes2.groupIndices.length > 0;
1411
+ if (groupedMode) {
1412
+ if (empty(sizes2.sizeTree)) {
1413
+ return sizes2;
1414
+ }
1415
+ let newSizeTree = newTree();
1416
+ const prevGroupIndicesValue = getValue(prevGroupIndices);
1417
+ let removedItemsCount = 0;
1418
+ let groupIndex = 0;
1419
+ let groupOffset = 0;
1420
+ while (removedItemsCount < -shiftWith2) {
1421
+ groupOffset = prevGroupIndicesValue[groupIndex];
1422
+ const groupItemCount = prevGroupIndicesValue[groupIndex + 1] - groupOffset - 1;
1423
+ groupIndex++;
1424
+ removedItemsCount += groupItemCount + 1;
1425
+ }
1426
+ newSizeTree = walk(sizes2.sizeTree).reduce((acc, { k, v }) => {
1427
+ return insert(acc, Math.max(0, k + shiftWith2), v);
1428
+ }, newSizeTree);
1429
+ const aGroupIsShrunk = removedItemsCount !== -shiftWith2;
1430
+ if (aGroupIsShrunk) {
1431
+ const firstGroupSize = find(sizes2.sizeTree, groupOffset);
1432
+ newSizeTree = insert(newSizeTree, 0, firstGroupSize);
1433
+ const nextItemSize = findMaxKeyValue(sizes2.sizeTree, -shiftWith2 + 1)[1];
1434
+ newSizeTree = insert(newSizeTree, 1, nextItemSize);
1435
+ }
1436
+ return {
1437
+ ...sizes2,
1438
+ sizeTree: newSizeTree,
1439
+ ...createOffsetTree(sizes2.offsetTree, 0, newSizeTree, gap2)
1440
+ };
1441
+ } else {
1442
+ const newSizeTree = walk(sizes2.sizeTree).reduce((acc, { k, v }) => {
1443
+ return insert(acc, Math.max(0, k + shiftWith2), v);
1444
+ }, newTree());
1445
+ return {
1446
+ ...sizes2,
1447
+ sizeTree: newSizeTree,
1448
+ ...createOffsetTree(sizes2.offsetTree, 0, newSizeTree, gap2)
1449
+ };
1256
1450
  }
1257
- const newSizeTree = walk(sizes2.sizeTree).reduce((acc, { k, v }) => {
1258
- return insert(acc, Math.max(0, k + shiftWith2), v);
1259
- }, newTree());
1260
- return {
1261
- ...sizes2,
1262
- sizeTree: newSizeTree,
1263
- ...createOffsetTree(sizes2.offsetTree, 0, newSizeTree, gap2)
1264
- };
1265
1451
  })
1266
1452
  ),
1267
1453
  sizes
1268
1454
  );
1269
1455
  return {
1456
+ // input
1270
1457
  data,
1271
1458
  totalCount,
1272
1459
  sizeRanges,
@@ -1279,6 +1466,7 @@ const sizeSystem = system(
1279
1466
  beforeUnshiftWith,
1280
1467
  firstItemIndex,
1281
1468
  gap,
1469
+ // output
1282
1470
  sizes,
1283
1471
  listRefresh,
1284
1472
  statefulTotalCount,
@@ -1319,6 +1507,7 @@ const scrollToIndexSystem = system(
1319
1507
  { log }
1320
1508
  ]) => {
1321
1509
  const scrollToIndex = stream();
1510
+ const scrollTargetReached = stream();
1322
1511
  const topListHeight = statefulStream(0);
1323
1512
  let unsubscribeNextListRefresh = null;
1324
1513
  let cleartTimeoutRef = null;
@@ -1374,6 +1563,7 @@ const scrollToIndexSystem = system(
1374
1563
  log2("retrying to scroll to", { location }, LogLevel.DEBUG);
1375
1564
  publish(scrollToIndex, location);
1376
1565
  } else {
1566
+ publish(scrollTargetReached, true);
1377
1567
  log2("list did not change, scroll successful", {}, LogLevel.DEBUG);
1378
1568
  }
1379
1569
  };
@@ -1402,6 +1592,7 @@ const scrollToIndexSystem = system(
1402
1592
  );
1403
1593
  return {
1404
1594
  scrollToIndex,
1595
+ scrollTargetReached,
1405
1596
  topListHeight
1406
1597
  };
1407
1598
  },
@@ -1581,6 +1772,7 @@ const stateFlagsSystem = system(([{ scrollContainerState, scrollTop, viewportHei
1581
1772
  pipe(
1582
1773
  isScrolling,
1583
1774
  filter((value) => !value),
1775
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1584
1776
  mapTo(0)
1585
1777
  ),
1586
1778
  scrollVelocity
@@ -1628,15 +1820,23 @@ const propsReadySystem = system(
1628
1820
  tup(loggerSystem),
1629
1821
  { singleton: true }
1630
1822
  );
1823
+ function skipFrames(frameCount, callback) {
1824
+ if (frameCount == 0) {
1825
+ callback();
1826
+ } else {
1827
+ requestAnimationFrame(() => skipFrames(frameCount - 1, callback));
1828
+ }
1829
+ }
1631
1830
  function getInitialTopMostItemIndexNumber(location, totalCount) {
1632
1831
  const lastIndex = totalCount - 1;
1633
1832
  const index = typeof location === "number" ? location : location.index === "LAST" ? lastIndex : location.index;
1634
1833
  return index;
1635
1834
  }
1636
1835
  const initialTopMostItemIndexSystem = system(
1637
- ([{ sizes, listRefresh, defaultItemSize }, { scrollTop }, { scrollToIndex }, { didMount }]) => {
1836
+ ([{ sizes, listRefresh, defaultItemSize }, { scrollTop }, { scrollToIndex, scrollTargetReached }, { didMount }]) => {
1638
1837
  const scrolledToInitialItem = statefulStream(true);
1639
1838
  const initialTopMostItemIndex = statefulStream(0);
1839
+ const initialItemFinalLocationReached = statefulStream(true);
1640
1840
  connect(
1641
1841
  pipe(
1642
1842
  didMount,
@@ -1646,17 +1846,29 @@ const initialTopMostItemIndexSystem = system(
1646
1846
  ),
1647
1847
  scrolledToInitialItem
1648
1848
  );
1849
+ connect(
1850
+ pipe(
1851
+ didMount,
1852
+ withLatestFrom(initialTopMostItemIndex),
1853
+ filter(([_, location]) => !!location),
1854
+ mapTo(false)
1855
+ ),
1856
+ initialItemFinalLocationReached
1857
+ );
1649
1858
  subscribe(
1650
1859
  pipe(
1651
1860
  combineLatest(listRefresh, didMount),
1652
- withLatestFrom(scrolledToInitialItem, sizes, defaultItemSize),
1653
- filter(([[, didMount2], scrolledToInitialItem2, { sizeTree }, defaultItemSize2]) => {
1654
- return didMount2 && (!empty(sizeTree) || defaultItemSize2 !== void 0) && !scrolledToInitialItem2;
1861
+ withLatestFrom(scrolledToInitialItem, sizes, defaultItemSize, initialItemFinalLocationReached),
1862
+ filter(([[, didMount2], scrolledToInitialItem2, { sizeTree }, defaultItemSize2, scrollScheduled]) => {
1863
+ return didMount2 && (!empty(sizeTree) || isDefined(defaultItemSize2)) && !scrolledToInitialItem2 && !scrollScheduled;
1655
1864
  }),
1656
1865
  withLatestFrom(initialTopMostItemIndex)
1657
1866
  ),
1658
1867
  ([, initialTopMostItemIndex2]) => {
1659
- setTimeout(() => {
1868
+ handleNext(scrollTargetReached, () => {
1869
+ publish(initialItemFinalLocationReached, true);
1870
+ });
1871
+ skipFrames(3, () => {
1660
1872
  handleNext(scrollTop, () => {
1661
1873
  publish(scrolledToInitialItem, true);
1662
1874
  });
@@ -1666,7 +1878,8 @@ const initialTopMostItemIndexSystem = system(
1666
1878
  );
1667
1879
  return {
1668
1880
  scrolledToInitialItem,
1669
- initialTopMostItemIndex
1881
+ initialTopMostItemIndex,
1882
+ initialItemFinalLocationReached
1670
1883
  };
1671
1884
  },
1672
1885
  tup(sizeSystem, domIOSystem, scrollToIndexSystem, propsReadySystem),
@@ -1754,7 +1967,9 @@ const followOutputSystem = system(
1754
1967
  withLatestFrom(followOutput, totalCount)
1755
1968
  ),
1756
1969
  ([, followOutput2]) => {
1757
- trapNextSizeIncrease(followOutput2 !== false);
1970
+ if (getValue(scrolledToInitialItem)) {
1971
+ trapNextSizeIncrease(followOutput2 !== false);
1972
+ }
1758
1973
  }
1759
1974
  );
1760
1975
  subscribe(autoscrollToBottom, () => {
@@ -1897,10 +2112,12 @@ const sizeRangeSystem = system(
1897
2112
  [0, 0]
1898
2113
  );
1899
2114
  return {
2115
+ // input
1900
2116
  listBoundary,
1901
2117
  overscan,
1902
2118
  topListHeight,
1903
2119
  increaseViewportBy,
2120
+ // output
1904
2121
  visibleRange
1905
2122
  };
1906
2123
  },
@@ -1994,6 +2211,26 @@ function buildListState(items, topItems, totalCount, gap, sizes, firstItemIndex)
1994
2211
  firstItemIndex
1995
2212
  };
1996
2213
  }
2214
+ function buildListStateFromItemCount(itemCount, initialTopMostItemIndex, sizes, firstItemIndex, gap, data) {
2215
+ let includedGroupsCount = 0;
2216
+ if (sizes.groupIndices.length > 0) {
2217
+ for (const index of sizes.groupIndices) {
2218
+ if (index - includedGroupsCount >= itemCount) {
2219
+ break;
2220
+ }
2221
+ includedGroupsCount++;
2222
+ }
2223
+ }
2224
+ const adjustedCount = itemCount + includedGroupsCount;
2225
+ const initialTopMostItemIndexNumber = getInitialTopMostItemIndexNumber(initialTopMostItemIndex, adjustedCount);
2226
+ const items = Array.from({ length: adjustedCount }).map((_, index) => ({
2227
+ index: index + initialTopMostItemIndexNumber,
2228
+ size: 0,
2229
+ offset: 0,
2230
+ data: data[index + initialTopMostItemIndexNumber]
2231
+ }));
2232
+ return buildListState(items, [], adjustedCount, gap, sizes, firstItemIndex);
2233
+ }
1997
2234
  const listStateSystem = system(
1998
2235
  ([
1999
2236
  { sizes, totalCount, data, firstItemIndex, gap },
@@ -2006,6 +2243,7 @@ const listStateSystem = system(
2006
2243
  { recalcInProgress }
2007
2244
  ]) => {
2008
2245
  const topItemsIndexes = statefulStream([]);
2246
+ const initialItemCount = statefulStream(0);
2009
2247
  const itemsRendered = stream();
2010
2248
  connect(groupedListSystem2.topItemsIndexes, topItemsIndexes);
2011
2249
  const listState = statefulStreamFromEmitter(
@@ -2023,8 +2261,9 @@ const listStateSystem = system(
2023
2261
  duc(gap),
2024
2262
  data
2025
2263
  ),
2026
- filter(([mount, recalcInProgress2]) => {
2027
- return mount && !recalcInProgress2;
2264
+ filter(([mount, recalcInProgress2, , totalCount2, , , , , , , data2]) => {
2265
+ const dataChangeInProgress = data2 && data2.length !== totalCount2;
2266
+ return mount && !recalcInProgress2 && !dataChangeInProgress;
2028
2267
  }),
2029
2268
  map(
2030
2269
  ([
@@ -2042,11 +2281,22 @@ const listStateSystem = system(
2042
2281
  ]) => {
2043
2282
  const sizesValue = sizes2;
2044
2283
  const { sizeTree, offsetTree } = sizesValue;
2045
- if (totalCount2 === 0 || startOffset === 0 && endOffset === 0) {
2284
+ const initialItemCountValue = getValue(initialItemCount);
2285
+ if (totalCount2 === 0) {
2046
2286
  return { ...EMPTY_LIST_STATE, totalCount: totalCount2 };
2047
2287
  }
2288
+ if (startOffset === 0 && endOffset === 0) {
2289
+ if (initialItemCountValue === 0) {
2290
+ return { ...EMPTY_LIST_STATE, totalCount: totalCount2 };
2291
+ } else {
2292
+ return buildListStateFromItemCount(initialItemCountValue, initialTopMostItemIndex2, sizes2, firstItemIndex2, gap2, data2 || []);
2293
+ }
2294
+ }
2048
2295
  if (empty(sizeTree)) {
2049
- return buildListState(
2296
+ if (initialItemCountValue > 0) {
2297
+ return null;
2298
+ }
2299
+ const state = buildListState(
2050
2300
  probeItemSet(getInitialTopMostItemIndexNumber(initialTopMostItemIndex2, totalCount2), sizesValue, data2),
2051
2301
  [],
2052
2302
  totalCount2,
@@ -2054,6 +2304,7 @@ const listStateSystem = system(
2054
2304
  sizesValue,
2055
2305
  firstItemIndex2
2056
2306
  );
2307
+ return state;
2057
2308
  }
2058
2309
  const topItems = [];
2059
2310
  if (topItemsIndexes2.length > 0) {
@@ -2107,6 +2358,7 @@ const listStateSystem = system(
2107
2358
  return buildListState(items, topItems, totalCount2, gap2, sizesValue, firstItemIndex2);
2108
2359
  }
2109
2360
  ),
2361
+ //@ts-expect-error filter needs to be fixed
2110
2362
  filter((value) => value !== null),
2111
2363
  distinctUntilChanged()
2112
2364
  ),
@@ -2115,7 +2367,7 @@ const listStateSystem = system(
2115
2367
  connect(
2116
2368
  pipe(
2117
2369
  data,
2118
- filter((data2) => data2 !== void 0),
2370
+ filter(isDefined),
2119
2371
  map((data2) => data2 == null ? void 0 : data2.length)
2120
2372
  ),
2121
2373
  totalCount
@@ -2185,7 +2437,7 @@ const listStateSystem = system(
2185
2437
  distinctUntilChanged(rangeComparator)
2186
2438
  )
2187
2439
  );
2188
- return { listState, topItemsIndexes, endReached, startReached, rangeChanged, itemsRendered, ...stateFlags };
2440
+ return { listState, topItemsIndexes, endReached, startReached, rangeChanged, itemsRendered, initialItemCount, ...stateFlags };
2189
2441
  },
2190
2442
  tup(
2191
2443
  sizeSystem,
@@ -2200,34 +2452,22 @@ const listStateSystem = system(
2200
2452
  { singleton: true }
2201
2453
  );
2202
2454
  const initialItemCountSystem = system(
2203
- ([{ sizes, firstItemIndex, data, gap }, { listState }, { didMount }]) => {
2204
- const initialItemCount = statefulStream(0);
2455
+ ([{ sizes, firstItemIndex, data, gap }, { initialTopMostItemIndex }, { initialItemCount, listState }, { didMount }]) => {
2205
2456
  connect(
2206
2457
  pipe(
2207
2458
  didMount,
2208
2459
  withLatestFrom(initialItemCount),
2209
2460
  filter(([, count]) => count !== 0),
2210
- withLatestFrom(sizes, firstItemIndex, gap, data),
2211
- map(([[, count], sizes2, firstItemIndex2, gap2, data2 = []]) => {
2212
- let includedGroupsCount = 0;
2213
- if (sizes2.groupIndices.length > 0) {
2214
- for (const index of sizes2.groupIndices) {
2215
- if (index - includedGroupsCount >= count) {
2216
- break;
2217
- }
2218
- includedGroupsCount++;
2219
- }
2220
- }
2221
- const adjustedCount = count + includedGroupsCount;
2222
- const items = Array.from({ length: adjustedCount }).map((_, index) => ({ index, size: 0, offset: 0, data: data2[index] }));
2223
- return buildListState(items, [], adjustedCount, gap2, sizes2, firstItemIndex2);
2461
+ withLatestFrom(initialTopMostItemIndex, sizes, firstItemIndex, gap, data),
2462
+ map(([[, count], initialTopMostItemIndexValue, sizes2, firstItemIndex2, gap2, data2 = []]) => {
2463
+ return buildListStateFromItemCount(count, initialTopMostItemIndexValue, sizes2, firstItemIndex2, gap2, data2);
2224
2464
  })
2225
2465
  ),
2226
2466
  listState
2227
2467
  );
2228
- return { initialItemCount };
2468
+ return {};
2229
2469
  },
2230
- tup(sizeSystem, listStateSystem, propsReadySystem),
2470
+ tup(sizeSystem, initialTopMostItemIndexSystem, listStateSystem, propsReadySystem),
2231
2471
  { singleton: true }
2232
2472
  );
2233
2473
  const scrollSeekSystem = system(
@@ -2343,9 +2583,9 @@ const upwardScrollFixSystem = system(
2343
2583
  [0, [], 0, 0]
2344
2584
  ),
2345
2585
  filter(([amount]) => amount !== 0),
2346
- withLatestFrom(scrollTop, scrollDirection, scrollingInProgress, isAtBottom, log),
2347
- filter(([, scrollTop2, scrollDirection2, scrollingInProgress2]) => {
2348
- return !scrollingInProgress2 && scrollTop2 !== 0 && scrollDirection2 === UP;
2586
+ withLatestFrom(scrollTop, scrollDirection, scrollingInProgress, isAtBottom, log, recalcInProgress),
2587
+ filter(([, scrollTop2, scrollDirection2, scrollingInProgress2, , , recalcInProgress2]) => {
2588
+ return !recalcInProgress2 && !scrollingInProgress2 && scrollTop2 !== 0 && scrollDirection2 === UP;
2349
2589
  }),
2350
2590
  map(([[amount], , , , , log2]) => {
2351
2591
  log2("Upward scrolling compensation", { amount }, LogLevel.DEBUG);
@@ -2391,7 +2631,32 @@ const upwardScrollFixSystem = system(
2391
2631
  pipe(
2392
2632
  beforeUnshiftWith,
2393
2633
  withLatestFrom(sizes, gap),
2394
- map(([offset, { lastSize }, gap2]) => offset * lastSize + offset * gap2)
2634
+ map(([offset, { lastSize: defaultItemSize, groupIndices, sizeTree }, gap2]) => {
2635
+ function getItemOffset(itemCount) {
2636
+ return itemCount * (defaultItemSize + gap2);
2637
+ }
2638
+ if (groupIndices.length === 0) {
2639
+ return getItemOffset(offset);
2640
+ } else {
2641
+ let amount = 0;
2642
+ const defaultGroupSize = find(sizeTree, 0);
2643
+ let recognizedOffsetItems = 0;
2644
+ let groupIndex = 0;
2645
+ while (recognizedOffsetItems < offset) {
2646
+ recognizedOffsetItems++;
2647
+ amount += defaultGroupSize;
2648
+ let groupItemCount = groupIndices.length === groupIndex + 1 ? Infinity : groupIndices[groupIndex + 1] - groupIndices[groupIndex] - 1;
2649
+ if (recognizedOffsetItems + groupItemCount > offset) {
2650
+ amount -= defaultGroupSize;
2651
+ groupItemCount = offset - recognizedOffsetItems + 1;
2652
+ }
2653
+ recognizedOffsetItems += groupItemCount;
2654
+ amount += getItemOffset(groupItemCount);
2655
+ groupIndex++;
2656
+ }
2657
+ return amount;
2658
+ }
2659
+ })
2395
2660
  ),
2396
2661
  (offset) => {
2397
2662
  publish(deviation, offset);
@@ -2409,7 +2674,7 @@ const upwardScrollFixSystem = system(
2409
2674
  tup(domIOSystem, stateFlagsSystem, listStateSystem, sizeSystem, loggerSystem, recalcSystem)
2410
2675
  );
2411
2676
  const initialScrollTopSystem = system(
2412
- ([{ totalListHeight }, { didMount }, { scrollTo }]) => {
2677
+ ([{ didMount }, { scrollTo }, { listState }]) => {
2413
2678
  const initialScrollTop = statefulStream(0);
2414
2679
  subscribe(
2415
2680
  pipe(
@@ -2421,11 +2686,12 @@ const initialScrollTopSystem = system(
2421
2686
  (location) => {
2422
2687
  handleNext(
2423
2688
  pipe(
2424
- totalListHeight,
2425
- filter((val) => val !== 0)
2689
+ listState,
2690
+ skip(1),
2691
+ filter((state) => state.items.length > 1)
2426
2692
  ),
2427
2693
  () => {
2428
- setTimeout(() => {
2694
+ requestAnimationFrame(() => {
2429
2695
  publish(scrollTo, location);
2430
2696
  });
2431
2697
  }
@@ -2436,7 +2702,7 @@ const initialScrollTopSystem = system(
2436
2702
  initialScrollTop
2437
2703
  };
2438
2704
  },
2439
- tup(totalListHeightSystem, propsReadySystem, domIOSystem),
2705
+ tup(propsReadySystem, domIOSystem, listStateSystem),
2440
2706
  { singleton: true }
2441
2707
  );
2442
2708
  const alignToBottomSystem = system(
@@ -2449,6 +2715,7 @@ const alignToBottomSystem = system(
2449
2715
  map(([, viewportHeight2, totalListHeight2]) => {
2450
2716
  return Math.max(0, viewportHeight2 - totalListHeight2);
2451
2717
  }),
2718
+ throttleTime(0),
2452
2719
  distinctUntilChanged()
2453
2720
  ),
2454
2721
  0
@@ -2491,13 +2758,31 @@ const windowScrollerSystem = system(([{ scrollTo, scrollContainerState }]) => {
2491
2758
  windowScrollTo
2492
2759
  );
2493
2760
  return {
2761
+ // config
2494
2762
  useWindowScroll,
2495
2763
  customScrollParent,
2764
+ // input
2496
2765
  windowScrollContainerState,
2497
2766
  windowViewportRect,
2767
+ // signals
2498
2768
  windowScrollTo
2499
2769
  };
2500
2770
  }, tup(domIOSystem));
2771
+ const defaultCalculateViewLocation = ({
2772
+ itemTop: itemTop2,
2773
+ itemBottom,
2774
+ viewportTop,
2775
+ viewportBottom,
2776
+ locationParams: { behavior, align, ...rest }
2777
+ }) => {
2778
+ if (itemTop2 < viewportTop) {
2779
+ return { ...rest, behavior, align: align != null ? align : "start" };
2780
+ }
2781
+ if (itemBottom > viewportBottom) {
2782
+ return { ...rest, behavior, align: align != null ? align : "end" };
2783
+ }
2784
+ return null;
2785
+ };
2501
2786
  const scrollIntoViewSystem = system(
2502
2787
  ([
2503
2788
  { sizes, totalCount, gap },
@@ -2511,24 +2796,27 @@ const scrollIntoViewSystem = system(
2511
2796
  withLatestFrom(sizes, viewportHeight, totalCount, headerHeight, fixedHeaderHeight, fixedFooterHeight, scrollTop),
2512
2797
  withLatestFrom(gap),
2513
2798
  map(([[viewLocation, sizes2, viewportHeight2, totalCount2, headerHeight2, fixedHeaderHeight2, fixedFooterHeight2, scrollTop2], gap2]) => {
2514
- const { done, behavior, align, ...rest } = viewLocation;
2515
- let location = null;
2799
+ const { done, behavior, align, calculateViewLocation = defaultCalculateViewLocation, ...rest } = viewLocation;
2516
2800
  const actualIndex = originalIndexFromLocation(viewLocation, sizes2, totalCount2 - 1);
2517
2801
  const itemTop2 = offsetOf(actualIndex, sizes2.offsetTree, gap2) + headerHeight2 + fixedHeaderHeight2;
2518
- if (itemTop2 < scrollTop2 + fixedHeaderHeight2) {
2519
- location = { ...rest, behavior, align: align ?? "start" };
2520
- } else {
2521
- const itemBottom = itemTop2 + findMaxKeyValue(sizes2.sizeTree, actualIndex)[1];
2522
- if (itemBottom > scrollTop2 + viewportHeight2 - fixedFooterHeight2) {
2523
- location = { ...rest, behavior, align: align ?? "end" };
2524
- }
2525
- }
2802
+ const itemBottom = itemTop2 + findMaxKeyValue(sizes2.sizeTree, actualIndex)[1];
2803
+ const viewportTop = scrollTop2 + fixedHeaderHeight2;
2804
+ const viewportBottom = scrollTop2 + viewportHeight2 - fixedFooterHeight2;
2805
+ const location = calculateViewLocation({
2806
+ itemTop: itemTop2,
2807
+ itemBottom,
2808
+ viewportTop,
2809
+ viewportBottom,
2810
+ locationParams: { behavior, align, ...rest }
2811
+ });
2526
2812
  if (location) {
2527
2813
  done && handleNext(
2528
2814
  pipe(
2529
2815
  scrollingInProgress,
2530
- skip(1),
2531
- filter((value) => value === false)
2816
+ filter((value) => value === false),
2817
+ // skips the initial publish of false, and the cleanup call.
2818
+ // but if scrollingInProgress is true, we skip the initial publish.
2819
+ skip(getValue(scrollingInProgress) ? 1 : 2)
2532
2820
  ),
2533
2821
  done
2534
2822
  );
@@ -2548,6 +2836,53 @@ const scrollIntoViewSystem = system(
2548
2836
  tup(sizeSystem, domIOSystem, scrollToIndexSystem, listStateSystem, loggerSystem),
2549
2837
  { singleton: true }
2550
2838
  );
2839
+ const stateLoadSystem = system(
2840
+ ([
2841
+ { sizes, sizeRanges },
2842
+ { scrollTop },
2843
+ { initialTopMostItemIndex },
2844
+ { didMount },
2845
+ { useWindowScroll, windowScrollContainerState, windowViewportRect }
2846
+ ]) => {
2847
+ const getState = stream();
2848
+ const restoreStateFrom = statefulStream(void 0);
2849
+ const statefulWindowScrollContainerState = statefulStream(null);
2850
+ const statefulWindowViewportRect = statefulStream(null);
2851
+ connect(windowScrollContainerState, statefulWindowScrollContainerState);
2852
+ connect(windowViewportRect, statefulWindowViewportRect);
2853
+ subscribe(
2854
+ pipe(getState, withLatestFrom(sizes, scrollTop, useWindowScroll, statefulWindowScrollContainerState, statefulWindowViewportRect)),
2855
+ ([callback, sizes2, scrollTop2, useWindowScroll2, windowScrollContainerState2, windowViewportRect2]) => {
2856
+ const ranges = sizeTreeToRanges(sizes2.sizeTree);
2857
+ if (useWindowScroll2 && windowScrollContainerState2 !== null && windowViewportRect2 !== null) {
2858
+ scrollTop2 = windowScrollContainerState2.scrollTop - windowViewportRect2.offsetTop;
2859
+ }
2860
+ callback({ ranges, scrollTop: scrollTop2 });
2861
+ }
2862
+ );
2863
+ connect(pipe(restoreStateFrom, filter(isDefined), map(locationFromSnapshot)), initialTopMostItemIndex);
2864
+ connect(
2865
+ pipe(
2866
+ didMount,
2867
+ withLatestFrom(restoreStateFrom),
2868
+ filter(([, state]) => state !== void 0),
2869
+ distinctUntilChanged(),
2870
+ map(([, snapshot]) => {
2871
+ return snapshot.ranges;
2872
+ })
2873
+ ),
2874
+ sizeRanges
2875
+ );
2876
+ return {
2877
+ getState,
2878
+ restoreStateFrom
2879
+ };
2880
+ },
2881
+ tup(sizeSystem, domIOSystem, initialTopMostItemIndexSystem, propsReadySystem, windowScrollerSystem)
2882
+ );
2883
+ function locationFromSnapshot(snapshot) {
2884
+ return { offset: snapshot.scrollTop, index: 0, align: "start" };
2885
+ }
2551
2886
  const featureGroup1System = system(
2552
2887
  ([
2553
2888
  sizeRange,
@@ -2558,7 +2893,8 @@ const featureGroup1System = system(
2558
2893
  initialScrollTopSystem2,
2559
2894
  alignToBottom,
2560
2895
  windowScroller,
2561
- scrollIntoView
2896
+ scrollIntoView,
2897
+ logger
2562
2898
  ]) => {
2563
2899
  return {
2564
2900
  ...sizeRange,
@@ -2569,7 +2905,8 @@ const featureGroup1System = system(
2569
2905
  ...initialScrollTopSystem2,
2570
2906
  ...alignToBottom,
2571
2907
  ...windowScroller,
2572
- ...scrollIntoView
2908
+ ...scrollIntoView,
2909
+ ...logger
2573
2910
  };
2574
2911
  },
2575
2912
  tup(
@@ -2581,7 +2918,8 @@ const featureGroup1System = system(
2581
2918
  initialScrollTopSystem,
2582
2919
  alignToBottomSystem,
2583
2920
  windowScrollerSystem,
2584
- scrollIntoViewSystem
2921
+ scrollIntoViewSystem,
2922
+ loggerSystem
2585
2923
  )
2586
2924
  );
2587
2925
  const listSystem = system(
@@ -2597,18 +2935,19 @@ const listSystem = system(
2597
2935
  firstItemIndex,
2598
2936
  groupIndices,
2599
2937
  statefulTotalCount,
2600
- gap
2938
+ gap,
2939
+ sizes
2601
2940
  },
2602
- { initialTopMostItemIndex, scrolledToInitialItem },
2941
+ { initialTopMostItemIndex, scrolledToInitialItem, initialItemFinalLocationReached },
2603
2942
  domIO,
2943
+ stateLoad,
2604
2944
  followOutput,
2605
2945
  { listState, topItemsIndexes, ...flags },
2606
2946
  { scrollToIndex },
2607
2947
  _,
2608
2948
  { topItemCount },
2609
2949
  { groupCounts },
2610
- featureGroup1,
2611
- log
2950
+ featureGroup1
2612
2951
  ]) => {
2613
2952
  connect(flags.rangeChanged, featureGroup1.scrollSeekRangeChanged);
2614
2953
  connect(
@@ -2619,12 +2958,14 @@ const listSystem = system(
2619
2958
  domIO.viewportHeight
2620
2959
  );
2621
2960
  return {
2961
+ // input
2622
2962
  totalCount,
2623
2963
  data,
2624
2964
  firstItemIndex,
2625
2965
  sizeRanges,
2626
2966
  initialTopMostItemIndex,
2627
2967
  scrolledToInitialItem,
2968
+ initialItemFinalLocationReached,
2628
2969
  topItemsIndexes,
2629
2970
  topItemCount,
2630
2971
  groupCounts,
@@ -2632,30 +2973,34 @@ const listSystem = system(
2632
2973
  defaultItemHeight: defaultItemSize,
2633
2974
  gap,
2634
2975
  ...followOutput,
2976
+ // output
2635
2977
  statefulTotalCount,
2636
2978
  listState,
2637
2979
  scrollToIndex,
2638
2980
  trackItemSizes,
2639
2981
  itemSize,
2640
2982
  groupIndices,
2983
+ // exported from stateFlagsSystem
2641
2984
  ...flags,
2985
+ // the bag of IO from featureGroup1System
2642
2986
  ...featureGroup1,
2643
2987
  ...domIO,
2644
- ...log
2988
+ sizes,
2989
+ ...stateLoad
2645
2990
  };
2646
2991
  },
2647
2992
  tup(
2648
2993
  sizeSystem,
2649
2994
  initialTopMostItemIndexSystem,
2650
2995
  domIOSystem,
2996
+ stateLoadSystem,
2651
2997
  followOutputSystem,
2652
2998
  listStateSystem,
2653
2999
  scrollToIndexSystem,
2654
3000
  upwardScrollFixSystem,
2655
3001
  topItemCountSystem,
2656
3002
  groupedListSystem,
2657
- featureGroup1System,
2658
- loggerSystem
3003
+ featureGroup1System
2659
3004
  )
2660
3005
  );
2661
3006
  const WEBKIT_STICKY = "-webkit-sticky";
@@ -2703,7 +3048,9 @@ function useWindowViewportRectRef(callback, customScrollParent) {
2703
3048
  React.useEffect(() => {
2704
3049
  if (customScrollParent) {
2705
3050
  customScrollParent.addEventListener("scroll", scrollAndResizeEventHandler);
2706
- const observer = new ResizeObserver(scrollAndResizeEventHandler);
3051
+ const observer = new ResizeObserver(() => {
3052
+ requestAnimationFrame(scrollAndResizeEventHandler);
3053
+ });
2707
3054
  observer.observe(customScrollParent);
2708
3055
  return () => {
2709
3056
  customScrollParent.removeEventListener("scroll", scrollAndResizeEventHandler);
@@ -2767,7 +3114,8 @@ const combinedSystem$2 = /* @__PURE__ */ system(([listSystem2, propsSystem]) =>
2767
3114
  }, tup(listSystem, listComponentPropsSystem));
2768
3115
  const DefaultScrollSeekPlaceholder$1 = ({ height }) => /* @__PURE__ */ React.createElement("div", { style: { height } });
2769
3116
  const GROUP_STYLE = { position: positionStickyCssValue(), zIndex: 1, overflowAnchor: "none" };
2770
- const ITEM_STYLE = { overflowAnchor: "none" };
3117
+ const ITEM_STYLE$1 = { overflowAnchor: "none" };
3118
+ const HORIZONTAL_ITEM_STYLE = { ...ITEM_STYLE$1, display: "inline-block", height: "100%" };
2771
3119
  const Items$1 = /* @__PURE__ */ React.memo(function VirtuosoItems({ showTopList = false }) {
2772
3120
  const listState = useEmitterValue$2("listState");
2773
3121
  const sizeRanges = usePublisher$2("sizeRanges");
@@ -2783,6 +3131,7 @@ const Items$1 = /* @__PURE__ */ React.memo(function VirtuosoItems({ showTopList
2783
3131
  const itemSize = useEmitterValue$2("itemSize");
2784
3132
  const log = useEmitterValue$2("log");
2785
3133
  const listGap = usePublisher$2("gap");
3134
+ const horizontalDirection = useEmitterValue$2("horizontalDirection");
2786
3135
  const { callbackRef } = useChangedListContentsSizes(
2787
3136
  sizeRanges,
2788
3137
  itemSize,
@@ -2790,7 +3139,8 @@ const Items$1 = /* @__PURE__ */ React.memo(function VirtuosoItems({ showTopList
2790
3139
  showTopList ? noop : scrollContainerStateCallback,
2791
3140
  log,
2792
3141
  listGap,
2793
- customScrollParent
3142
+ customScrollParent,
3143
+ horizontalDirection
2794
3144
  );
2795
3145
  const [deviation, setDeviation] = React.useState(0);
2796
3146
  useEmitter$2("deviation", (value) => {
@@ -2806,12 +3156,23 @@ const Items$1 = /* @__PURE__ */ React.memo(function VirtuosoItems({ showTopList
2806
3156
  const computeItemKey = useEmitterValue$2("computeItemKey");
2807
3157
  const isSeeking = useEmitterValue$2("isSeeking");
2808
3158
  const hasGroups2 = useEmitterValue$2("groupIndices").length > 0;
2809
- const paddingTopAddition = useEmitterValue$2("paddingTopAddition");
3159
+ const alignToBottom = useEmitterValue$2("alignToBottom");
3160
+ const initialItemFinalLocationReached = useEmitterValue$2("initialItemFinalLocationReached");
2810
3161
  const containerStyle = showTopList ? {} : {
2811
3162
  boxSizing: "border-box",
2812
- paddingTop: listState.offsetTop + paddingTopAddition,
2813
- paddingBottom: listState.offsetBottom,
2814
- marginTop: deviation
3163
+ ...horizontalDirection ? {
3164
+ whiteSpace: "nowrap",
3165
+ display: "inline-block",
3166
+ height: "100%",
3167
+ paddingLeft: listState.offsetTop,
3168
+ paddingRight: listState.offsetBottom,
3169
+ marginLeft: deviation !== 0 ? deviation : alignToBottom ? "auto" : 0
3170
+ } : {
3171
+ marginTop: deviation !== 0 ? deviation : alignToBottom ? "auto" : 0,
3172
+ paddingTop: listState.offsetTop,
3173
+ paddingBottom: listState.offsetBottom
3174
+ },
3175
+ ...initialItemFinalLocationReached ? {} : { visibility: "hidden" }
2815
3176
  };
2816
3177
  if (!showTopList && listState.totalCount === 0 && EmptyPlaceholder) {
2817
3178
  return React.createElement(EmptyPlaceholder, contextPropIfNotDomElement(EmptyPlaceholder, context));
@@ -2822,7 +3183,7 @@ const Items$1 = /* @__PURE__ */ React.memo(function VirtuosoItems({ showTopList
2822
3183
  ...contextPropIfNotDomElement(ListComponent, context),
2823
3184
  ref: callbackRef,
2824
3185
  style: containerStyle,
2825
- "data-test-id": showTopList ? "virtuoso-top-item-list" : "virtuoso-item-list"
3186
+ "data-testid": showTopList ? "virtuoso-top-item-list" : "virtuoso-item-list"
2826
3187
  },
2827
3188
  (showTopList ? listState.topItems : listState.items).map((item) => {
2828
3189
  const index = item.originalIndex;
@@ -2848,20 +3209,20 @@ const Items$1 = /* @__PURE__ */ React.memo(function VirtuosoItems({ showTopList
2848
3209
  "data-item-index": item.index,
2849
3210
  style: GROUP_STYLE
2850
3211
  },
2851
- groupContent(item.index)
3212
+ groupContent(item.index, context)
2852
3213
  );
2853
3214
  } else {
2854
3215
  return React.createElement(
2855
3216
  ItemComponent,
2856
3217
  {
2857
3218
  ...contextPropIfNotDomElement(ItemComponent, context),
3219
+ ...itemPropIfNotDomElement(ItemComponent, item.data),
2858
3220
  key,
2859
3221
  "data-index": index,
2860
3222
  "data-known-size": item.size,
2861
3223
  "data-item-index": item.index,
2862
3224
  "data-item-group-index": item.groupIndex,
2863
- item: item.data,
2864
- style: ITEM_STYLE
3225
+ style: horizontalDirection ? HORIZONTAL_ITEM_STYLE : ITEM_STYLE$1
2865
3226
  },
2866
3227
  hasGroups2 ? itemContent(item.index, item.groupIndex, item.data, context) : itemContent(item.index, item.data, context)
2867
3228
  );
@@ -2876,12 +3237,18 @@ const scrollerStyle = {
2876
3237
  position: "relative",
2877
3238
  WebkitOverflowScrolling: "touch"
2878
3239
  };
2879
- const viewportStyle = {
3240
+ const horizontalScrollerStyle = {
3241
+ outline: "none",
3242
+ overflowX: "auto",
3243
+ position: "relative"
3244
+ };
3245
+ const viewportStyle = (alignToBottom) => ({
2880
3246
  width: "100%",
2881
3247
  height: "100%",
2882
3248
  position: "absolute",
2883
- top: 0
2884
- };
3249
+ top: 0,
3250
+ ...alignToBottom ? { display: "flex", flexDirection: "column" } : {}
3251
+ });
2885
3252
  const topItemListStyle = {
2886
3253
  width: "100%",
2887
3254
  position: positionStickyCssValue(),
@@ -2894,6 +3261,9 @@ function contextPropIfNotDomElement(element, context) {
2894
3261
  }
2895
3262
  return { context };
2896
3263
  }
3264
+ function itemPropIfNotDomElement(element, item) {
3265
+ return { item: typeof element === "string" ? void 0 : item };
3266
+ }
2897
3267
  const Header$1 = /* @__PURE__ */ React.memo(function VirtuosoHeader() {
2898
3268
  const Header2 = useEmitterValue$2("HeaderComponent");
2899
3269
  const headerHeight = usePublisher$2("headerHeight");
@@ -2917,20 +3287,24 @@ function buildScroller({ usePublisher: usePublisher2, useEmitter: useEmitter2, u
2917
3287
  const smoothScrollTargetReached = usePublisher2("smoothScrollTargetReached");
2918
3288
  const scrollerRefCallback = useEmitterValue2("scrollerRef");
2919
3289
  const context = useEmitterValue2("context");
3290
+ const horizontalDirection = useEmitterValue2("horizontalDirection") || false;
2920
3291
  const { scrollerRef, scrollByCallback, scrollToCallback } = useScrollTop(
2921
3292
  scrollContainerStateCallback,
2922
3293
  smoothScrollTargetReached,
2923
3294
  ScrollerComponent,
2924
- scrollerRefCallback
3295
+ scrollerRefCallback,
3296
+ void 0,
3297
+ horizontalDirection
2925
3298
  );
2926
3299
  useEmitter2("scrollTo", scrollToCallback);
2927
3300
  useEmitter2("scrollBy", scrollByCallback);
3301
+ const defaultStyle = horizontalDirection ? horizontalScrollerStyle : scrollerStyle;
2928
3302
  return React.createElement(
2929
3303
  ScrollerComponent,
2930
3304
  {
2931
3305
  ref: scrollerRef,
2932
- style: { ...scrollerStyle, ...style },
2933
- "data-test-id": "virtuoso-scroller",
3306
+ style: { ...defaultStyle, ...style },
3307
+ "data-testid": "virtuoso-scroller",
2934
3308
  "data-virtuoso-scroller": true,
2935
3309
  tabIndex: 0,
2936
3310
  ...props,
@@ -2982,14 +3356,16 @@ const Viewport$2 = ({ children }) => {
2982
3356
  const ctx = React.useContext(VirtuosoMockContext);
2983
3357
  const viewportHeight = usePublisher$2("viewportHeight");
2984
3358
  const fixedItemHeight = usePublisher$2("fixedItemHeight");
2985
- const viewportRef = useSize(compose(viewportHeight, (el) => correctItemSize(el, "height")));
3359
+ const alignToBottom = useEmitterValue$2("alignToBottom");
3360
+ const horizontalDirection = useEmitterValue$2("horizontalDirection");
3361
+ const viewportRef = useSize(compose(viewportHeight, (el) => correctItemSize(el, horizontalDirection ? "width" : "height")));
2986
3362
  React.useEffect(() => {
2987
3363
  if (ctx) {
2988
3364
  viewportHeight(ctx.viewportHeight);
2989
3365
  fixedItemHeight(ctx.itemHeight);
2990
3366
  }
2991
3367
  }, [ctx, viewportHeight, fixedItemHeight]);
2992
- return /* @__PURE__ */ React.createElement("div", { style: viewportStyle, ref: viewportRef, "data-viewport-type": "element" }, children);
3368
+ return /* @__PURE__ */ React.createElement("div", { style: viewportStyle(alignToBottom), ref: viewportRef, "data-viewport-type": "element" }, children);
2993
3369
  };
2994
3370
  const WindowViewport$2 = ({ children }) => {
2995
3371
  const ctx = React.useContext(VirtuosoMockContext);
@@ -2997,20 +3373,21 @@ const WindowViewport$2 = ({ children }) => {
2997
3373
  const fixedItemHeight = usePublisher$2("fixedItemHeight");
2998
3374
  const customScrollParent = useEmitterValue$2("customScrollParent");
2999
3375
  const viewportRef = useWindowViewportRectRef(windowViewportRect, customScrollParent);
3376
+ const alignToBottom = useEmitterValue$2("alignToBottom");
3000
3377
  React.useEffect(() => {
3001
3378
  if (ctx) {
3002
3379
  fixedItemHeight(ctx.itemHeight);
3003
3380
  windowViewportRect({ offsetTop: 0, visibleHeight: ctx.viewportHeight, visibleWidth: 100 });
3004
3381
  }
3005
3382
  }, [ctx, windowViewportRect, fixedItemHeight]);
3006
- return /* @__PURE__ */ React.createElement("div", { ref: viewportRef, style: viewportStyle, "data-viewport-type": "window" }, children);
3383
+ return /* @__PURE__ */ React.createElement("div", { ref: viewportRef, style: viewportStyle(alignToBottom), "data-viewport-type": "window" }, children);
3007
3384
  };
3008
3385
  const TopItemListContainer = ({ children }) => {
3009
- const TopItemList = useEmitterValue$2("TopItemListComponent");
3386
+ const TopItemList = useEmitterValue$2("TopItemListComponent") || "div";
3010
3387
  const headerHeight = useEmitterValue$2("headerHeight");
3011
3388
  const style = { ...topItemListStyle, marginTop: `${headerHeight}px` };
3012
3389
  const context = useEmitterValue$2("context");
3013
- return React.createElement(TopItemList || "div", { style, context }, children);
3390
+ return React.createElement(TopItemList, { style, ...contextPropIfNotDomElement(TopItemList, context) }, children);
3014
3391
  };
3015
3392
  const ListRoot = /* @__PURE__ */ React.memo(function VirtuosoRoot(props) {
3016
3393
  const useWindowScroll = useEmitterValue$2("useWindowScroll");
@@ -3030,18 +3407,19 @@ const {
3030
3407
  {
3031
3408
  required: {},
3032
3409
  optional: {
3410
+ restoreStateFrom: "restoreStateFrom",
3033
3411
  context: "context",
3034
3412
  followOutput: "followOutput",
3035
- firstItemIndex: "firstItemIndex",
3036
3413
  itemContent: "itemContent",
3037
3414
  groupContent: "groupContent",
3038
3415
  overscan: "overscan",
3039
3416
  increaseViewportBy: "increaseViewportBy",
3040
3417
  totalCount: "totalCount",
3418
+ groupCounts: "groupCounts",
3041
3419
  topItemCount: "topItemCount",
3420
+ firstItemIndex: "firstItemIndex",
3042
3421
  initialTopMostItemIndex: "initialTopMostItemIndex",
3043
3422
  components: "components",
3044
- groupCounts: "groupCounts",
3045
3423
  atBottomThreshold: "atBottomThreshold",
3046
3424
  atTopThreshold: "atTopThreshold",
3047
3425
  computeItemKey: "computeItemKey",
@@ -3057,14 +3435,16 @@ const {
3057
3435
  useWindowScroll: "useWindowScroll",
3058
3436
  customScrollParent: "customScrollParent",
3059
3437
  scrollerRef: "scrollerRef",
3060
- logLevel: "logLevel"
3438
+ logLevel: "logLevel",
3439
+ horizontalDirection: "horizontalDirection"
3061
3440
  },
3062
3441
  methods: {
3063
3442
  scrollToIndex: "scrollToIndex",
3064
3443
  scrollIntoView: "scrollIntoView",
3065
3444
  scrollTo: "scrollTo",
3066
3445
  scrollBy: "scrollBy",
3067
- autoscrollToBottom: "autoscrollToBottom"
3446
+ autoscrollToBottom: "autoscrollToBottom",
3447
+ getState: "getState"
3068
3448
  },
3069
3449
  events: {
3070
3450
  isScrolling: "isScrolling",
@@ -3110,13 +3490,17 @@ function buildProbeGridState(items) {
3110
3490
  };
3111
3491
  }
3112
3492
  function buildItems(startIndex, endIndex, data) {
3113
- return Array.from({ length: endIndex - startIndex + 1 }).map(
3114
- (_, i) => ({ index: i + startIndex, data: data == null ? void 0 : data[i + startIndex] })
3115
- );
3493
+ return Array.from({ length: endIndex - startIndex + 1 }).map((_, i) => {
3494
+ const dataItem = data === null ? null : data[i + startIndex];
3495
+ return { index: i + startIndex, data: dataItem };
3496
+ });
3116
3497
  }
3117
3498
  function gapComparator(prev, next) {
3118
3499
  return prev && prev.column === next.column && prev.row === next.row;
3119
3500
  }
3501
+ function dimensionComparator(prev, next) {
3502
+ return prev && prev.width === next.width && prev.height === next.height;
3503
+ }
3120
3504
  const gridSystem = /* @__PURE__ */ system(
3121
3505
  ([
3122
3506
  { overscan, visibleRange, listBoundary },
@@ -3124,7 +3508,7 @@ const gridSystem = /* @__PURE__ */ system(
3124
3508
  stateFlags,
3125
3509
  scrollSeek,
3126
3510
  { propsReady, didMount },
3127
- { windowViewportRect, windowScrollTo, useWindowScroll, customScrollParent, windowScrollContainerState },
3511
+ { windowViewportRect, useWindowScroll, customScrollParent, windowScrollContainerState, windowScrollTo },
3128
3512
  log
3129
3513
  ]) => {
3130
3514
  const totalCount = statefulStream(0);
@@ -3135,25 +3519,96 @@ const gridSystem = /* @__PURE__ */ system(
3135
3519
  const scrollToIndex = stream();
3136
3520
  const scrollHeight = stream();
3137
3521
  const deviation = statefulStream(0);
3138
- const data = statefulStream(void 0);
3522
+ const data = statefulStream(null);
3139
3523
  const gap = statefulStream({ row: 0, column: 0 });
3140
- connect(
3524
+ const stateChanged = stream();
3525
+ const restoreStateFrom = stream();
3526
+ const stateRestoreInProgress = statefulStream(false);
3527
+ const initialTopMostItemIndex = statefulStream(0);
3528
+ const scrolledToInitialItem = statefulStream(true);
3529
+ const scrollScheduled = statefulStream(false);
3530
+ const horizontalDirection = statefulStream(false);
3531
+ subscribe(
3141
3532
  pipe(
3142
- combineLatest(didMount, initialItemCount, data),
3143
- filter(([, count]) => count !== 0),
3144
- map(([, count, data2]) => {
3145
- return {
3146
- items: buildItems(0, count - 1, data2),
3147
- top: 0,
3148
- bottom: 0,
3149
- offsetBottom: 0,
3150
- offsetTop: 0,
3151
- itemHeight: 0,
3152
- itemWidth: 0
3153
- };
3533
+ didMount,
3534
+ withLatestFrom(initialTopMostItemIndex),
3535
+ filter(([_, location]) => !!location)
3536
+ ),
3537
+ () => {
3538
+ publish(scrolledToInitialItem, false);
3539
+ publish(initialItemCount, 0);
3540
+ }
3541
+ );
3542
+ subscribe(
3543
+ pipe(
3544
+ combineLatest(didMount, scrolledToInitialItem, itemDimensions, viewportDimensions, initialTopMostItemIndex, scrollScheduled),
3545
+ filter(([didMount2, scrolledToInitialItem2, itemDimensions2, viewportDimensions2, , scrollScheduled2]) => {
3546
+ return didMount2 && !scrolledToInitialItem2 && itemDimensions2.height !== 0 && viewportDimensions2.height !== 0 && !scrollScheduled2;
3154
3547
  })
3155
3548
  ),
3156
- gridState
3549
+ ([, , , , initialTopMostItemIndex2]) => {
3550
+ publish(scrollScheduled, true);
3551
+ skipFrames(1, () => {
3552
+ publish(scrollToIndex, initialTopMostItemIndex2);
3553
+ });
3554
+ handleNext(pipe(scrollTop), () => {
3555
+ publish(listBoundary, [0, 0]);
3556
+ publish(scrolledToInitialItem, true);
3557
+ });
3558
+ }
3559
+ );
3560
+ connect(
3561
+ pipe(
3562
+ restoreStateFrom,
3563
+ filter((value) => value !== void 0 && value !== null && value.scrollTop > 0),
3564
+ mapTo(0)
3565
+ ),
3566
+ initialItemCount
3567
+ );
3568
+ subscribe(
3569
+ pipe(
3570
+ didMount,
3571
+ withLatestFrom(restoreStateFrom),
3572
+ filter(([, snapshot]) => snapshot !== void 0 && snapshot !== null)
3573
+ ),
3574
+ ([, snapshot]) => {
3575
+ if (!snapshot) {
3576
+ return;
3577
+ }
3578
+ publish(viewportDimensions, snapshot.viewport), publish(itemDimensions, snapshot == null ? void 0 : snapshot.item);
3579
+ publish(gap, snapshot.gap);
3580
+ if (snapshot.scrollTop > 0) {
3581
+ publish(stateRestoreInProgress, true);
3582
+ handleNext(pipe(scrollTop, skip(1)), (_value) => {
3583
+ publish(stateRestoreInProgress, false);
3584
+ });
3585
+ publish(scrollTo, { top: snapshot.scrollTop });
3586
+ }
3587
+ }
3588
+ );
3589
+ connect(
3590
+ pipe(
3591
+ viewportDimensions,
3592
+ map(({ height }) => height)
3593
+ ),
3594
+ viewportHeight
3595
+ );
3596
+ connect(
3597
+ pipe(
3598
+ combineLatest(
3599
+ duc(viewportDimensions, dimensionComparator),
3600
+ duc(itemDimensions, dimensionComparator),
3601
+ duc(gap, (prev, next) => prev && prev.column === next.column && prev.row === next.row),
3602
+ duc(scrollTop)
3603
+ ),
3604
+ map(([viewport, item, gap2, scrollTop2]) => ({
3605
+ viewport,
3606
+ item,
3607
+ gap: gap2,
3608
+ scrollTop: scrollTop2
3609
+ }))
3610
+ ),
3611
+ stateChanged
3157
3612
  );
3158
3613
  connect(
3159
3614
  pipe(
@@ -3161,67 +3616,106 @@ const gridSystem = /* @__PURE__ */ system(
3161
3616
  duc(totalCount),
3162
3617
  visibleRange,
3163
3618
  duc(gap, gapComparator),
3164
- duc(itemDimensions, (prev, next) => prev && prev.width === next.width && prev.height === next.height),
3165
- data
3619
+ duc(itemDimensions, dimensionComparator),
3620
+ duc(viewportDimensions, dimensionComparator),
3621
+ duc(data),
3622
+ duc(initialItemCount),
3623
+ duc(stateRestoreInProgress),
3624
+ duc(scrolledToInitialItem),
3625
+ duc(initialTopMostItemIndex)
3166
3626
  ),
3167
- withLatestFrom(viewportDimensions),
3168
- map(([[totalCount2, [startOffset, endOffset], gap2, item, data2], viewport]) => {
3169
- const { row: rowGap, column: columnGap } = gap2;
3170
- const { height: itemHeight, width: itemWidth } = item;
3171
- const { width: viewportWidth } = viewport;
3172
- if (totalCount2 === 0 || viewportWidth === 0) {
3173
- return INITIAL_GRID_STATE;
3174
- }
3175
- if (itemWidth === 0) {
3176
- return buildProbeGridState(buildItems(0, 0, data2));
3627
+ filter(([, , , , , , , stateRestoreInProgress2]) => {
3628
+ return !stateRestoreInProgress2;
3629
+ }),
3630
+ map(
3631
+ ([
3632
+ totalCount2,
3633
+ [startOffset, endOffset],
3634
+ gap2,
3635
+ item,
3636
+ viewport,
3637
+ data2,
3638
+ initialItemCount2,
3639
+ ,
3640
+ scrolledToInitialItem2,
3641
+ initialTopMostItemIndex2
3642
+ ]) => {
3643
+ const { row: rowGap, column: columnGap } = gap2;
3644
+ const { height: itemHeight, width: itemWidth } = item;
3645
+ const { width: viewportWidth } = viewport;
3646
+ if (initialItemCount2 === 0 && (totalCount2 === 0 || viewportWidth === 0)) {
3647
+ return INITIAL_GRID_STATE;
3648
+ }
3649
+ if (itemWidth === 0) {
3650
+ const startIndex2 = getInitialTopMostItemIndexNumber(initialTopMostItemIndex2, totalCount2);
3651
+ const endIndex2 = startIndex2 === 0 ? Math.max(initialItemCount2 - 1, 0) : startIndex2;
3652
+ return buildProbeGridState(buildItems(startIndex2, endIndex2, data2));
3653
+ }
3654
+ const perRow = itemsPerRow(viewportWidth, itemWidth, columnGap);
3655
+ let startIndex;
3656
+ let endIndex;
3657
+ if (!scrolledToInitialItem2) {
3658
+ startIndex = 0;
3659
+ endIndex = -1;
3660
+ } else if (startOffset === 0 && endOffset === 0 && initialItemCount2 > 0) {
3661
+ startIndex = 0;
3662
+ endIndex = initialItemCount2 - 1;
3663
+ } else {
3664
+ startIndex = perRow * floor((startOffset + rowGap) / (itemHeight + rowGap));
3665
+ endIndex = perRow * ceil((endOffset + rowGap) / (itemHeight + rowGap)) - 1;
3666
+ endIndex = min(totalCount2 - 1, max(endIndex, perRow - 1));
3667
+ startIndex = min(endIndex, max(0, startIndex));
3668
+ }
3669
+ const items = buildItems(startIndex, endIndex, data2);
3670
+ const { top, bottom } = gridLayout(viewport, gap2, item, items);
3671
+ const rowCount = ceil(totalCount2 / perRow);
3672
+ const totalHeight = rowCount * itemHeight + (rowCount - 1) * rowGap;
3673
+ const offsetBottom = totalHeight - bottom;
3674
+ return { items, offsetTop: top, offsetBottom, top, bottom, itemHeight, itemWidth };
3177
3675
  }
3178
- const perRow = itemsPerRow(viewportWidth, itemWidth, columnGap);
3179
- let startIndex = perRow * floor((startOffset + rowGap) / (itemHeight + rowGap));
3180
- let endIndex = perRow * ceil((endOffset + rowGap) / (itemHeight + rowGap)) - 1;
3181
- endIndex = min(totalCount2 - 1, max(endIndex, perRow - 1));
3182
- startIndex = min(endIndex, max(0, startIndex));
3183
- const items = buildItems(startIndex, endIndex, data2);
3184
- const { top, bottom } = gridLayout(viewport, gap2, item, items);
3185
- const rowCount = ceil(totalCount2 / perRow);
3186
- const totalHeight = rowCount * itemHeight + (rowCount - 1) * rowGap;
3187
- const offsetBottom = totalHeight - bottom;
3188
- return { items, offsetTop: top, offsetBottom, top, bottom, itemHeight, itemWidth };
3189
- })
3676
+ )
3190
3677
  ),
3191
3678
  gridState
3192
3679
  );
3193
3680
  connect(
3194
3681
  pipe(
3195
3682
  data,
3196
- filter((data2) => data2 !== void 0),
3683
+ filter((data2) => data2 !== null),
3197
3684
  map((data2) => data2.length)
3198
3685
  ),
3199
3686
  totalCount
3200
3687
  );
3201
- connect(
3202
- pipe(
3203
- viewportDimensions,
3204
- map(({ height }) => height)
3205
- ),
3206
- viewportHeight
3207
- );
3208
3688
  connect(
3209
3689
  pipe(
3210
3690
  combineLatest(viewportDimensions, itemDimensions, gridState, gap),
3211
- map(([viewportDimensions2, item, { items }, gap2]) => {
3212
- const { top, bottom } = gridLayout(viewportDimensions2, gap2, item, items);
3691
+ filter(([viewportDimensions2, itemDimensions2, { items }]) => {
3692
+ return items.length > 0 && itemDimensions2.height !== 0 && viewportDimensions2.height !== 0;
3693
+ }),
3694
+ map(([viewportDimensions2, itemDimensions2, { items }, gap2]) => {
3695
+ const { top, bottom } = gridLayout(viewportDimensions2, gap2, itemDimensions2, items);
3213
3696
  return [top, bottom];
3214
3697
  }),
3215
3698
  distinctUntilChanged(tupleComparator)
3216
3699
  ),
3217
3700
  listBoundary
3218
3701
  );
3702
+ const hasScrolled = statefulStream(false);
3703
+ connect(
3704
+ pipe(
3705
+ scrollTop,
3706
+ withLatestFrom(hasScrolled),
3707
+ map(([scrollTop2, hasScrolled2]) => {
3708
+ return hasScrolled2 || scrollTop2 !== 0;
3709
+ })
3710
+ ),
3711
+ hasScrolled
3712
+ );
3219
3713
  const endReached = streamFromEmitter(
3220
3714
  pipe(
3221
3715
  duc(gridState),
3222
3716
  filter(({ items }) => items.length > 0),
3223
- withLatestFrom(totalCount),
3224
- filter(([{ items }, totalCount2]) => items[items.length - 1].index === totalCount2 - 1),
3717
+ withLatestFrom(totalCount, hasScrolled),
3718
+ filter(([{ items }, totalCount2, hasScrolled2]) => hasScrolled2 && items[items.length - 1].index === totalCount2 - 1),
3225
3719
  map(([, totalCount2]) => totalCount2 - 1),
3226
3720
  distinctUntilChanged()
3227
3721
  )
@@ -3232,6 +3726,7 @@ const gridSystem = /* @__PURE__ */ system(
3232
3726
  filter(({ items }) => {
3233
3727
  return items.length > 0 && items[0].index === 0;
3234
3728
  }),
3729
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
3235
3730
  mapTo(0),
3236
3731
  distinctUntilChanged()
3237
3732
  )
@@ -3239,14 +3734,16 @@ const gridSystem = /* @__PURE__ */ system(
3239
3734
  const rangeChanged = streamFromEmitter(
3240
3735
  pipe(
3241
3736
  duc(gridState),
3242
- filter(({ items }) => items.length > 0),
3243
- map(({ items }) => {
3737
+ withLatestFrom(stateRestoreInProgress),
3738
+ filter(([{ items }, stateRestoreInProgress2]) => items.length > 0 && !stateRestoreInProgress2),
3739
+ map(([{ items }]) => {
3244
3740
  return {
3245
3741
  startIndex: items[0].index,
3246
3742
  endIndex: items[items.length - 1].index
3247
3743
  };
3248
3744
  }),
3249
- distinctUntilChanged(rangeComparator)
3745
+ distinctUntilChanged(rangeComparator),
3746
+ throttleTime(0)
3250
3747
  )
3251
3748
  );
3252
3749
  connect(rangeChanged, scrollSeek.scrollSeekRangeChanged);
@@ -3254,7 +3751,7 @@ const gridSystem = /* @__PURE__ */ system(
3254
3751
  pipe(
3255
3752
  scrollToIndex,
3256
3753
  withLatestFrom(viewportDimensions, itemDimensions, totalCount, gap),
3257
- map(([location, viewport, item, totalCount2, gap2]) => {
3754
+ map(([location, viewportDimensions2, itemDimensions2, totalCount2, gap2]) => {
3258
3755
  const normalLocation = normalizeIndexLocation(location);
3259
3756
  const { align, behavior, offset } = normalLocation;
3260
3757
  let index = normalLocation.index;
@@ -3262,11 +3759,11 @@ const gridSystem = /* @__PURE__ */ system(
3262
3759
  index = totalCount2 - 1;
3263
3760
  }
3264
3761
  index = max(0, index, min(totalCount2 - 1, index));
3265
- let top = itemTop(viewport, gap2, item, index);
3762
+ let top = itemTop(viewportDimensions2, gap2, itemDimensions2, index);
3266
3763
  if (align === "end") {
3267
- top = round(top - viewport.height + item.height);
3764
+ top = round(top - viewportDimensions2.height + itemDimensions2.height);
3268
3765
  } else if (align === "center") {
3269
- top = round(top - viewport.height / 2 + item.height / 2);
3766
+ top = round(top - viewportDimensions2.height / 2 + itemDimensions2.height / 2);
3270
3767
  }
3271
3768
  if (offset) {
3272
3769
  top += offset;
@@ -3293,6 +3790,7 @@ const gridSystem = /* @__PURE__ */ system(
3293
3790
  viewportDimensions
3294
3791
  );
3295
3792
  return {
3793
+ // input
3296
3794
  data,
3297
3795
  totalCount,
3298
3796
  viewportDimensions,
@@ -3315,14 +3813,20 @@ const gridSystem = /* @__PURE__ */ system(
3315
3813
  headerHeight,
3316
3814
  initialItemCount,
3317
3815
  gap,
3816
+ restoreStateFrom,
3318
3817
  ...scrollSeek,
3818
+ initialTopMostItemIndex,
3819
+ horizontalDirection,
3820
+ // output
3319
3821
  gridState,
3320
3822
  totalListHeight,
3321
3823
  ...stateFlags,
3322
3824
  startReached,
3323
3825
  endReached,
3324
3826
  rangeChanged,
3827
+ stateChanged,
3325
3828
  propsReady,
3829
+ stateRestoreInProgress,
3326
3830
  ...log
3327
3831
  };
3328
3832
  },
@@ -3344,7 +3848,7 @@ function itemTop(viewport, gap, item, index) {
3344
3848
  return top > 0 ? top + gap.row : top;
3345
3849
  }
3346
3850
  function itemsPerRow(viewportWidth, itemWidth, gap) {
3347
- return max(1, floor((viewportWidth + gap) / (itemWidth + gap)));
3851
+ return max(1, floor((viewportWidth + gap) / (floor(itemWidth) + gap)));
3348
3852
  }
3349
3853
  const gridComponentPropsSystem = /* @__PURE__ */ system(() => {
3350
3854
  const itemContent = statefulStream((index) => `Item ${index}`);
@@ -3400,18 +3904,23 @@ const GridItems = /* @__PURE__ */ React.memo(function GridItems2() {
3400
3904
  const itemDimensions = usePublisher$1("itemDimensions");
3401
3905
  const gridGap = usePublisher$1("gap");
3402
3906
  const log = useEmitterValue$1("log");
3907
+ const stateRestoreInProgress = useEmitterValue$1("stateRestoreInProgress");
3403
3908
  const listRef = useSize((el) => {
3404
3909
  const scrollHeight = el.parentElement.parentElement.scrollHeight;
3405
3910
  scrollHeightCallback(scrollHeight);
3406
3911
  const firstItem = el.firstChild;
3407
3912
  if (firstItem) {
3408
- itemDimensions(firstItem.getBoundingClientRect());
3913
+ const { width, height } = firstItem.getBoundingClientRect();
3914
+ itemDimensions({ width, height });
3409
3915
  }
3410
3916
  gridGap({
3411
3917
  row: resolveGapValue("row-gap", getComputedStyle(el).rowGap, log),
3412
3918
  column: resolveGapValue("column-gap", getComputedStyle(el).columnGap, log)
3413
3919
  });
3414
3920
  });
3921
+ if (stateRestoreInProgress) {
3922
+ return null;
3923
+ }
3415
3924
  return React.createElement(
3416
3925
  ListComponent,
3417
3926
  {
@@ -3419,7 +3928,7 @@ const GridItems = /* @__PURE__ */ React.memo(function GridItems2() {
3419
3928
  className: listClassName,
3420
3929
  ...contextPropIfNotDomElement(ListComponent, context),
3421
3930
  style: { paddingTop: gridState.offsetTop, paddingBottom: gridState.offsetBottom },
3422
- "data-test-id": "virtuoso-item-list"
3931
+ "data-testid": "virtuoso-item-list"
3423
3932
  },
3424
3933
  gridState.items.map((item) => {
3425
3934
  const key = computeItemKey(item.index, item.data, context);
@@ -3466,7 +3975,7 @@ const Viewport$1 = ({ children }) => {
3466
3975
  itemDimensions({ height: ctx.itemHeight, width: ctx.itemWidth });
3467
3976
  }
3468
3977
  }, [ctx, viewportDimensions, itemDimensions]);
3469
- return /* @__PURE__ */ React.createElement("div", { style: viewportStyle, ref: viewportRef }, children);
3978
+ return /* @__PURE__ */ React.createElement("div", { style: viewportStyle(false), ref: viewportRef }, children);
3470
3979
  };
3471
3980
  const WindowViewport$1 = ({ children }) => {
3472
3981
  const ctx = React.useContext(VirtuosoGridMockContext);
@@ -3480,7 +3989,7 @@ const WindowViewport$1 = ({ children }) => {
3480
3989
  windowViewportRect({ offsetTop: 0, visibleHeight: ctx.viewportHeight, visibleWidth: ctx.viewportWidth });
3481
3990
  }
3482
3991
  }, [ctx, windowViewportRect, itemDimensions]);
3483
- return /* @__PURE__ */ React.createElement("div", { ref: viewportRef, style: viewportStyle }, children);
3992
+ return /* @__PURE__ */ React.createElement("div", { ref: viewportRef, style: viewportStyle(false) }, children);
3484
3993
  };
3485
3994
  const GridRoot = /* @__PURE__ */ React.memo(function GridRoot2({ ...props }) {
3486
3995
  const useWindowScroll = useEmitterValue$1("useWindowScroll");
@@ -3512,7 +4021,10 @@ const {
3512
4021
  itemClassName: "itemClassName",
3513
4022
  useWindowScroll: "useWindowScroll",
3514
4023
  customScrollParent: "customScrollParent",
3515
- scrollerRef: "scrollerRef"
4024
+ scrollerRef: "scrollerRef",
4025
+ logLevel: "logLevel",
4026
+ restoreStateFrom: "restoreStateFrom",
4027
+ initialTopMostItemIndex: "initialTopMostItemIndex"
3516
4028
  },
3517
4029
  methods: {
3518
4030
  scrollTo: "scrollTo",
@@ -3525,7 +4037,8 @@ const {
3525
4037
  startReached: "startReached",
3526
4038
  rangeChanged: "rangeChanged",
3527
4039
  atBottomStateChange: "atBottomStateChange",
3528
- atTopStateChange: "atTopStateChange"
4040
+ atTopStateChange: "atTopStateChange",
4041
+ stateChanged: "stateChanged"
3529
4042
  }
3530
4043
  },
3531
4044
  GridRoot
@@ -3539,7 +4052,7 @@ function resolveGapValue(property, value, log) {
3539
4052
  if (value === "normal") {
3540
4053
  return 0;
3541
4054
  }
3542
- return parseInt(value ?? "0", 10);
4055
+ return parseInt(value != null ? value : "0", 10);
3543
4056
  }
3544
4057
  const VirtuosoGrid = Grid;
3545
4058
  const tableComponentPropsSystem = /* @__PURE__ */ system(() => {
@@ -3584,6 +4097,7 @@ const combinedSystem = /* @__PURE__ */ system(([listSystem2, propsSystem]) => {
3584
4097
  }, tup(listSystem, tableComponentPropsSystem));
3585
4098
  const DefaultScrollSeekPlaceholder = ({ height }) => /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { style: { height } }));
3586
4099
  const DefaultFillerRow = ({ height }) => /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { style: { height, padding: 0, border: 0 } }));
4100
+ const ITEM_STYLE = { overflowAnchor: "none" };
3587
4101
  const Items = /* @__PURE__ */ React.memo(function VirtuosoItems2() {
3588
4102
  const listState = useEmitterValue("listState");
3589
4103
  const sizeRanges = usePublisher("sizeRanges");
@@ -3646,19 +4160,19 @@ const Items = /* @__PURE__ */ React.memo(function VirtuosoItems2() {
3646
4160
  TableRowComponent,
3647
4161
  {
3648
4162
  ...contextPropIfNotDomElement(TableRowComponent, context),
4163
+ ...itemPropIfNotDomElement(TableRowComponent, item.data),
3649
4164
  key,
3650
4165
  "data-index": index,
3651
4166
  "data-known-size": item.size,
3652
4167
  "data-item-index": item.index,
3653
- item: item.data,
3654
- style: { overflowAnchor: "none" }
4168
+ style: ITEM_STYLE
3655
4169
  },
3656
4170
  itemContent(item.index, item.data, context)
3657
4171
  );
3658
4172
  });
3659
4173
  return React.createElement(
3660
4174
  TableBodyComponent,
3661
- { ref: callbackRef, "data-test-id": "virtuoso-item-list", ...contextPropIfNotDomElement(TableBodyComponent, context) },
4175
+ { ref: callbackRef, "data-testid": "virtuoso-item-list", ...contextPropIfNotDomElement(TableBodyComponent, context) },
3662
4176
  [paddingTopEl, ...items, paddingBottomEl]
3663
4177
  );
3664
4178
  });
@@ -3673,7 +4187,7 @@ const Viewport = ({ children }) => {
3673
4187
  fixedItemHeight(ctx.itemHeight);
3674
4188
  }
3675
4189
  }, [ctx, viewportHeight, fixedItemHeight]);
3676
- return /* @__PURE__ */ React.createElement("div", { style: viewportStyle, ref: viewportRef, "data-viewport-type": "element" }, children);
4190
+ return /* @__PURE__ */ React.createElement("div", { style: viewportStyle(false), ref: viewportRef, "data-viewport-type": "element" }, children);
3677
4191
  };
3678
4192
  const WindowViewport = ({ children }) => {
3679
4193
  const ctx = React.useContext(VirtuosoMockContext);
@@ -3687,7 +4201,7 @@ const WindowViewport = ({ children }) => {
3687
4201
  windowViewportRect({ offsetTop: 0, visibleHeight: ctx.viewportHeight, visibleWidth: 100 });
3688
4202
  }
3689
4203
  }, [ctx, windowViewportRect, fixedItemHeight]);
3690
- return /* @__PURE__ */ React.createElement("div", { ref: viewportRef, style: viewportStyle, "data-viewport-type": "window" }, children);
4204
+ return /* @__PURE__ */ React.createElement("div", { ref: viewportRef, style: viewportStyle(false), "data-viewport-type": "window" }, children);
3691
4205
  };
3692
4206
  const TableRoot = /* @__PURE__ */ React.memo(function TableVirtuosoRoot(props) {
3693
4207
  const useWindowScroll = useEmitterValue("useWindowScroll");
@@ -3708,7 +4222,7 @@ const TableRoot = /* @__PURE__ */ React.memo(function TableVirtuosoRoot(props) {
3708
4222
  TheTHead,
3709
4223
  {
3710
4224
  key: "TableHead",
3711
- style: { zIndex: 1, position: "sticky", top: 0 },
4225
+ style: { zIndex: 2, position: "sticky", top: 0 },
3712
4226
  ref: theadRef,
3713
4227
  ...contextPropIfNotDomElement(TheTHead, context)
3714
4228
  },
@@ -3724,11 +4238,11 @@ const TableRoot = /* @__PURE__ */ React.memo(function TableVirtuosoRoot(props) {
3724
4238
  },
3725
4239
  fixedFooterContent()
3726
4240
  ) : null;
3727
- return /* @__PURE__ */ React.createElement(TheScroller, { ...props }, /* @__PURE__ */ React.createElement(TheViewport, null, React.createElement(TheTable, { style: { borderSpacing: 0 }, ...contextPropIfNotDomElement(TheTable, context) }, [
3728
- theHead,
3729
- /* @__PURE__ */ React.createElement(Items, { key: "TableBody" }),
3730
- theFoot
3731
- ])));
4241
+ return /* @__PURE__ */ React.createElement(TheScroller, { ...props }, /* @__PURE__ */ React.createElement(TheViewport, null, React.createElement(
4242
+ TheTable,
4243
+ { style: { borderSpacing: 0, overflowAnchor: "none" }, ...contextPropIfNotDomElement(TheTable, context) },
4244
+ [theHead, /* @__PURE__ */ React.createElement(Items, { key: "TableBody" }), theFoot]
4245
+ )));
3732
4246
  });
3733
4247
  const {
3734
4248
  Component: Table,
@@ -3740,6 +4254,7 @@ const {
3740
4254
  {
3741
4255
  required: {},
3742
4256
  optional: {
4257
+ restoreStateFrom: "restoreStateFrom",
3743
4258
  context: "context",
3744
4259
  followOutput: "followOutput",
3745
4260
  firstItemIndex: "firstItemIndex",
@@ -3773,7 +4288,8 @@ const {
3773
4288
  scrollToIndex: "scrollToIndex",
3774
4289
  scrollIntoView: "scrollIntoView",
3775
4290
  scrollTo: "scrollTo",
3776
- scrollBy: "scrollBy"
4291
+ scrollBy: "scrollBy",
4292
+ getState: "getState"
3777
4293
  },
3778
4294
  events: {
3779
4295
  isScrolling: "isScrolling",