@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.
@@ -0,0 +1,9 @@
1
+ import * as _legendapp_list from '@legendapp/list';
2
+ import * as React from 'react';
3
+ import { Animated } from 'react-native';
4
+
5
+ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: _legendapp_list.LegendListProps<T> & React.RefAttributes<_legendapp_list.LegendListRef>) => React.ReactNode) & {
6
+ displayName?: string;
7
+ }>;
8
+
9
+ export { AnimatedLegendList };
@@ -0,0 +1,9 @@
1
+ import * as _legendapp_list from '@legendapp/list';
2
+ import * as React from 'react';
3
+ import { Animated } from 'react-native';
4
+
5
+ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: _legendapp_list.LegendListProps<T> & React.RefAttributes<_legendapp_list.LegendListRef>) => React.ReactNode) & {
6
+ displayName?: string;
7
+ }>;
8
+
9
+ export { AnimatedLegendList };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ var reactNative = require('react-native');
4
+ var list = require('@legendapp/list');
5
+
6
+ // src/integrations/animated.tsx
7
+ var AnimatedLegendList = reactNative.Animated.createAnimatedComponent(list.LegendList);
8
+
9
+ exports.AnimatedLegendList = AnimatedLegendList;
@@ -0,0 +1,7 @@
1
+ import { Animated } from 'react-native';
2
+ import { LegendList } from '@legendapp/list';
3
+
4
+ // src/integrations/animated.tsx
5
+ var AnimatedLegendList = Animated.createAnimatedComponent(LegendList);
6
+
7
+ export { AnimatedLegendList };
package/index.d.mts CHANGED
@@ -119,6 +119,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
119
119
  * @default false
120
120
  */
121
121
  alignItemsAtEnd?: boolean;
122
+ /**
123
+ * Keeps selected items mounted even when they scroll out of view.
124
+ * @default undefined
125
+ */
126
+ alwaysRender?: AlwaysRenderConfig;
122
127
  /**
123
128
  * Style applied to each column's wrapper view.
124
129
  */
@@ -255,6 +260,10 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
255
260
  itemKey: string;
256
261
  itemData: ItemT;
257
262
  }) => void;
263
+ /**
264
+ * Called when list layout metrics change.
265
+ */
266
+ onMetricsChange?: (metrics: LegendListMetrics) => void;
258
267
  /**
259
268
  * Function to call when the user pulls to refresh.
260
269
  */
@@ -374,6 +383,12 @@ interface StickyHeaderConfig {
374
383
  */
375
384
  backdropComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
376
385
  }
386
+ interface AlwaysRenderConfig {
387
+ top?: number;
388
+ bottom?: number;
389
+ indices?: number[];
390
+ keys?: string[];
391
+ }
377
392
  interface MaintainScrollAtEndOptions {
378
393
  onLayout?: boolean;
379
394
  onItemLayout?: boolean;
@@ -384,6 +399,11 @@ interface ColumnWrapperStyle {
384
399
  gap?: number;
385
400
  columnGap?: number;
386
401
  }
402
+ interface LegendListMetrics {
403
+ alignItemsAtEndPadding: number;
404
+ headerSize: number;
405
+ footerSize: number;
406
+ }
387
407
  type LegendListProps<ItemT = any> = LegendListPropsBase<ItemT, ComponentProps<typeof ScrollView>>;
388
408
  interface ThresholdSnapshot {
389
409
  scrollPosition: number;
@@ -495,6 +515,9 @@ interface InternalState {
495
515
  props: {
496
516
  alignItemsAtEnd: boolean;
497
517
  animatedProps: StylesAsSharedValue<ScrollViewProps>;
518
+ alwaysRender: AlwaysRenderConfig | undefined;
519
+ alwaysRenderIndicesArr: number[];
520
+ alwaysRenderIndicesSet: Set<number>;
498
521
  contentInset: Insets | undefined;
499
522
  data: readonly any[];
500
523
  dataVersion: Key | undefined;
@@ -768,4 +791,4 @@ declare function useListScrollSize(): {
768
791
  };
769
792
  declare function useSyncLayout(): () => void;
770
793
 
771
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InitialScrollAnchor, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type LegendListState, type MaintainScrollAtEndOptions, type MaintainVisibleContentPositionConfig, type MaintainVisibleContentPositionNormalized, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollTarget, type StickyHeaderConfig, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
794
+ export { type AlwaysRenderConfig, type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InitialScrollAnchor, type InternalState, LegendList, type LegendListMetrics, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type LegendListState, type MaintainScrollAtEndOptions, type MaintainVisibleContentPositionConfig, type MaintainVisibleContentPositionNormalized, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollTarget, type StickyHeaderConfig, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
package/index.d.ts CHANGED
@@ -119,6 +119,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
119
119
  * @default false
120
120
  */
121
121
  alignItemsAtEnd?: boolean;
122
+ /**
123
+ * Keeps selected items mounted even when they scroll out of view.
124
+ * @default undefined
125
+ */
126
+ alwaysRender?: AlwaysRenderConfig;
122
127
  /**
123
128
  * Style applied to each column's wrapper view.
124
129
  */
@@ -255,6 +260,10 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
255
260
  itemKey: string;
256
261
  itemData: ItemT;
257
262
  }) => void;
263
+ /**
264
+ * Called when list layout metrics change.
265
+ */
266
+ onMetricsChange?: (metrics: LegendListMetrics) => void;
258
267
  /**
259
268
  * Function to call when the user pulls to refresh.
260
269
  */
@@ -374,6 +383,12 @@ interface StickyHeaderConfig {
374
383
  */
375
384
  backdropComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
376
385
  }
386
+ interface AlwaysRenderConfig {
387
+ top?: number;
388
+ bottom?: number;
389
+ indices?: number[];
390
+ keys?: string[];
391
+ }
377
392
  interface MaintainScrollAtEndOptions {
378
393
  onLayout?: boolean;
379
394
  onItemLayout?: boolean;
@@ -384,6 +399,11 @@ interface ColumnWrapperStyle {
384
399
  gap?: number;
385
400
  columnGap?: number;
386
401
  }
402
+ interface LegendListMetrics {
403
+ alignItemsAtEndPadding: number;
404
+ headerSize: number;
405
+ footerSize: number;
406
+ }
387
407
  type LegendListProps<ItemT = any> = LegendListPropsBase<ItemT, ComponentProps<typeof ScrollView>>;
388
408
  interface ThresholdSnapshot {
389
409
  scrollPosition: number;
@@ -495,6 +515,9 @@ interface InternalState {
495
515
  props: {
496
516
  alignItemsAtEnd: boolean;
497
517
  animatedProps: StylesAsSharedValue<ScrollViewProps>;
518
+ alwaysRender: AlwaysRenderConfig | undefined;
519
+ alwaysRenderIndicesArr: number[];
520
+ alwaysRenderIndicesSet: Set<number>;
498
521
  contentInset: Insets | undefined;
499
522
  data: readonly any[];
500
523
  dataVersion: Key | undefined;
@@ -768,4 +791,4 @@ declare function useListScrollSize(): {
768
791
  };
769
792
  declare function useSyncLayout(): () => void;
770
793
 
771
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InitialScrollAnchor, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type LegendListState, type MaintainScrollAtEndOptions, type MaintainVisibleContentPositionConfig, type MaintainVisibleContentPositionNormalized, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollTarget, type StickyHeaderConfig, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
794
+ export { type AlwaysRenderConfig, type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InitialScrollAnchor, type InternalState, LegendList, type LegendListMetrics, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type LegendListState, type MaintainScrollAtEndOptions, type MaintainVisibleContentPositionConfig, type MaintainVisibleContentPositionNormalized, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollTarget, type StickyHeaderConfig, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
package/index.js CHANGED
@@ -316,7 +316,7 @@ var PositionViewState = typedMemo(function PositionViewState2({
316
316
  };
317
317
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
318
318
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
319
- const { animatedScrollY, stickyOffset, onLayout, ...webProps } = props;
319
+ const { animatedScrollY, stickyOffset, onLayout, index, ...webProps } = props;
320
320
  return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
321
321
  });
322
322
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
@@ -2532,7 +2532,7 @@ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2532
2532
  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))
2533
2533
  );
2534
2534
  }
2535
- function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2535
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
2536
2536
  var _a3;
2537
2537
  const state = ctx.state;
2538
2538
  const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
@@ -2542,18 +2542,20 @@ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentSt
2542
2542
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
2543
2543
  const stickyIndex = stickyArray[idx];
2544
2544
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2545
- if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
2545
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
2546
+ needNewContainersSet.add(stickyIndex);
2546
2547
  needNewContainers.push(stickyIndex);
2547
2548
  }
2548
2549
  }
2549
2550
  }
2550
- function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2551
+ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2551
2552
  var _a3, _b, _c;
2552
2553
  const state = ctx.state;
2553
2554
  for (const containerIndex of state.stickyContainerPool) {
2554
2555
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2555
2556
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
2556
2557
  if (itemIndex === void 0) continue;
2558
+ if (alwaysRenderIndicesSet.has(itemIndex)) continue;
2557
2559
  const arrayIdx = stickyArray.indexOf(itemIndex);
2558
2560
  if (arrayIdx === -1) {
2559
2561
  state.stickyContainerPool.delete(containerIndex);
@@ -2585,7 +2587,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentSt
2585
2587
  function calculateItemsInView(ctx, params = {}) {
2586
2588
  const state = ctx.state;
2587
2589
  reactDom.unstable_batchedUpdates(() => {
2588
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2590
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
2589
2591
  const {
2590
2592
  columns,
2591
2593
  containerItemKeys,
@@ -2595,7 +2597,15 @@ function calculateItemsInView(ctx, params = {}) {
2595
2597
  initialScroll,
2596
2598
  minIndexSizeChanged,
2597
2599
  positions,
2598
- props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2600
+ props: {
2601
+ alwaysRenderIndicesArr,
2602
+ alwaysRenderIndicesSet,
2603
+ getItemType,
2604
+ itemsAreEqual,
2605
+ keyExtractor,
2606
+ onStickyHeaderChange,
2607
+ scrollBuffer
2608
+ },
2599
2609
  scrollForNextCalculateItemsInView,
2600
2610
  scrollLength,
2601
2611
  sizes,
@@ -2605,6 +2615,8 @@ function calculateItemsInView(ctx, params = {}) {
2605
2615
  const { data } = state.props;
2606
2616
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2607
2617
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2618
+ const alwaysRenderArr = alwaysRenderIndicesArr || [];
2619
+ const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2608
2620
  const prevNumContainers = peek$(ctx, "numContainers");
2609
2621
  if (!data || scrollLength === 0 || !prevNumContainers) {
2610
2622
  return;
@@ -2781,12 +2793,24 @@ function calculateItemsInView(ctx, params = {}) {
2781
2793
  }
2782
2794
  if (startBuffered !== null && endBuffered !== null) {
2783
2795
  const needNewContainers = [];
2796
+ const needNewContainersSet = /* @__PURE__ */ new Set();
2784
2797
  for (let i = startBuffered; i <= endBuffered; i++) {
2785
2798
  const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2786
2799
  if (!containerItemKeys.has(id)) {
2800
+ needNewContainersSet.add(i);
2787
2801
  needNewContainers.push(i);
2788
2802
  }
2789
2803
  }
2804
+ if (alwaysRenderArr.length > 0) {
2805
+ for (const index of alwaysRenderArr) {
2806
+ if (index < 0 || index >= dataLength) continue;
2807
+ const id = (_i = idCache[index]) != null ? _i : getId(state, index);
2808
+ if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2809
+ needNewContainersSet.add(index);
2810
+ needNewContainers.push(index);
2811
+ }
2812
+ }
2813
+ }
2790
2814
  if (stickyIndicesArr.length > 0) {
2791
2815
  handleStickyActivation(
2792
2816
  ctx,
@@ -2794,6 +2818,7 @@ function calculateItemsInView(ctx, params = {}) {
2794
2818
  stickyIndicesArr,
2795
2819
  currentStickyIdx,
2796
2820
  needNewContainers,
2821
+ needNewContainersSet,
2797
2822
  startBuffered,
2798
2823
  endBuffered
2799
2824
  );
@@ -2817,7 +2842,7 @@ function calculateItemsInView(ctx, params = {}) {
2817
2842
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2818
2843
  const i = needNewContainers[idx];
2819
2844
  const containerIndex = availableContainers[idx];
2820
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2845
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
2821
2846
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2822
2847
  if (oldKey && oldKey !== id) {
2823
2848
  containerItemKeys.delete(oldKey);
@@ -2829,14 +2854,23 @@ function calculateItemsInView(ctx, params = {}) {
2829
2854
  }
2830
2855
  containerItemKeys.set(id, containerIndex);
2831
2856
  const containerSticky = `containerSticky${containerIndex}`;
2832
- if (stickyIndicesSet.has(i)) {
2857
+ const isSticky = stickyIndicesSet.has(i);
2858
+ const isAlwaysRender = alwaysRenderSet.has(i);
2859
+ if (isSticky) {
2833
2860
  set$(ctx, containerSticky, true);
2834
2861
  const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2835
2862
  set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2836
2863
  state.stickyContainerPool.add(containerIndex);
2837
- } else if (peek$(ctx, containerSticky)) {
2838
- set$(ctx, containerSticky, false);
2839
- state.stickyContainerPool.delete(containerIndex);
2864
+ } else {
2865
+ if (peek$(ctx, containerSticky)) {
2866
+ set$(ctx, containerSticky, false);
2867
+ set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2868
+ }
2869
+ if (isAlwaysRender) {
2870
+ state.stickyContainerPool.add(containerIndex);
2871
+ } else if (state.stickyContainerPool.has(containerIndex)) {
2872
+ state.stickyContainerPool.delete(containerIndex);
2873
+ }
2840
2874
  }
2841
2875
  if (containerIndex >= numContainers) {
2842
2876
  numContainers = containerIndex + 1;
@@ -2849,9 +2883,27 @@ function calculateItemsInView(ctx, params = {}) {
2849
2883
  }
2850
2884
  }
2851
2885
  }
2886
+ if (alwaysRenderArr.length > 0) {
2887
+ for (const index of alwaysRenderArr) {
2888
+ if (index < 0 || index >= dataLength) continue;
2889
+ const id = (_k = idCache[index]) != null ? _k : getId(state, index);
2890
+ const containerIndex = containerItemKeys.get(id);
2891
+ if (containerIndex !== void 0) {
2892
+ state.stickyContainerPool.add(containerIndex);
2893
+ }
2894
+ }
2895
+ }
2852
2896
  }
2853
- if (stickyIndicesArr.length > 0) {
2854
- handleStickyRecycling(ctx, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2897
+ if (state.stickyContainerPool.size > 0) {
2898
+ handleStickyRecycling(
2899
+ ctx,
2900
+ stickyIndicesArr,
2901
+ scroll,
2902
+ scrollBuffer,
2903
+ currentStickyIdx,
2904
+ pendingRemoval,
2905
+ alwaysRenderSet
2906
+ );
2855
2907
  }
2856
2908
  let didChangePositions = false;
2857
2909
  for (let i = 0; i < numContainers; i++) {
@@ -2874,7 +2926,7 @@ function calculateItemsInView(ctx, params = {}) {
2874
2926
  const itemIndex = indexByKey.get(itemKey);
2875
2927
  const item = data[itemIndex];
2876
2928
  if (item !== void 0) {
2877
- const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2929
+ const id = (_l = idCache[itemIndex]) != null ? _l : getId(state, itemIndex);
2878
2930
  const positionValue = positions.get(id);
2879
2931
  if (positionValue === void 0) {
2880
2932
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -3538,6 +3590,54 @@ function createImperativeHandle(ctx) {
3538
3590
  }
3539
3591
  };
3540
3592
  }
3593
+
3594
+ // src/utils/getAlwaysRenderIndices.ts
3595
+ var sortAsc = (a, b) => a - b;
3596
+ var toCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
3597
+ var addIndex = (result, dataLength, index) => {
3598
+ if (index >= 0 && index < dataLength) {
3599
+ result.add(index);
3600
+ }
3601
+ };
3602
+ function getAlwaysRenderIndices(config, data, keyExtractor) {
3603
+ var _a3, _b;
3604
+ if (!config || data.length === 0) {
3605
+ return [];
3606
+ }
3607
+ const result = /* @__PURE__ */ new Set();
3608
+ const dataLength = data.length;
3609
+ const topCount = toCount(config.top);
3610
+ if (topCount > 0) {
3611
+ for (let i = 0; i < Math.min(topCount, dataLength); i++) {
3612
+ addIndex(result, dataLength, i);
3613
+ }
3614
+ }
3615
+ const bottomCount = toCount(config.bottom);
3616
+ if (bottomCount > 0) {
3617
+ for (let i = Math.max(0, dataLength - bottomCount); i < dataLength; i++) {
3618
+ addIndex(result, dataLength, i);
3619
+ }
3620
+ }
3621
+ if ((_a3 = config.indices) == null ? void 0 : _a3.length) {
3622
+ for (const index of config.indices) {
3623
+ if (!Number.isFinite(index)) continue;
3624
+ addIndex(result, dataLength, Math.floor(index));
3625
+ }
3626
+ }
3627
+ if ((_b = config.keys) == null ? void 0 : _b.length) {
3628
+ const keys = new Set(config.keys);
3629
+ for (let i = 0; i < dataLength && keys.size > 0; i++) {
3630
+ const key = keyExtractor(data[i], i);
3631
+ if (keys.has(key)) {
3632
+ addIndex(result, dataLength, i);
3633
+ keys.delete(key);
3634
+ }
3635
+ }
3636
+ }
3637
+ const indices = Array.from(result);
3638
+ indices.sort(sortAsc);
3639
+ return indices;
3640
+ }
3541
3641
  function getRenderedItem(ctx, key) {
3542
3642
  var _a3;
3543
3643
  const state = ctx.state;
@@ -3654,9 +3754,10 @@ var LegendList = typedMemo(
3654
3754
  })
3655
3755
  );
3656
3756
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3657
- var _a3, _b;
3757
+ var _a3, _b, _c, _d;
3658
3758
  const {
3659
3759
  alignItemsAtEnd = false,
3760
+ alwaysRender,
3660
3761
  columnWrapperStyle,
3661
3762
  contentContainerStyle: contentContainerStyleProp,
3662
3763
  contentInset,
@@ -3685,6 +3786,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3685
3786
  onEndReached,
3686
3787
  onEndReachedThreshold = 0.5,
3687
3788
  onItemSizeChanged,
3789
+ onMetricsChange,
3688
3790
  onLayout: onLayoutProp,
3689
3791
  onLoad,
3690
3792
  onMomentumScrollEnd,
@@ -3733,6 +3835,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3733
3835
  const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3734
3836
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
3735
3837
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
3838
+ const alwaysRenderIndices = React3.useMemo(() => {
3839
+ const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
3840
+ return { arr: indices, set: new Set(indices) };
3841
+ }, [
3842
+ alwaysRender == null ? void 0 : alwaysRender.top,
3843
+ alwaysRender == null ? void 0 : alwaysRender.bottom,
3844
+ (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
3845
+ (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
3846
+ dataProp,
3847
+ dataVersion,
3848
+ keyExtractor
3849
+ ]);
3736
3850
  if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3737
3851
  warnDevOnce(
3738
3852
  "stickyIndices",
@@ -3764,7 +3878,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3764
3878
  attempts: 0,
3765
3879
  index: initialScrollProp.index,
3766
3880
  settledTicks: 0,
3767
- viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3881
+ viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
3768
3882
  viewPosition: initialScrollProp.viewPosition
3769
3883
  } : void 0,
3770
3884
  initialScroll: initialScrollProp,
@@ -3824,6 +3938,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3824
3938
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3825
3939
  state.props = {
3826
3940
  alignItemsAtEnd,
3941
+ alwaysRender,
3942
+ alwaysRenderIndicesArr: alwaysRenderIndices.arr,
3943
+ alwaysRenderIndicesSet: alwaysRenderIndices.set,
3827
3944
  animatedProps: animatedPropsInternal,
3828
3945
  contentInset,
3829
3946
  data: dataProp,
@@ -3989,6 +4106,34 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3989
4106
  stylePaddingBottomState,
3990
4107
  stylePaddingTopState
3991
4108
  ]);
4109
+ React3.useEffect(() => {
4110
+ if (!onMetricsChange) {
4111
+ return;
4112
+ }
4113
+ let lastMetrics;
4114
+ const emitMetrics = () => {
4115
+ const metrics = {
4116
+ alignItemsAtEndPadding: peek$(ctx, "alignItemsPaddingTop") || 0,
4117
+ footerSize: peek$(ctx, "footerSize") || 0,
4118
+ headerSize: peek$(ctx, "headerSize") || 0
4119
+ };
4120
+ if (!lastMetrics || metrics.alignItemsAtEndPadding !== lastMetrics.alignItemsAtEndPadding || metrics.headerSize !== lastMetrics.headerSize || metrics.footerSize !== lastMetrics.footerSize) {
4121
+ lastMetrics = metrics;
4122
+ onMetricsChange(metrics);
4123
+ }
4124
+ };
4125
+ emitMetrics();
4126
+ const unsubscribe = [
4127
+ listen$(ctx, "alignItemsPaddingTop", emitMetrics),
4128
+ listen$(ctx, "headerSize", emitMetrics),
4129
+ listen$(ctx, "footerSize", emitMetrics)
4130
+ ];
4131
+ return () => {
4132
+ for (const unsub of unsubscribe) {
4133
+ unsub();
4134
+ }
4135
+ };
4136
+ }, [ctx, onMetricsChange]);
3992
4137
  React3.useEffect(() => {
3993
4138
  const viewability = setupViewability({
3994
4139
  onViewableItemsChanged,
@@ -4046,7 +4191,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4046
4191
  }
4047
4192
  ),
4048
4193
  refScrollView: combinedRef,
4049
- scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
4194
+ scrollAdjustHandler: (_d = refState.current) == null ? void 0 : _d.scrollAdjustHandler,
4050
4195
  scrollEventThrottle: 0,
4051
4196
  snapToIndices,
4052
4197
  stickyHeaderConfig,