@legendapp/list 3.0.0-beta.20 → 3.0.0-beta.21

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.mjs CHANGED
@@ -295,7 +295,7 @@ var PositionViewState = typedMemo(function PositionViewState2({
295
295
  };
296
296
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
297
297
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
298
- const { animatedScrollY, stickyOffset, onLayout, ...webProps } = props;
298
+ const { animatedScrollY, stickyOffset, onLayout, index, ...webProps } = props;
299
299
  return /* @__PURE__ */ React3.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
300
300
  });
301
301
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
@@ -2511,7 +2511,7 @@ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2511
2511
  Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyHeaderIndices.has(idx))
2512
2512
  );
2513
2513
  }
2514
- function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2514
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
2515
2515
  var _a3;
2516
2516
  const state = ctx.state;
2517
2517
  const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
@@ -2521,18 +2521,20 @@ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentSt
2521
2521
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
2522
2522
  const stickyIndex = stickyArray[idx];
2523
2523
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2524
- if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
2524
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
2525
+ needNewContainersSet.add(stickyIndex);
2525
2526
  needNewContainers.push(stickyIndex);
2526
2527
  }
2527
2528
  }
2528
2529
  }
2529
- function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2530
+ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2530
2531
  var _a3, _b, _c;
2531
2532
  const state = ctx.state;
2532
2533
  for (const containerIndex of state.stickyContainerPool) {
2533
2534
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2534
2535
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
2535
2536
  if (itemIndex === void 0) continue;
2537
+ if (alwaysRenderIndicesSet.has(itemIndex)) continue;
2536
2538
  const arrayIdx = stickyArray.indexOf(itemIndex);
2537
2539
  if (arrayIdx === -1) {
2538
2540
  state.stickyContainerPool.delete(containerIndex);
@@ -2564,7 +2566,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentSt
2564
2566
  function calculateItemsInView(ctx, params = {}) {
2565
2567
  const state = ctx.state;
2566
2568
  unstable_batchedUpdates(() => {
2567
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2569
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
2568
2570
  const {
2569
2571
  columns,
2570
2572
  containerItemKeys,
@@ -2574,7 +2576,15 @@ function calculateItemsInView(ctx, params = {}) {
2574
2576
  initialScroll,
2575
2577
  minIndexSizeChanged,
2576
2578
  positions,
2577
- props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2579
+ props: {
2580
+ alwaysRenderIndicesArr,
2581
+ alwaysRenderIndicesSet,
2582
+ getItemType,
2583
+ itemsAreEqual,
2584
+ keyExtractor,
2585
+ onStickyHeaderChange,
2586
+ scrollBuffer
2587
+ },
2578
2588
  scrollForNextCalculateItemsInView,
2579
2589
  scrollLength,
2580
2590
  sizes,
@@ -2584,6 +2594,8 @@ function calculateItemsInView(ctx, params = {}) {
2584
2594
  const { data } = state.props;
2585
2595
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2586
2596
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2597
+ const alwaysRenderArr = alwaysRenderIndicesArr || [];
2598
+ const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2587
2599
  const prevNumContainers = peek$(ctx, "numContainers");
2588
2600
  if (!data || scrollLength === 0 || !prevNumContainers) {
2589
2601
  return;
@@ -2760,12 +2772,24 @@ function calculateItemsInView(ctx, params = {}) {
2760
2772
  }
2761
2773
  if (startBuffered !== null && endBuffered !== null) {
2762
2774
  const needNewContainers = [];
2775
+ const needNewContainersSet = /* @__PURE__ */ new Set();
2763
2776
  for (let i = startBuffered; i <= endBuffered; i++) {
2764
2777
  const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2765
2778
  if (!containerItemKeys.has(id)) {
2779
+ needNewContainersSet.add(i);
2766
2780
  needNewContainers.push(i);
2767
2781
  }
2768
2782
  }
2783
+ if (alwaysRenderArr.length > 0) {
2784
+ for (const index of alwaysRenderArr) {
2785
+ if (index < 0 || index >= dataLength) continue;
2786
+ const id = (_i = idCache[index]) != null ? _i : getId(state, index);
2787
+ if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2788
+ needNewContainersSet.add(index);
2789
+ needNewContainers.push(index);
2790
+ }
2791
+ }
2792
+ }
2769
2793
  if (stickyIndicesArr.length > 0) {
2770
2794
  handleStickyActivation(
2771
2795
  ctx,
@@ -2773,6 +2797,7 @@ function calculateItemsInView(ctx, params = {}) {
2773
2797
  stickyIndicesArr,
2774
2798
  currentStickyIdx,
2775
2799
  needNewContainers,
2800
+ needNewContainersSet,
2776
2801
  startBuffered,
2777
2802
  endBuffered
2778
2803
  );
@@ -2796,7 +2821,7 @@ function calculateItemsInView(ctx, params = {}) {
2796
2821
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2797
2822
  const i = needNewContainers[idx];
2798
2823
  const containerIndex = availableContainers[idx];
2799
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2824
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
2800
2825
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2801
2826
  if (oldKey && oldKey !== id) {
2802
2827
  containerItemKeys.delete(oldKey);
@@ -2808,14 +2833,23 @@ function calculateItemsInView(ctx, params = {}) {
2808
2833
  }
2809
2834
  containerItemKeys.set(id, containerIndex);
2810
2835
  const containerSticky = `containerSticky${containerIndex}`;
2811
- if (stickyIndicesSet.has(i)) {
2836
+ const isSticky = stickyIndicesSet.has(i);
2837
+ const isAlwaysRender = alwaysRenderSet.has(i);
2838
+ if (isSticky) {
2812
2839
  set$(ctx, containerSticky, true);
2813
2840
  const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2814
2841
  set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2815
2842
  state.stickyContainerPool.add(containerIndex);
2816
- } else if (peek$(ctx, containerSticky)) {
2817
- set$(ctx, containerSticky, false);
2818
- state.stickyContainerPool.delete(containerIndex);
2843
+ } else {
2844
+ if (peek$(ctx, containerSticky)) {
2845
+ set$(ctx, containerSticky, false);
2846
+ set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2847
+ }
2848
+ if (isAlwaysRender) {
2849
+ state.stickyContainerPool.add(containerIndex);
2850
+ } else if (state.stickyContainerPool.has(containerIndex)) {
2851
+ state.stickyContainerPool.delete(containerIndex);
2852
+ }
2819
2853
  }
2820
2854
  if (containerIndex >= numContainers) {
2821
2855
  numContainers = containerIndex + 1;
@@ -2828,9 +2862,27 @@ function calculateItemsInView(ctx, params = {}) {
2828
2862
  }
2829
2863
  }
2830
2864
  }
2865
+ if (alwaysRenderArr.length > 0) {
2866
+ for (const index of alwaysRenderArr) {
2867
+ if (index < 0 || index >= dataLength) continue;
2868
+ const id = (_k = idCache[index]) != null ? _k : getId(state, index);
2869
+ const containerIndex = containerItemKeys.get(id);
2870
+ if (containerIndex !== void 0) {
2871
+ state.stickyContainerPool.add(containerIndex);
2872
+ }
2873
+ }
2874
+ }
2831
2875
  }
2832
- if (stickyIndicesArr.length > 0) {
2833
- handleStickyRecycling(ctx, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2876
+ if (state.stickyContainerPool.size > 0) {
2877
+ handleStickyRecycling(
2878
+ ctx,
2879
+ stickyIndicesArr,
2880
+ scroll,
2881
+ scrollBuffer,
2882
+ currentStickyIdx,
2883
+ pendingRemoval,
2884
+ alwaysRenderSet
2885
+ );
2834
2886
  }
2835
2887
  let didChangePositions = false;
2836
2888
  for (let i = 0; i < numContainers; i++) {
@@ -2853,7 +2905,7 @@ function calculateItemsInView(ctx, params = {}) {
2853
2905
  const itemIndex = indexByKey.get(itemKey);
2854
2906
  const item = data[itemIndex];
2855
2907
  if (item !== void 0) {
2856
- const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2908
+ const id = (_l = idCache[itemIndex]) != null ? _l : getId(state, itemIndex);
2857
2909
  const positionValue = positions.get(id);
2858
2910
  if (positionValue === void 0) {
2859
2911
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -3517,6 +3569,54 @@ function createImperativeHandle(ctx) {
3517
3569
  }
3518
3570
  };
3519
3571
  }
3572
+
3573
+ // src/utils/getAlwaysRenderIndices.ts
3574
+ var sortAsc = (a, b) => a - b;
3575
+ var toCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
3576
+ var addIndex = (result, dataLength, index) => {
3577
+ if (index >= 0 && index < dataLength) {
3578
+ result.add(index);
3579
+ }
3580
+ };
3581
+ function getAlwaysRenderIndices(config, data, keyExtractor) {
3582
+ var _a3, _b;
3583
+ if (!config || data.length === 0) {
3584
+ return [];
3585
+ }
3586
+ const result = /* @__PURE__ */ new Set();
3587
+ const dataLength = data.length;
3588
+ const topCount = toCount(config.top);
3589
+ if (topCount > 0) {
3590
+ for (let i = 0; i < Math.min(topCount, dataLength); i++) {
3591
+ addIndex(result, dataLength, i);
3592
+ }
3593
+ }
3594
+ const bottomCount = toCount(config.bottom);
3595
+ if (bottomCount > 0) {
3596
+ for (let i = Math.max(0, dataLength - bottomCount); i < dataLength; i++) {
3597
+ addIndex(result, dataLength, i);
3598
+ }
3599
+ }
3600
+ if ((_a3 = config.indices) == null ? void 0 : _a3.length) {
3601
+ for (const index of config.indices) {
3602
+ if (!Number.isFinite(index)) continue;
3603
+ addIndex(result, dataLength, Math.floor(index));
3604
+ }
3605
+ }
3606
+ if ((_b = config.keys) == null ? void 0 : _b.length) {
3607
+ const keys = new Set(config.keys);
3608
+ for (let i = 0; i < dataLength && keys.size > 0; i++) {
3609
+ const key = keyExtractor(data[i], i);
3610
+ if (keys.has(key)) {
3611
+ addIndex(result, dataLength, i);
3612
+ keys.delete(key);
3613
+ }
3614
+ }
3615
+ }
3616
+ const indices = Array.from(result);
3617
+ indices.sort(sortAsc);
3618
+ return indices;
3619
+ }
3520
3620
  function getRenderedItem(ctx, key) {
3521
3621
  var _a3;
3522
3622
  const state = ctx.state;
@@ -3633,9 +3733,10 @@ var LegendList = typedMemo(
3633
3733
  })
3634
3734
  );
3635
3735
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3636
- var _a3, _b;
3736
+ var _a3, _b, _c, _d;
3637
3737
  const {
3638
3738
  alignItemsAtEnd = false,
3739
+ alwaysRender,
3639
3740
  columnWrapperStyle,
3640
3741
  contentContainerStyle: contentContainerStyleProp,
3641
3742
  contentInset,
@@ -3664,6 +3765,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3664
3765
  onEndReached,
3665
3766
  onEndReachedThreshold = 0.5,
3666
3767
  onItemSizeChanged,
3768
+ onMetricsChange,
3667
3769
  onLayout: onLayoutProp,
3668
3770
  onLoad,
3669
3771
  onMomentumScrollEnd,
@@ -3712,6 +3814,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3712
3814
  const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3713
3815
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
3714
3816
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
3817
+ const alwaysRenderIndices = useMemo(() => {
3818
+ const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
3819
+ return { arr: indices, set: new Set(indices) };
3820
+ }, [
3821
+ alwaysRender == null ? void 0 : alwaysRender.top,
3822
+ alwaysRender == null ? void 0 : alwaysRender.bottom,
3823
+ (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
3824
+ (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
3825
+ dataProp,
3826
+ dataVersion,
3827
+ keyExtractor
3828
+ ]);
3715
3829
  if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3716
3830
  warnDevOnce(
3717
3831
  "stickyIndices",
@@ -3743,7 +3857,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3743
3857
  attempts: 0,
3744
3858
  index: initialScrollProp.index,
3745
3859
  settledTicks: 0,
3746
- viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3860
+ viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
3747
3861
  viewPosition: initialScrollProp.viewPosition
3748
3862
  } : void 0,
3749
3863
  initialScroll: initialScrollProp,
@@ -3803,6 +3917,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3803
3917
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3804
3918
  state.props = {
3805
3919
  alignItemsAtEnd,
3920
+ alwaysRender,
3921
+ alwaysRenderIndicesArr: alwaysRenderIndices.arr,
3922
+ alwaysRenderIndicesSet: alwaysRenderIndices.set,
3806
3923
  animatedProps: animatedPropsInternal,
3807
3924
  contentInset,
3808
3925
  data: dataProp,
@@ -3968,6 +4085,34 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3968
4085
  stylePaddingBottomState,
3969
4086
  stylePaddingTopState
3970
4087
  ]);
4088
+ useEffect(() => {
4089
+ if (!onMetricsChange) {
4090
+ return;
4091
+ }
4092
+ let lastMetrics;
4093
+ const emitMetrics = () => {
4094
+ const metrics = {
4095
+ alignItemsAtEndPadding: peek$(ctx, "alignItemsPaddingTop") || 0,
4096
+ footerSize: peek$(ctx, "footerSize") || 0,
4097
+ headerSize: peek$(ctx, "headerSize") || 0
4098
+ };
4099
+ if (!lastMetrics || metrics.alignItemsAtEndPadding !== lastMetrics.alignItemsAtEndPadding || metrics.headerSize !== lastMetrics.headerSize || metrics.footerSize !== lastMetrics.footerSize) {
4100
+ lastMetrics = metrics;
4101
+ onMetricsChange(metrics);
4102
+ }
4103
+ };
4104
+ emitMetrics();
4105
+ const unsubscribe = [
4106
+ listen$(ctx, "alignItemsPaddingTop", emitMetrics),
4107
+ listen$(ctx, "headerSize", emitMetrics),
4108
+ listen$(ctx, "footerSize", emitMetrics)
4109
+ ];
4110
+ return () => {
4111
+ for (const unsub of unsubscribe) {
4112
+ unsub();
4113
+ }
4114
+ };
4115
+ }, [ctx, onMetricsChange]);
3971
4116
  useEffect(() => {
3972
4117
  const viewability = setupViewability({
3973
4118
  onViewableItemsChanged,
@@ -4025,7 +4170,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4025
4170
  }
4026
4171
  ),
4027
4172
  refScrollView: combinedRef,
4028
- scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
4173
+ scrollAdjustHandler: (_d = refState.current) == null ? void 0 : _d.scrollAdjustHandler,
4029
4174
  scrollEventThrottle: 0,
4030
4175
  snapToIndices,
4031
4176
  stickyHeaderConfig,