@legendapp/list 2.1.0-beta.5 → 2.1.0-beta.6

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/index.d.mts CHANGED
@@ -379,6 +379,7 @@ interface InternalState {
379
379
  stickyContainers: Map<number, number>;
380
380
  stickyContainerPool: Set<number>;
381
381
  scrollProcessingEnabled: boolean;
382
+ isOptimizingItemPositions: boolean;
382
383
  props: {
383
384
  alignItemsAtEnd: boolean;
384
385
  data: readonly any[];
package/index.d.ts CHANGED
@@ -379,6 +379,7 @@ interface InternalState {
379
379
  stickyContainers: Map<number, number>;
380
380
  stickyContainerPool: Set<number>;
381
381
  scrollProcessingEnabled: boolean;
382
+ isOptimizingItemPositions: boolean;
382
383
  props: {
383
384
  alignItemsAtEnd: boolean;
384
385
  data: readonly any[];
package/index.js CHANGED
@@ -442,6 +442,10 @@ function getGlobalResizeObserver() {
442
442
  }
443
443
  var callbackMap = /* @__PURE__ */ new WeakMap();
444
444
  function createResizeObserver(element, callback) {
445
+ if (typeof ResizeObserver === "undefined") {
446
+ return () => {
447
+ };
448
+ }
445
449
  if (!element) {
446
450
  return () => {
447
451
  };
@@ -477,7 +481,7 @@ function useOnLayoutSync({
477
481
  const current = ref.current;
478
482
  const scrollableNode = (_b = (_a3 = current == null ? void 0 : current.getScrollableNode) == null ? void 0 : _a3.call(current)) != null ? _b : null;
479
483
  const element = scrollableNode || current;
480
- if (!element || !(element instanceof HTMLElement)) {
484
+ if (!element) {
481
485
  return;
482
486
  }
483
487
  const emit = (layout, fromLayoutEffect) => {
@@ -499,6 +503,9 @@ function useOnLayoutSync({
499
503
  return {};
500
504
  }
501
505
  function toLayout(rect) {
506
+ if (!rect) {
507
+ return { height: 0, width: 0, x: 0, y: 0 };
508
+ }
502
509
  return {
503
510
  height: rect.height,
504
511
  width: rect.width,
@@ -1189,6 +1196,94 @@ function calculateOffsetForIndex(ctx, state, index) {
1189
1196
  return position;
1190
1197
  }
1191
1198
 
1199
+ // src/utils/setPaddingTop.ts
1200
+ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1201
+ if (stylePaddingTop !== void 0) {
1202
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1203
+ if (stylePaddingTop < prevStylePaddingTop) {
1204
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
1205
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1206
+ state.timeoutSetPaddingTop = setTimeout(() => {
1207
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
1208
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1209
+ }, 16);
1210
+ }
1211
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1212
+ }
1213
+ if (alignItemsPaddingTop !== void 0) {
1214
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1215
+ }
1216
+ }
1217
+
1218
+ // src/utils/updateAlignItemsPaddingTop.ts
1219
+ function updateAlignItemsPaddingTop(ctx, state) {
1220
+ const {
1221
+ scrollLength,
1222
+ props: { alignItemsAtEnd, data }
1223
+ } = state;
1224
+ if (alignItemsAtEnd) {
1225
+ let alignItemsPaddingTop = 0;
1226
+ if ((data == null ? void 0 : data.length) > 0) {
1227
+ const contentSize = getContentSize(ctx);
1228
+ alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1229
+ }
1230
+ setPaddingTop(ctx, state, { alignItemsPaddingTop });
1231
+ }
1232
+ }
1233
+
1234
+ // src/core/updateTotalSize.ts
1235
+ function updateTotalSize(ctx, state) {
1236
+ const {
1237
+ positions,
1238
+ props: { data }
1239
+ } = state;
1240
+ if (data.length === 0) {
1241
+ addTotalSize(ctx, state, null, 0);
1242
+ } else {
1243
+ const lastId = getId(state, data.length - 1);
1244
+ if (lastId !== void 0) {
1245
+ const lastPosition = positions.get(lastId);
1246
+ if (lastPosition !== void 0) {
1247
+ const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1248
+ if (lastSize !== void 0) {
1249
+ const totalSize = lastPosition + lastSize;
1250
+ addTotalSize(ctx, state, null, totalSize);
1251
+ }
1252
+ }
1253
+ }
1254
+ }
1255
+ }
1256
+ function addTotalSize(ctx, state, key, add) {
1257
+ const { alignItemsAtEnd } = state.props;
1258
+ const prevTotalSize = state.totalSize;
1259
+ if (key === null) {
1260
+ state.totalSize = add;
1261
+ if (state.timeoutSetPaddingTop) {
1262
+ clearTimeout(state.timeoutSetPaddingTop);
1263
+ state.timeoutSetPaddingTop = void 0;
1264
+ }
1265
+ } else {
1266
+ state.totalSize += add;
1267
+ }
1268
+ if (prevTotalSize !== state.totalSize) {
1269
+ set$(ctx, "totalSize", state.totalSize);
1270
+ if (alignItemsAtEnd) {
1271
+ updateAlignItemsPaddingTop(ctx, state);
1272
+ }
1273
+ }
1274
+ }
1275
+
1276
+ // src/core/setSize.ts
1277
+ function setSize(ctx, state, itemKey, size) {
1278
+ const { sizes } = state;
1279
+ const previousSize = sizes.get(itemKey);
1280
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1281
+ if (diff !== 0) {
1282
+ addTotalSize(ctx, state, itemKey, diff);
1283
+ }
1284
+ sizes.set(itemKey, size);
1285
+ }
1286
+
1192
1287
  // src/utils/getItemSize.ts
1193
1288
  function getItemSize(ctx, state, key, index, data, useAverageSize) {
1194
1289
  var _a3, _b;
@@ -1226,7 +1321,7 @@ function getItemSize(ctx, state, key, index, data, useAverageSize) {
1226
1321
  if (size === void 0) {
1227
1322
  size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1228
1323
  }
1229
- sizes.set(key, size);
1324
+ setSize(ctx, state, key, size);
1230
1325
  return size;
1231
1326
  }
1232
1327
 
@@ -1414,10 +1509,15 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1414
1509
 
1415
1510
  // src/core/finishScrollTo.ts
1416
1511
  function finishScrollTo(ctx, state) {
1512
+ var _a3;
1417
1513
  if (state) {
1418
1514
  state.scrollHistory.length = 0;
1419
1515
  state.initialScroll = void 0;
1516
+ state.isOptimizingItemPositions = false;
1420
1517
  set$(ctx, "scrollingTo", void 0);
1518
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1519
+ calculateItemsInView(ctx, state, { forceFullItemPositions: true });
1520
+ }
1421
1521
  }
1422
1522
  }
1423
1523
 
@@ -1601,82 +1701,43 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1601
1701
  return maxSize;
1602
1702
  }
1603
1703
 
1604
- // src/utils/setPaddingTop.ts
1605
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1606
- if (stylePaddingTop !== void 0) {
1607
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1608
- if (stylePaddingTop < prevStylePaddingTop) {
1609
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
1610
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1611
- state.timeoutSetPaddingTop = setTimeout(() => {
1612
- prevTotalSize = peek$(ctx, "totalSize") || 0;
1613
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1614
- }, 16);
1615
- }
1616
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1617
- }
1618
- if (alignItemsPaddingTop !== void 0) {
1619
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1620
- }
1621
- }
1622
-
1623
- // src/utils/updateAlignItemsPaddingTop.ts
1624
- function updateAlignItemsPaddingTop(ctx, state) {
1625
- const {
1626
- scrollLength,
1627
- props: { alignItemsAtEnd, data }
1628
- } = state;
1629
- if (alignItemsAtEnd) {
1630
- let alignItemsPaddingTop = 0;
1631
- if ((data == null ? void 0 : data.length) > 0) {
1632
- const contentSize = getContentSize(ctx);
1633
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1634
- }
1635
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1636
- }
1637
- }
1638
-
1639
- // src/core/updateTotalSize.ts
1640
- function updateTotalSize(ctx, state) {
1641
- const {
1642
- positions,
1643
- props: { data }
1644
- } = state;
1645
- if (data.length === 0) {
1646
- addTotalSize(ctx, state, null, 0);
1647
- } else {
1648
- const lastId = getId(state, data.length - 1);
1649
- if (lastId !== void 0) {
1650
- const lastPosition = positions.get(lastId);
1651
- if (lastPosition !== void 0) {
1652
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1653
- if (lastSize !== void 0) {
1654
- const totalSize = lastPosition + lastSize;
1655
- addTotalSize(ctx, state, null, totalSize);
1704
+ // src/utils/getScrollVelocity.ts
1705
+ var getScrollVelocity = (state) => {
1706
+ const { scrollHistory } = state;
1707
+ let velocity = 0;
1708
+ if (scrollHistory.length >= 1) {
1709
+ const newest = scrollHistory[scrollHistory.length - 1];
1710
+ let oldest;
1711
+ let start = 0;
1712
+ const now = Date.now();
1713
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1714
+ const entry = scrollHistory[i];
1715
+ const nextEntry = scrollHistory[i + 1];
1716
+ if (i > 0) {
1717
+ const prevEntry = scrollHistory[i - 1];
1718
+ const prevDirection = entry.scroll - prevEntry.scroll;
1719
+ const currentDirection = nextEntry.scroll - entry.scroll;
1720
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1721
+ start = i;
1722
+ break;
1656
1723
  }
1657
1724
  }
1658
1725
  }
1659
- }
1660
- }
1661
- function addTotalSize(ctx, state, key, add) {
1662
- const { alignItemsAtEnd } = state.props;
1663
- const prevTotalSize = state.totalSize;
1664
- if (key === null) {
1665
- state.totalSize = add;
1666
- if (state.timeoutSetPaddingTop) {
1667
- clearTimeout(state.timeoutSetPaddingTop);
1668
- state.timeoutSetPaddingTop = void 0;
1726
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1727
+ const entry = scrollHistory[i];
1728
+ if (now - entry.time <= 1e3) {
1729
+ oldest = entry;
1730
+ break;
1731
+ }
1669
1732
  }
1670
- } else {
1671
- state.totalSize += add;
1672
- }
1673
- if (prevTotalSize !== state.totalSize) {
1674
- set$(ctx, "totalSize", state.totalSize);
1675
- if (alignItemsAtEnd) {
1676
- updateAlignItemsPaddingTop(ctx, state);
1733
+ if (oldest && oldest !== newest) {
1734
+ const scrollDiff = newest.scroll - oldest.scroll;
1735
+ const timeDiff = newest.time - oldest.time;
1736
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1677
1737
  }
1678
1738
  }
1679
- }
1739
+ return velocity;
1740
+ };
1680
1741
 
1681
1742
  // src/utils/updateSnapToOffsets.ts
1682
1743
  function updateSnapToOffsets(ctx, state) {
@@ -1694,7 +1755,11 @@ function updateSnapToOffsets(ctx, state) {
1694
1755
  }
1695
1756
 
1696
1757
  // src/core/updateItemPositions.ts
1697
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered } = { scrollBottomBuffered: -1, startIndex: 0 }) {
1758
+ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false } = {
1759
+ forceFullUpdate: false,
1760
+ scrollBottomBuffered: -1,
1761
+ startIndex: 0
1762
+ }) {
1698
1763
  var _a3, _b, _c, _d;
1699
1764
  const {
1700
1765
  columns,
@@ -1710,6 +1775,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1710
1775
  const scrollingTo = peek$(ctx, "scrollingTo");
1711
1776
  const hasColumns = numColumns > 1;
1712
1777
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1778
+ const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1779
+ state.isOptimizingItemPositions = shouldOptimize;
1713
1780
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1714
1781
  const useAverageSize = enableAverages && !getEstimatedItemSize;
1715
1782
  let currentRowTop = 0;
@@ -1737,11 +1804,11 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1737
1804
  let didBreakEarly = false;
1738
1805
  let breakAt;
1739
1806
  for (let i = startIndex; i < dataLength; i++) {
1740
- if (breakAt && i > breakAt) {
1807
+ if (shouldOptimize && breakAt !== void 0 && i > breakAt) {
1741
1808
  didBreakEarly = true;
1742
1809
  break;
1743
1810
  }
1744
- if (breakAt === void 0 && !scrollingTo && !dataChanged && currentRowTop > maxVisibleArea) {
1811
+ if (shouldOptimize && breakAt === void 0 && !scrollingTo && !dataChanged && currentRowTop > maxVisibleArea) {
1745
1812
  const itemsPerRow = hasColumns ? numColumns : 1;
1746
1813
  breakAt = i + itemsPerRow + 10;
1747
1814
  }
@@ -2119,44 +2186,6 @@ function comparatorByDistance(a, b) {
2119
2186
  return b.distance - a.distance;
2120
2187
  }
2121
2188
 
2122
- // src/utils/getScrollVelocity.ts
2123
- var getScrollVelocity = (state) => {
2124
- const { scrollHistory } = state;
2125
- let velocity = 0;
2126
- if (scrollHistory.length >= 1) {
2127
- const newest = scrollHistory[scrollHistory.length - 1];
2128
- let oldest;
2129
- let start = 0;
2130
- const now = Date.now();
2131
- for (let i = 0; i < scrollHistory.length - 1; i++) {
2132
- const entry = scrollHistory[i];
2133
- const nextEntry = scrollHistory[i + 1];
2134
- if (i > 0) {
2135
- const prevEntry = scrollHistory[i - 1];
2136
- const prevDirection = entry.scroll - prevEntry.scroll;
2137
- const currentDirection = nextEntry.scroll - entry.scroll;
2138
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
2139
- start = i;
2140
- break;
2141
- }
2142
- }
2143
- }
2144
- for (let i = start; i < scrollHistory.length - 1; i++) {
2145
- const entry = scrollHistory[i];
2146
- if (now - entry.time <= 1e3) {
2147
- oldest = entry;
2148
- break;
2149
- }
2150
- }
2151
- if (oldest && oldest !== newest) {
2152
- const scrollDiff = newest.scroll - oldest.scroll;
2153
- const timeDiff = newest.time - oldest.time;
2154
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
2155
- }
2156
- }
2157
- return velocity;
2158
- };
2159
-
2160
2189
  // src/core/scrollToIndex.ts
2161
2190
  function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2162
2191
  if (index >= state.props.data.length) {
@@ -2276,14 +2305,15 @@ function calculateItemsInView(ctx, state, params = {}) {
2276
2305
  enableScrollForNextCalculateItemsInView,
2277
2306
  idCache,
2278
2307
  indexByKey,
2308
+ initialScroll,
2279
2309
  minIndexSizeChanged,
2280
2310
  positions,
2311
+ props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2281
2312
  scrollForNextCalculateItemsInView,
2282
2313
  scrollLength,
2283
2314
  sizes,
2284
2315
  startBufferedId: startBufferedIdOrig,
2285
- viewabilityConfigCallbackPairs,
2286
- props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer }
2316
+ viewabilityConfigCallbackPairs
2287
2317
  } = state;
2288
2318
  const { data } = state.props;
2289
2319
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
@@ -2295,7 +2325,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2295
2325
  const totalSize = peek$(ctx, "totalSize");
2296
2326
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2297
2327
  const numColumns = peek$(ctx, "numColumns");
2298
- const { dataChanged, doMVCP } = params;
2328
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2299
2329
  const speed = getScrollVelocity(state);
2300
2330
  const scrollExtra = 0;
2301
2331
  const { queuedInitialLayout } = state;
@@ -2349,7 +2379,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2349
2379
  positions.clear();
2350
2380
  }
2351
2381
  const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2352
- updateItemPositions(ctx, state, dataChanged, { scrollBottomBuffered, startIndex });
2382
+ updateItemPositions(ctx, state, dataChanged, {
2383
+ forceFullUpdate: !!forceFullItemPositions,
2384
+ scrollBottomBuffered,
2385
+ startIndex
2386
+ });
2353
2387
  if (minIndexSizeChanged !== void 0) {
2354
2388
  state.minIndexSizeChanged = void 0;
2355
2389
  }
@@ -2731,7 +2765,7 @@ function doInitialAllocateContainers(ctx, state) {
2731
2765
  set$(ctx, "numContainers", numContainers);
2732
2766
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2733
2767
  if (state.lastLayout) {
2734
- if (state.props.initialScroll) {
2768
+ if (state.initialScroll) {
2735
2769
  requestAnimationFrame(() => {
2736
2770
  calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2737
2771
  });
@@ -2897,7 +2931,6 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2897
2931
  if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2898
2932
  shouldMaintainScrollAtEnd = true;
2899
2933
  }
2900
- addTotalSize(ctx, state, itemKey, diff);
2901
2934
  onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2902
2935
  index,
2903
2936
  itemData: state.props.data[index],
@@ -2962,7 +2995,7 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2962
2995
  averages.num++;
2963
2996
  }
2964
2997
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2965
- sizes.set(itemKey, size);
2998
+ setSize(ctx, state, itemKey, size);
2966
2999
  return size - prevSize;
2967
3000
  }
2968
3001
  return 0;
@@ -3189,6 +3222,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3189
3222
  idsInView: [],
3190
3223
  indexByKey: /* @__PURE__ */ new Map(),
3191
3224
  initialScroll: initialScrollProp,
3225
+ isOptimizingItemPositions: false,
3192
3226
  isAtEnd: false,
3193
3227
  isAtStart: false,
3194
3228
  isEndReached: false,
@@ -3332,6 +3366,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3332
3366
  );
3333
3367
  refState.current.sizes.clear();
3334
3368
  refState.current.positions.clear();
3369
+ refState.current.totalSize = 0;
3370
+ set$(ctx, "totalSize", 0);
3335
3371
  }
3336
3372
  }
3337
3373
  const onLayoutHeader = React3.useCallback((rect, fromLayoutEffect) => {
package/index.mjs CHANGED
@@ -421,6 +421,10 @@ function getGlobalResizeObserver() {
421
421
  }
422
422
  var callbackMap = /* @__PURE__ */ new WeakMap();
423
423
  function createResizeObserver(element, callback) {
424
+ if (typeof ResizeObserver === "undefined") {
425
+ return () => {
426
+ };
427
+ }
424
428
  if (!element) {
425
429
  return () => {
426
430
  };
@@ -456,7 +460,7 @@ function useOnLayoutSync({
456
460
  const current = ref.current;
457
461
  const scrollableNode = (_b = (_a3 = current == null ? void 0 : current.getScrollableNode) == null ? void 0 : _a3.call(current)) != null ? _b : null;
458
462
  const element = scrollableNode || current;
459
- if (!element || !(element instanceof HTMLElement)) {
463
+ if (!element) {
460
464
  return;
461
465
  }
462
466
  const emit = (layout, fromLayoutEffect) => {
@@ -478,6 +482,9 @@ function useOnLayoutSync({
478
482
  return {};
479
483
  }
480
484
  function toLayout(rect) {
485
+ if (!rect) {
486
+ return { height: 0, width: 0, x: 0, y: 0 };
487
+ }
481
488
  return {
482
489
  height: rect.height,
483
490
  width: rect.width,
@@ -1168,6 +1175,94 @@ function calculateOffsetForIndex(ctx, state, index) {
1168
1175
  return position;
1169
1176
  }
1170
1177
 
1178
+ // src/utils/setPaddingTop.ts
1179
+ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1180
+ if (stylePaddingTop !== void 0) {
1181
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1182
+ if (stylePaddingTop < prevStylePaddingTop) {
1183
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
1184
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1185
+ state.timeoutSetPaddingTop = setTimeout(() => {
1186
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
1187
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1188
+ }, 16);
1189
+ }
1190
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1191
+ }
1192
+ if (alignItemsPaddingTop !== void 0) {
1193
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1194
+ }
1195
+ }
1196
+
1197
+ // src/utils/updateAlignItemsPaddingTop.ts
1198
+ function updateAlignItemsPaddingTop(ctx, state) {
1199
+ const {
1200
+ scrollLength,
1201
+ props: { alignItemsAtEnd, data }
1202
+ } = state;
1203
+ if (alignItemsAtEnd) {
1204
+ let alignItemsPaddingTop = 0;
1205
+ if ((data == null ? void 0 : data.length) > 0) {
1206
+ const contentSize = getContentSize(ctx);
1207
+ alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1208
+ }
1209
+ setPaddingTop(ctx, state, { alignItemsPaddingTop });
1210
+ }
1211
+ }
1212
+
1213
+ // src/core/updateTotalSize.ts
1214
+ function updateTotalSize(ctx, state) {
1215
+ const {
1216
+ positions,
1217
+ props: { data }
1218
+ } = state;
1219
+ if (data.length === 0) {
1220
+ addTotalSize(ctx, state, null, 0);
1221
+ } else {
1222
+ const lastId = getId(state, data.length - 1);
1223
+ if (lastId !== void 0) {
1224
+ const lastPosition = positions.get(lastId);
1225
+ if (lastPosition !== void 0) {
1226
+ const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1227
+ if (lastSize !== void 0) {
1228
+ const totalSize = lastPosition + lastSize;
1229
+ addTotalSize(ctx, state, null, totalSize);
1230
+ }
1231
+ }
1232
+ }
1233
+ }
1234
+ }
1235
+ function addTotalSize(ctx, state, key, add) {
1236
+ const { alignItemsAtEnd } = state.props;
1237
+ const prevTotalSize = state.totalSize;
1238
+ if (key === null) {
1239
+ state.totalSize = add;
1240
+ if (state.timeoutSetPaddingTop) {
1241
+ clearTimeout(state.timeoutSetPaddingTop);
1242
+ state.timeoutSetPaddingTop = void 0;
1243
+ }
1244
+ } else {
1245
+ state.totalSize += add;
1246
+ }
1247
+ if (prevTotalSize !== state.totalSize) {
1248
+ set$(ctx, "totalSize", state.totalSize);
1249
+ if (alignItemsAtEnd) {
1250
+ updateAlignItemsPaddingTop(ctx, state);
1251
+ }
1252
+ }
1253
+ }
1254
+
1255
+ // src/core/setSize.ts
1256
+ function setSize(ctx, state, itemKey, size) {
1257
+ const { sizes } = state;
1258
+ const previousSize = sizes.get(itemKey);
1259
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1260
+ if (diff !== 0) {
1261
+ addTotalSize(ctx, state, itemKey, diff);
1262
+ }
1263
+ sizes.set(itemKey, size);
1264
+ }
1265
+
1171
1266
  // src/utils/getItemSize.ts
1172
1267
  function getItemSize(ctx, state, key, index, data, useAverageSize) {
1173
1268
  var _a3, _b;
@@ -1205,7 +1300,7 @@ function getItemSize(ctx, state, key, index, data, useAverageSize) {
1205
1300
  if (size === void 0) {
1206
1301
  size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1207
1302
  }
1208
- sizes.set(key, size);
1303
+ setSize(ctx, state, key, size);
1209
1304
  return size;
1210
1305
  }
1211
1306
 
@@ -1393,10 +1488,15 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1393
1488
 
1394
1489
  // src/core/finishScrollTo.ts
1395
1490
  function finishScrollTo(ctx, state) {
1491
+ var _a3;
1396
1492
  if (state) {
1397
1493
  state.scrollHistory.length = 0;
1398
1494
  state.initialScroll = void 0;
1495
+ state.isOptimizingItemPositions = false;
1399
1496
  set$(ctx, "scrollingTo", void 0);
1497
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1498
+ calculateItemsInView(ctx, state, { forceFullItemPositions: true });
1499
+ }
1400
1500
  }
1401
1501
  }
1402
1502
 
@@ -1580,82 +1680,43 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1580
1680
  return maxSize;
1581
1681
  }
1582
1682
 
1583
- // src/utils/setPaddingTop.ts
1584
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1585
- if (stylePaddingTop !== void 0) {
1586
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1587
- if (stylePaddingTop < prevStylePaddingTop) {
1588
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
1589
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1590
- state.timeoutSetPaddingTop = setTimeout(() => {
1591
- prevTotalSize = peek$(ctx, "totalSize") || 0;
1592
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1593
- }, 16);
1594
- }
1595
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1596
- }
1597
- if (alignItemsPaddingTop !== void 0) {
1598
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1599
- }
1600
- }
1601
-
1602
- // src/utils/updateAlignItemsPaddingTop.ts
1603
- function updateAlignItemsPaddingTop(ctx, state) {
1604
- const {
1605
- scrollLength,
1606
- props: { alignItemsAtEnd, data }
1607
- } = state;
1608
- if (alignItemsAtEnd) {
1609
- let alignItemsPaddingTop = 0;
1610
- if ((data == null ? void 0 : data.length) > 0) {
1611
- const contentSize = getContentSize(ctx);
1612
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1613
- }
1614
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1615
- }
1616
- }
1617
-
1618
- // src/core/updateTotalSize.ts
1619
- function updateTotalSize(ctx, state) {
1620
- const {
1621
- positions,
1622
- props: { data }
1623
- } = state;
1624
- if (data.length === 0) {
1625
- addTotalSize(ctx, state, null, 0);
1626
- } else {
1627
- const lastId = getId(state, data.length - 1);
1628
- if (lastId !== void 0) {
1629
- const lastPosition = positions.get(lastId);
1630
- if (lastPosition !== void 0) {
1631
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1632
- if (lastSize !== void 0) {
1633
- const totalSize = lastPosition + lastSize;
1634
- addTotalSize(ctx, state, null, totalSize);
1683
+ // src/utils/getScrollVelocity.ts
1684
+ var getScrollVelocity = (state) => {
1685
+ const { scrollHistory } = state;
1686
+ let velocity = 0;
1687
+ if (scrollHistory.length >= 1) {
1688
+ const newest = scrollHistory[scrollHistory.length - 1];
1689
+ let oldest;
1690
+ let start = 0;
1691
+ const now = Date.now();
1692
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1693
+ const entry = scrollHistory[i];
1694
+ const nextEntry = scrollHistory[i + 1];
1695
+ if (i > 0) {
1696
+ const prevEntry = scrollHistory[i - 1];
1697
+ const prevDirection = entry.scroll - prevEntry.scroll;
1698
+ const currentDirection = nextEntry.scroll - entry.scroll;
1699
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1700
+ start = i;
1701
+ break;
1635
1702
  }
1636
1703
  }
1637
1704
  }
1638
- }
1639
- }
1640
- function addTotalSize(ctx, state, key, add) {
1641
- const { alignItemsAtEnd } = state.props;
1642
- const prevTotalSize = state.totalSize;
1643
- if (key === null) {
1644
- state.totalSize = add;
1645
- if (state.timeoutSetPaddingTop) {
1646
- clearTimeout(state.timeoutSetPaddingTop);
1647
- state.timeoutSetPaddingTop = void 0;
1705
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1706
+ const entry = scrollHistory[i];
1707
+ if (now - entry.time <= 1e3) {
1708
+ oldest = entry;
1709
+ break;
1710
+ }
1648
1711
  }
1649
- } else {
1650
- state.totalSize += add;
1651
- }
1652
- if (prevTotalSize !== state.totalSize) {
1653
- set$(ctx, "totalSize", state.totalSize);
1654
- if (alignItemsAtEnd) {
1655
- updateAlignItemsPaddingTop(ctx, state);
1712
+ if (oldest && oldest !== newest) {
1713
+ const scrollDiff = newest.scroll - oldest.scroll;
1714
+ const timeDiff = newest.time - oldest.time;
1715
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1656
1716
  }
1657
1717
  }
1658
- }
1718
+ return velocity;
1719
+ };
1659
1720
 
1660
1721
  // src/utils/updateSnapToOffsets.ts
1661
1722
  function updateSnapToOffsets(ctx, state) {
@@ -1673,7 +1734,11 @@ function updateSnapToOffsets(ctx, state) {
1673
1734
  }
1674
1735
 
1675
1736
  // src/core/updateItemPositions.ts
1676
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered } = { scrollBottomBuffered: -1, startIndex: 0 }) {
1737
+ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false } = {
1738
+ forceFullUpdate: false,
1739
+ scrollBottomBuffered: -1,
1740
+ startIndex: 0
1741
+ }) {
1677
1742
  var _a3, _b, _c, _d;
1678
1743
  const {
1679
1744
  columns,
@@ -1689,6 +1754,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1689
1754
  const scrollingTo = peek$(ctx, "scrollingTo");
1690
1755
  const hasColumns = numColumns > 1;
1691
1756
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1757
+ const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1758
+ state.isOptimizingItemPositions = shouldOptimize;
1692
1759
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1693
1760
  const useAverageSize = enableAverages && !getEstimatedItemSize;
1694
1761
  let currentRowTop = 0;
@@ -1716,11 +1783,11 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1716
1783
  let didBreakEarly = false;
1717
1784
  let breakAt;
1718
1785
  for (let i = startIndex; i < dataLength; i++) {
1719
- if (breakAt && i > breakAt) {
1786
+ if (shouldOptimize && breakAt !== void 0 && i > breakAt) {
1720
1787
  didBreakEarly = true;
1721
1788
  break;
1722
1789
  }
1723
- if (breakAt === void 0 && !scrollingTo && !dataChanged && currentRowTop > maxVisibleArea) {
1790
+ if (shouldOptimize && breakAt === void 0 && !scrollingTo && !dataChanged && currentRowTop > maxVisibleArea) {
1724
1791
  const itemsPerRow = hasColumns ? numColumns : 1;
1725
1792
  breakAt = i + itemsPerRow + 10;
1726
1793
  }
@@ -2098,44 +2165,6 @@ function comparatorByDistance(a, b) {
2098
2165
  return b.distance - a.distance;
2099
2166
  }
2100
2167
 
2101
- // src/utils/getScrollVelocity.ts
2102
- var getScrollVelocity = (state) => {
2103
- const { scrollHistory } = state;
2104
- let velocity = 0;
2105
- if (scrollHistory.length >= 1) {
2106
- const newest = scrollHistory[scrollHistory.length - 1];
2107
- let oldest;
2108
- let start = 0;
2109
- const now = Date.now();
2110
- for (let i = 0; i < scrollHistory.length - 1; i++) {
2111
- const entry = scrollHistory[i];
2112
- const nextEntry = scrollHistory[i + 1];
2113
- if (i > 0) {
2114
- const prevEntry = scrollHistory[i - 1];
2115
- const prevDirection = entry.scroll - prevEntry.scroll;
2116
- const currentDirection = nextEntry.scroll - entry.scroll;
2117
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
2118
- start = i;
2119
- break;
2120
- }
2121
- }
2122
- }
2123
- for (let i = start; i < scrollHistory.length - 1; i++) {
2124
- const entry = scrollHistory[i];
2125
- if (now - entry.time <= 1e3) {
2126
- oldest = entry;
2127
- break;
2128
- }
2129
- }
2130
- if (oldest && oldest !== newest) {
2131
- const scrollDiff = newest.scroll - oldest.scroll;
2132
- const timeDiff = newest.time - oldest.time;
2133
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
2134
- }
2135
- }
2136
- return velocity;
2137
- };
2138
-
2139
2168
  // src/core/scrollToIndex.ts
2140
2169
  function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2141
2170
  if (index >= state.props.data.length) {
@@ -2255,14 +2284,15 @@ function calculateItemsInView(ctx, state, params = {}) {
2255
2284
  enableScrollForNextCalculateItemsInView,
2256
2285
  idCache,
2257
2286
  indexByKey,
2287
+ initialScroll,
2258
2288
  minIndexSizeChanged,
2259
2289
  positions,
2290
+ props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2260
2291
  scrollForNextCalculateItemsInView,
2261
2292
  scrollLength,
2262
2293
  sizes,
2263
2294
  startBufferedId: startBufferedIdOrig,
2264
- viewabilityConfigCallbackPairs,
2265
- props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer }
2295
+ viewabilityConfigCallbackPairs
2266
2296
  } = state;
2267
2297
  const { data } = state.props;
2268
2298
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
@@ -2274,7 +2304,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2274
2304
  const totalSize = peek$(ctx, "totalSize");
2275
2305
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2276
2306
  const numColumns = peek$(ctx, "numColumns");
2277
- const { dataChanged, doMVCP } = params;
2307
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2278
2308
  const speed = getScrollVelocity(state);
2279
2309
  const scrollExtra = 0;
2280
2310
  const { queuedInitialLayout } = state;
@@ -2328,7 +2358,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2328
2358
  positions.clear();
2329
2359
  }
2330
2360
  const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2331
- updateItemPositions(ctx, state, dataChanged, { scrollBottomBuffered, startIndex });
2361
+ updateItemPositions(ctx, state, dataChanged, {
2362
+ forceFullUpdate: !!forceFullItemPositions,
2363
+ scrollBottomBuffered,
2364
+ startIndex
2365
+ });
2332
2366
  if (minIndexSizeChanged !== void 0) {
2333
2367
  state.minIndexSizeChanged = void 0;
2334
2368
  }
@@ -2710,7 +2744,7 @@ function doInitialAllocateContainers(ctx, state) {
2710
2744
  set$(ctx, "numContainers", numContainers);
2711
2745
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2712
2746
  if (state.lastLayout) {
2713
- if (state.props.initialScroll) {
2747
+ if (state.initialScroll) {
2714
2748
  requestAnimationFrame(() => {
2715
2749
  calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2716
2750
  });
@@ -2876,7 +2910,6 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2876
2910
  if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2877
2911
  shouldMaintainScrollAtEnd = true;
2878
2912
  }
2879
- addTotalSize(ctx, state, itemKey, diff);
2880
2913
  onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2881
2914
  index,
2882
2915
  itemData: state.props.data[index],
@@ -2941,7 +2974,7 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2941
2974
  averages.num++;
2942
2975
  }
2943
2976
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2944
- sizes.set(itemKey, size);
2977
+ setSize(ctx, state, itemKey, size);
2945
2978
  return size - prevSize;
2946
2979
  }
2947
2980
  return 0;
@@ -3168,6 +3201,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3168
3201
  idsInView: [],
3169
3202
  indexByKey: /* @__PURE__ */ new Map(),
3170
3203
  initialScroll: initialScrollProp,
3204
+ isOptimizingItemPositions: false,
3171
3205
  isAtEnd: false,
3172
3206
  isAtStart: false,
3173
3207
  isEndReached: false,
@@ -3311,6 +3345,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3311
3345
  );
3312
3346
  refState.current.sizes.clear();
3313
3347
  refState.current.positions.clear();
3348
+ refState.current.totalSize = 0;
3349
+ set$(ctx, "totalSize", 0);
3314
3350
  }
3315
3351
  }
3316
3352
  const onLayoutHeader = useCallback((rect, fromLayoutEffect) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "2.1.0-beta.5",
3
+ "version": "2.1.0-beta.6",
4
4
  "description": "Legend List is a drop-in replacement for FlatList with much better performance and supporting dynamically sized items.",
5
5
  "sideEffects": false,
6
6
  "private": false,