@legendapp/list 0.3.7 → 0.4.1
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 +18 -4
- package/index.d.ts +18 -4
- package/index.js +135 -23
- package/index.mjs +136 -24
- package/package.json +1 -1
package/index.d.mts
CHANGED
|
@@ -18,7 +18,7 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
|
|
|
18
18
|
distanceFromEnd: number;
|
|
19
19
|
}) => void) | null | undefined;
|
|
20
20
|
keyExtractor?: (item: T, index: number) => string;
|
|
21
|
-
renderItem?: (props:
|
|
21
|
+
renderItem?: (props: LegendListRenderItemProps<T>) => ReactNode;
|
|
22
22
|
onViewableRangeChanged?: (range: ViewableRange<T>) => void;
|
|
23
23
|
ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
|
|
24
24
|
ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
@@ -59,9 +59,12 @@ interface ViewableRange<T> {
|
|
|
59
59
|
end: number;
|
|
60
60
|
items: T[];
|
|
61
61
|
}
|
|
62
|
-
interface
|
|
62
|
+
interface LegendListRenderItemProps<ItemT> {
|
|
63
63
|
item: ItemT;
|
|
64
64
|
index: number;
|
|
65
|
+
useViewability: (configId: string, callback: ViewabilityCallback) => void;
|
|
66
|
+
useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
|
|
67
|
+
useRecyclingState: <T>(updateState: (info: LegendListRecyclingState<ItemT>) => T) => [T, React.Dispatch<T>];
|
|
65
68
|
}
|
|
66
69
|
type LegendListRef = {
|
|
67
70
|
/**
|
|
@@ -106,7 +109,7 @@ interface ViewToken<ItemT = any> {
|
|
|
106
109
|
}
|
|
107
110
|
interface ViewabilityConfigCallbackPair {
|
|
108
111
|
viewabilityConfig: ViewabilityConfig;
|
|
109
|
-
onViewableItemsChanged
|
|
112
|
+
onViewableItemsChanged?: OnViewableItemsChanged;
|
|
110
113
|
}
|
|
111
114
|
type ViewabilityConfigCallbackPairs = ViewabilityConfigCallbackPair[];
|
|
112
115
|
type OnViewableItemsChanged = ((info: {
|
|
@@ -114,6 +117,10 @@ type OnViewableItemsChanged = ((info: {
|
|
|
114
117
|
changed: Array<ViewToken>;
|
|
115
118
|
}) => void) | null;
|
|
116
119
|
interface ViewabilityConfig {
|
|
120
|
+
/**
|
|
121
|
+
* A unique ID to identify this viewability config
|
|
122
|
+
*/
|
|
123
|
+
id: string;
|
|
117
124
|
/**
|
|
118
125
|
* Minimum amount of time (in milliseconds) that an item must be physically viewable before the
|
|
119
126
|
* viewability callback will be fired. A high number means that scrolling through content without
|
|
@@ -138,9 +145,16 @@ interface ViewabilityConfig {
|
|
|
138
145
|
*/
|
|
139
146
|
waitForInteraction?: boolean | undefined;
|
|
140
147
|
}
|
|
148
|
+
type ViewabilityCallback = (viewToken: ViewToken) => void;
|
|
149
|
+
interface LegendListRecyclingState<T> {
|
|
150
|
+
item: T;
|
|
151
|
+
prevItem: T | undefined;
|
|
152
|
+
index: number;
|
|
153
|
+
prevIndex: number | undefined;
|
|
154
|
+
}
|
|
141
155
|
|
|
142
156
|
declare const LegendList: <T>(props: LegendListProps<T> & {
|
|
143
157
|
ref?: ForwardedRef<LegendListRef>;
|
|
144
158
|
}) => ReactElement;
|
|
145
159
|
|
|
146
|
-
export { type InternalState, LegendList, type LegendListProps, type LegendListRef, type
|
|
160
|
+
export { type InternalState, LegendList, type LegendListProps, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ViewToken, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange };
|
package/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
|
|
|
18
18
|
distanceFromEnd: number;
|
|
19
19
|
}) => void) | null | undefined;
|
|
20
20
|
keyExtractor?: (item: T, index: number) => string;
|
|
21
|
-
renderItem?: (props:
|
|
21
|
+
renderItem?: (props: LegendListRenderItemProps<T>) => ReactNode;
|
|
22
22
|
onViewableRangeChanged?: (range: ViewableRange<T>) => void;
|
|
23
23
|
ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
|
|
24
24
|
ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
@@ -59,9 +59,12 @@ interface ViewableRange<T> {
|
|
|
59
59
|
end: number;
|
|
60
60
|
items: T[];
|
|
61
61
|
}
|
|
62
|
-
interface
|
|
62
|
+
interface LegendListRenderItemProps<ItemT> {
|
|
63
63
|
item: ItemT;
|
|
64
64
|
index: number;
|
|
65
|
+
useViewability: (configId: string, callback: ViewabilityCallback) => void;
|
|
66
|
+
useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
|
|
67
|
+
useRecyclingState: <T>(updateState: (info: LegendListRecyclingState<ItemT>) => T) => [T, React.Dispatch<T>];
|
|
65
68
|
}
|
|
66
69
|
type LegendListRef = {
|
|
67
70
|
/**
|
|
@@ -106,7 +109,7 @@ interface ViewToken<ItemT = any> {
|
|
|
106
109
|
}
|
|
107
110
|
interface ViewabilityConfigCallbackPair {
|
|
108
111
|
viewabilityConfig: ViewabilityConfig;
|
|
109
|
-
onViewableItemsChanged
|
|
112
|
+
onViewableItemsChanged?: OnViewableItemsChanged;
|
|
110
113
|
}
|
|
111
114
|
type ViewabilityConfigCallbackPairs = ViewabilityConfigCallbackPair[];
|
|
112
115
|
type OnViewableItemsChanged = ((info: {
|
|
@@ -114,6 +117,10 @@ type OnViewableItemsChanged = ((info: {
|
|
|
114
117
|
changed: Array<ViewToken>;
|
|
115
118
|
}) => void) | null;
|
|
116
119
|
interface ViewabilityConfig {
|
|
120
|
+
/**
|
|
121
|
+
* A unique ID to identify this viewability config
|
|
122
|
+
*/
|
|
123
|
+
id: string;
|
|
117
124
|
/**
|
|
118
125
|
* Minimum amount of time (in milliseconds) that an item must be physically viewable before the
|
|
119
126
|
* viewability callback will be fired. A high number means that scrolling through content without
|
|
@@ -138,9 +145,16 @@ interface ViewabilityConfig {
|
|
|
138
145
|
*/
|
|
139
146
|
waitForInteraction?: boolean | undefined;
|
|
140
147
|
}
|
|
148
|
+
type ViewabilityCallback = (viewToken: ViewToken) => void;
|
|
149
|
+
interface LegendListRecyclingState<T> {
|
|
150
|
+
item: T;
|
|
151
|
+
prevItem: T | undefined;
|
|
152
|
+
index: number;
|
|
153
|
+
prevIndex: number | undefined;
|
|
154
|
+
}
|
|
141
155
|
|
|
142
156
|
declare const LegendList: <T>(props: LegendListProps<T> & {
|
|
143
157
|
ref?: ForwardedRef<LegendListRef>;
|
|
144
158
|
}) => ReactElement;
|
|
145
159
|
|
|
146
|
-
export { type InternalState, LegendList, type LegendListProps, type LegendListRef, type
|
|
160
|
+
export { type InternalState, LegendList, type LegendListProps, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ViewToken, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange };
|
package/index.js
CHANGED
|
@@ -31,6 +31,7 @@ LeanView.displayName = "RCTView";
|
|
|
31
31
|
var ContextState = React7__namespace.createContext(null);
|
|
32
32
|
function StateProvider({ children }) {
|
|
33
33
|
const [value] = React7__namespace.useState(() => ({
|
|
34
|
+
hooks: /* @__PURE__ */ new Map(),
|
|
34
35
|
listeners: /* @__PURE__ */ new Map(),
|
|
35
36
|
values: /* @__PURE__ */ new Map()
|
|
36
37
|
}));
|
|
@@ -40,21 +41,37 @@ function useStateContext() {
|
|
|
40
41
|
return React7__namespace.useContext(ContextState);
|
|
41
42
|
}
|
|
42
43
|
function use$(signalName) {
|
|
43
|
-
const {
|
|
44
|
+
const { hooks, values } = React7__namespace.useContext(ContextState);
|
|
44
45
|
const [, forceUpdate] = React7__namespace.useReducer((x) => x + 1, 0);
|
|
45
|
-
|
|
46
|
+
hooks.set(signalName, forceUpdate);
|
|
46
47
|
return values.get(signalName);
|
|
47
48
|
}
|
|
49
|
+
function listen$(ctx, signalName, cb) {
|
|
50
|
+
const { listeners } = ctx;
|
|
51
|
+
let setListeners = listeners.get(signalName);
|
|
52
|
+
if (!setListeners) {
|
|
53
|
+
setListeners = /* @__PURE__ */ new Set();
|
|
54
|
+
listeners.set(signalName, setListeners);
|
|
55
|
+
}
|
|
56
|
+
setListeners.add(cb);
|
|
57
|
+
return () => setListeners.delete(cb);
|
|
58
|
+
}
|
|
48
59
|
function peek$(ctx, signalName) {
|
|
49
60
|
const { values } = ctx;
|
|
50
61
|
return values.get(signalName);
|
|
51
62
|
}
|
|
52
63
|
function set$(ctx, signalName, value) {
|
|
53
64
|
var _a;
|
|
54
|
-
const { listeners, values } = ctx;
|
|
65
|
+
const { listeners, hooks, values } = ctx;
|
|
55
66
|
if (values.get(signalName) !== value) {
|
|
56
67
|
values.set(signalName, value);
|
|
57
|
-
(_a =
|
|
68
|
+
(_a = hooks.get(signalName)) == null ? void 0 : _a();
|
|
69
|
+
const setListeners = listeners.get(signalName);
|
|
70
|
+
if (setListeners) {
|
|
71
|
+
for (const listener of setListeners) {
|
|
72
|
+
listener(value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
58
75
|
}
|
|
59
76
|
}
|
|
60
77
|
|
|
@@ -70,8 +87,15 @@ function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorCompon
|
|
|
70
87
|
if (itemIndex < 0) {
|
|
71
88
|
return null;
|
|
72
89
|
}
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
return /* @__PURE__ */ React7__namespace.createElement(React7__namespace.Fragment, { key: recycleItems ? void 0 : itemIndex }, /* @__PURE__ */ React7__namespace.createElement(RenderedItem, { itemIndex, id, getRenderedItem }), ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
|
|
91
|
+
}
|
|
92
|
+
function RenderedItem({
|
|
93
|
+
itemIndex,
|
|
94
|
+
id,
|
|
95
|
+
getRenderedItem
|
|
96
|
+
}) {
|
|
97
|
+
const renderedItem = getRenderedItem(itemIndex, id);
|
|
98
|
+
return renderedItem;
|
|
75
99
|
}
|
|
76
100
|
var Container = ({
|
|
77
101
|
id,
|
|
@@ -191,6 +215,7 @@ var ListComponent = React7__namespace.memo(function ListComponent2({
|
|
|
191
215
|
ListFooterComponentStyle,
|
|
192
216
|
getRenderedItem,
|
|
193
217
|
updateItemSize,
|
|
218
|
+
addTotalSize,
|
|
194
219
|
refScroller,
|
|
195
220
|
...rest
|
|
196
221
|
}) {
|
|
@@ -214,7 +239,21 @@ var ListComponent = React7__namespace.memo(function ListComponent2({
|
|
|
214
239
|
ref: refScroller
|
|
215
240
|
},
|
|
216
241
|
alignItemsAtEnd && /* @__PURE__ */ React7__namespace.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
|
|
217
|
-
ListHeaderComponent && /* @__PURE__ */ React7__namespace.createElement(
|
|
242
|
+
ListHeaderComponent && /* @__PURE__ */ React7__namespace.createElement(
|
|
243
|
+
reactNative.View,
|
|
244
|
+
{
|
|
245
|
+
style: ListHeaderComponentStyle,
|
|
246
|
+
onLayout: (event) => {
|
|
247
|
+
const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
|
|
248
|
+
const prevSize = peek$(ctx, "headerSize") || 0;
|
|
249
|
+
if (size !== prevSize) {
|
|
250
|
+
set$(ctx, "headerSize", size);
|
|
251
|
+
addTotalSize(size - prevSize);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
getComponent(ListHeaderComponent)
|
|
256
|
+
),
|
|
218
257
|
/* @__PURE__ */ React7__namespace.createElement(
|
|
219
258
|
Containers,
|
|
220
259
|
{
|
|
@@ -230,15 +269,16 @@ var ListComponent = React7__namespace.memo(function ListComponent2({
|
|
|
230
269
|
});
|
|
231
270
|
|
|
232
271
|
// src/viewability.ts
|
|
233
|
-
var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new
|
|
272
|
+
var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
|
|
273
|
+
var mapViewabilityCallbacks = /* @__PURE__ */ new Map();
|
|
234
274
|
function setupViewability(props) {
|
|
235
275
|
let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
|
|
236
|
-
viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs ||
|
|
276
|
+
viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
|
|
237
277
|
{ viewabilityConfig: viewabilityConfig || { viewAreaCoveragePercentThreshold: 0 }, onViewableItemsChanged }
|
|
238
278
|
];
|
|
239
279
|
if (viewabilityConfigCallbackPairs) {
|
|
240
280
|
for (const pair of viewabilityConfigCallbackPairs) {
|
|
241
|
-
mapViewabilityConfigCallbackPairs.set(pair, {
|
|
281
|
+
mapViewabilityConfigCallbackPairs.set(pair.viewabilityConfig.id, {
|
|
242
282
|
viewableItems: [],
|
|
243
283
|
start: -1,
|
|
244
284
|
end: -1,
|
|
@@ -251,7 +291,9 @@ function setupViewability(props) {
|
|
|
251
291
|
}
|
|
252
292
|
function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, getId, scrollSize, start, end) {
|
|
253
293
|
for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
|
|
254
|
-
const viewabilityState = mapViewabilityConfigCallbackPairs.get(
|
|
294
|
+
const viewabilityState = mapViewabilityConfigCallbackPairs.get(
|
|
295
|
+
viewabilityConfigCallbackPair.viewabilityConfig.id
|
|
296
|
+
);
|
|
255
297
|
viewabilityState.start = start;
|
|
256
298
|
viewabilityState.end = end;
|
|
257
299
|
if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
|
|
@@ -266,13 +308,14 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, getId,
|
|
|
266
308
|
}
|
|
267
309
|
}
|
|
268
310
|
function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getId, state, ctx, scrollSize) {
|
|
269
|
-
|
|
270
|
-
const
|
|
311
|
+
const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
|
|
312
|
+
const configId = viewabilityConfig.id;
|
|
313
|
+
const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
|
|
271
314
|
const { viewableItems: previousViewableItems, start, previousStart, end, previousEnd } = viewabilityState;
|
|
272
315
|
const changed = [];
|
|
273
316
|
if (previousViewableItems) {
|
|
274
317
|
for (const viewToken of previousViewableItems) {
|
|
275
|
-
if (
|
|
318
|
+
if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize)) {
|
|
276
319
|
viewToken.isViewable = false;
|
|
277
320
|
changed.push(viewToken);
|
|
278
321
|
}
|
|
@@ -283,7 +326,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
|
|
|
283
326
|
const item = data[i];
|
|
284
327
|
if (item) {
|
|
285
328
|
const key = getId(i);
|
|
286
|
-
if (isViewable(state, ctx,
|
|
329
|
+
if (isViewable(state, ctx, viewabilityConfig, key, scrollSize)) {
|
|
287
330
|
const viewToken = {
|
|
288
331
|
item,
|
|
289
332
|
key,
|
|
@@ -299,7 +342,15 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
|
|
|
299
342
|
}
|
|
300
343
|
Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
|
|
301
344
|
if (changed.length > 0) {
|
|
302
|
-
|
|
345
|
+
console.log("changed", changed);
|
|
346
|
+
viewabilityState.viewableItems = viewableItems;
|
|
347
|
+
for (let i = 0; i < changed.length; i++) {
|
|
348
|
+
const change = changed[i];
|
|
349
|
+
maybeUpdateViewabilityCallback(configId, change);
|
|
350
|
+
}
|
|
351
|
+
if (onViewableItemsChanged) {
|
|
352
|
+
onViewableItemsChanged({ viewableItems, changed });
|
|
353
|
+
}
|
|
303
354
|
}
|
|
304
355
|
}
|
|
305
356
|
function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
|
|
@@ -319,6 +370,18 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
|
|
|
319
370
|
const percent = 100 * (visibleHeight / (viewAreaMode ? scrollSize : size));
|
|
320
371
|
return percent >= viewablePercentThreshold;
|
|
321
372
|
}
|
|
373
|
+
function maybeUpdateViewabilityCallback(configId, viewToken) {
|
|
374
|
+
const key = viewToken.key + configId;
|
|
375
|
+
const cb = mapViewabilityCallbacks.get(key);
|
|
376
|
+
cb == null ? void 0 : cb(viewToken);
|
|
377
|
+
}
|
|
378
|
+
function registerViewabilityCallback(itemKey, configId, callback) {
|
|
379
|
+
const key = itemKey + configId;
|
|
380
|
+
mapViewabilityCallbacks.set(key, callback);
|
|
381
|
+
return () => {
|
|
382
|
+
mapViewabilityCallbacks.delete(key);
|
|
383
|
+
};
|
|
384
|
+
}
|
|
322
385
|
|
|
323
386
|
// src/LegendList.tsx
|
|
324
387
|
var DEFAULT_SCROLL_BUFFER = 0;
|
|
@@ -336,8 +399,8 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
|
|
|
336
399
|
style: styleProp,
|
|
337
400
|
contentContainerStyle: contentContainerStyleProp,
|
|
338
401
|
initialNumContainers,
|
|
339
|
-
drawDistance,
|
|
340
|
-
recycleItems =
|
|
402
|
+
drawDistance = 250,
|
|
403
|
+
recycleItems = false,
|
|
341
404
|
onEndReachedThreshold = 0.5,
|
|
342
405
|
maintainScrollAtEnd = false,
|
|
343
406
|
maintainScrollAtEndThreshold = 0.1,
|
|
@@ -420,7 +483,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
|
|
|
420
483
|
refState.current.data = data;
|
|
421
484
|
set$(ctx, "numItems", data.length);
|
|
422
485
|
set$(ctx, "stylePaddingTop", (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
|
|
423
|
-
const addTotalSize = (add) => {
|
|
486
|
+
const addTotalSize = React7.useCallback((add) => {
|
|
424
487
|
const prev = refState.current.totalSize;
|
|
425
488
|
refState.current.totalSize += add;
|
|
426
489
|
const totalSize = refState.current.totalSize;
|
|
@@ -438,17 +501,65 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
|
|
|
438
501
|
} else if (!refState.current.animFrameTotalSize) {
|
|
439
502
|
refState.current.animFrameTotalSize = requestAnimationFrame(doAdd);
|
|
440
503
|
}
|
|
441
|
-
};
|
|
504
|
+
}, []);
|
|
442
505
|
const getRenderedItem = React7.useCallback(
|
|
443
|
-
(index) => {
|
|
506
|
+
(index, containerIndex) => {
|
|
444
507
|
var _a2;
|
|
445
508
|
const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
|
|
446
509
|
if (!data2) {
|
|
447
510
|
return null;
|
|
448
511
|
}
|
|
512
|
+
const itemKey = getId(index);
|
|
513
|
+
const useViewability = (configId, callback) => {
|
|
514
|
+
React7.useEffect(() => registerViewabilityCallback(itemKey, configId, callback), []);
|
|
515
|
+
};
|
|
516
|
+
const useRecyclingEffect = (effect) => {
|
|
517
|
+
React7.useEffect(() => {
|
|
518
|
+
let prevIndex = index;
|
|
519
|
+
let prevItem = data2[index];
|
|
520
|
+
const signal = `containerIndex${containerIndex}`;
|
|
521
|
+
listen$(ctx, signal, () => {
|
|
522
|
+
var _a3;
|
|
523
|
+
const data3 = (_a3 = refState.current) == null ? void 0 : _a3.data;
|
|
524
|
+
if (data3) {
|
|
525
|
+
const newIndex = peek$(ctx, signal);
|
|
526
|
+
const newItem = data3[newIndex];
|
|
527
|
+
if (newItem) {
|
|
528
|
+
effect({
|
|
529
|
+
index: newIndex,
|
|
530
|
+
item: newItem,
|
|
531
|
+
prevIndex,
|
|
532
|
+
prevItem
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
prevIndex = newIndex;
|
|
536
|
+
prevItem = newItem;
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
}, []);
|
|
540
|
+
};
|
|
541
|
+
const useRecyclingState = (updateState) => {
|
|
542
|
+
const stateInfo = React7.useState(
|
|
543
|
+
() => updateState({
|
|
544
|
+
index,
|
|
545
|
+
item: data2[index],
|
|
546
|
+
prevIndex: void 0,
|
|
547
|
+
prevItem: void 0
|
|
548
|
+
})
|
|
549
|
+
);
|
|
550
|
+
useRecyclingEffect((state) => {
|
|
551
|
+
const newState = updateState(state);
|
|
552
|
+
console.log("setting state", newState);
|
|
553
|
+
stateInfo[1](newState);
|
|
554
|
+
});
|
|
555
|
+
return stateInfo;
|
|
556
|
+
};
|
|
449
557
|
const renderedItem = renderItem == null ? void 0 : renderItem({
|
|
450
558
|
item: data2[index],
|
|
451
|
-
index
|
|
559
|
+
index,
|
|
560
|
+
useViewability,
|
|
561
|
+
useRecyclingEffect,
|
|
562
|
+
useRecyclingState
|
|
452
563
|
});
|
|
453
564
|
return renderedItem;
|
|
454
565
|
},
|
|
@@ -757,7 +868,8 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
|
|
|
757
868
|
handleScroll,
|
|
758
869
|
onLayout,
|
|
759
870
|
recycleItems,
|
|
760
|
-
alignItemsAtEnd
|
|
871
|
+
alignItemsAtEnd,
|
|
872
|
+
addTotalSize
|
|
761
873
|
}
|
|
762
874
|
);
|
|
763
875
|
});
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React7 from 'react';
|
|
2
|
-
import { forwardRef, useRef, useMemo, useCallback, useEffect, useImperativeHandle } from 'react';
|
|
2
|
+
import { forwardRef, useRef, useMemo, useCallback, useEffect, useImperativeHandle, useState } from 'react';
|
|
3
3
|
import { ScrollView, View, StyleSheet, Dimensions, unstable_batchedUpdates } from 'react-native';
|
|
4
4
|
|
|
5
5
|
// src/LegendList.tsx
|
|
@@ -10,6 +10,7 @@ LeanView.displayName = "RCTView";
|
|
|
10
10
|
var ContextState = React7.createContext(null);
|
|
11
11
|
function StateProvider({ children }) {
|
|
12
12
|
const [value] = React7.useState(() => ({
|
|
13
|
+
hooks: /* @__PURE__ */ new Map(),
|
|
13
14
|
listeners: /* @__PURE__ */ new Map(),
|
|
14
15
|
values: /* @__PURE__ */ new Map()
|
|
15
16
|
}));
|
|
@@ -19,21 +20,37 @@ function useStateContext() {
|
|
|
19
20
|
return React7.useContext(ContextState);
|
|
20
21
|
}
|
|
21
22
|
function use$(signalName) {
|
|
22
|
-
const {
|
|
23
|
+
const { hooks, values } = React7.useContext(ContextState);
|
|
23
24
|
const [, forceUpdate] = React7.useReducer((x) => x + 1, 0);
|
|
24
|
-
|
|
25
|
+
hooks.set(signalName, forceUpdate);
|
|
25
26
|
return values.get(signalName);
|
|
26
27
|
}
|
|
28
|
+
function listen$(ctx, signalName, cb) {
|
|
29
|
+
const { listeners } = ctx;
|
|
30
|
+
let setListeners = listeners.get(signalName);
|
|
31
|
+
if (!setListeners) {
|
|
32
|
+
setListeners = /* @__PURE__ */ new Set();
|
|
33
|
+
listeners.set(signalName, setListeners);
|
|
34
|
+
}
|
|
35
|
+
setListeners.add(cb);
|
|
36
|
+
return () => setListeners.delete(cb);
|
|
37
|
+
}
|
|
27
38
|
function peek$(ctx, signalName) {
|
|
28
39
|
const { values } = ctx;
|
|
29
40
|
return values.get(signalName);
|
|
30
41
|
}
|
|
31
42
|
function set$(ctx, signalName, value) {
|
|
32
43
|
var _a;
|
|
33
|
-
const { listeners, values } = ctx;
|
|
44
|
+
const { listeners, hooks, values } = ctx;
|
|
34
45
|
if (values.get(signalName) !== value) {
|
|
35
46
|
values.set(signalName, value);
|
|
36
|
-
(_a =
|
|
47
|
+
(_a = hooks.get(signalName)) == null ? void 0 : _a();
|
|
48
|
+
const setListeners = listeners.get(signalName);
|
|
49
|
+
if (setListeners) {
|
|
50
|
+
for (const listener of setListeners) {
|
|
51
|
+
listener(value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
37
54
|
}
|
|
38
55
|
}
|
|
39
56
|
|
|
@@ -49,8 +66,15 @@ function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorCompon
|
|
|
49
66
|
if (itemIndex < 0) {
|
|
50
67
|
return null;
|
|
51
68
|
}
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
return /* @__PURE__ */ React7.createElement(React7.Fragment, { key: recycleItems ? void 0 : itemIndex }, /* @__PURE__ */ React7.createElement(RenderedItem, { itemIndex, id, getRenderedItem }), ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
|
|
70
|
+
}
|
|
71
|
+
function RenderedItem({
|
|
72
|
+
itemIndex,
|
|
73
|
+
id,
|
|
74
|
+
getRenderedItem
|
|
75
|
+
}) {
|
|
76
|
+
const renderedItem = getRenderedItem(itemIndex, id);
|
|
77
|
+
return renderedItem;
|
|
54
78
|
}
|
|
55
79
|
var Container = ({
|
|
56
80
|
id,
|
|
@@ -170,6 +194,7 @@ var ListComponent = React7.memo(function ListComponent2({
|
|
|
170
194
|
ListFooterComponentStyle,
|
|
171
195
|
getRenderedItem,
|
|
172
196
|
updateItemSize,
|
|
197
|
+
addTotalSize,
|
|
173
198
|
refScroller,
|
|
174
199
|
...rest
|
|
175
200
|
}) {
|
|
@@ -193,7 +218,21 @@ var ListComponent = React7.memo(function ListComponent2({
|
|
|
193
218
|
ref: refScroller
|
|
194
219
|
},
|
|
195
220
|
alignItemsAtEnd && /* @__PURE__ */ React7.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
|
|
196
|
-
ListHeaderComponent && /* @__PURE__ */ React7.createElement(
|
|
221
|
+
ListHeaderComponent && /* @__PURE__ */ React7.createElement(
|
|
222
|
+
View,
|
|
223
|
+
{
|
|
224
|
+
style: ListHeaderComponentStyle,
|
|
225
|
+
onLayout: (event) => {
|
|
226
|
+
const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
|
|
227
|
+
const prevSize = peek$(ctx, "headerSize") || 0;
|
|
228
|
+
if (size !== prevSize) {
|
|
229
|
+
set$(ctx, "headerSize", size);
|
|
230
|
+
addTotalSize(size - prevSize);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
getComponent(ListHeaderComponent)
|
|
235
|
+
),
|
|
197
236
|
/* @__PURE__ */ React7.createElement(
|
|
198
237
|
Containers,
|
|
199
238
|
{
|
|
@@ -209,15 +248,16 @@ var ListComponent = React7.memo(function ListComponent2({
|
|
|
209
248
|
});
|
|
210
249
|
|
|
211
250
|
// src/viewability.ts
|
|
212
|
-
var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new
|
|
251
|
+
var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
|
|
252
|
+
var mapViewabilityCallbacks = /* @__PURE__ */ new Map();
|
|
213
253
|
function setupViewability(props) {
|
|
214
254
|
let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
|
|
215
|
-
viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs ||
|
|
255
|
+
viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
|
|
216
256
|
{ viewabilityConfig: viewabilityConfig || { viewAreaCoveragePercentThreshold: 0 }, onViewableItemsChanged }
|
|
217
257
|
];
|
|
218
258
|
if (viewabilityConfigCallbackPairs) {
|
|
219
259
|
for (const pair of viewabilityConfigCallbackPairs) {
|
|
220
|
-
mapViewabilityConfigCallbackPairs.set(pair, {
|
|
260
|
+
mapViewabilityConfigCallbackPairs.set(pair.viewabilityConfig.id, {
|
|
221
261
|
viewableItems: [],
|
|
222
262
|
start: -1,
|
|
223
263
|
end: -1,
|
|
@@ -230,7 +270,9 @@ function setupViewability(props) {
|
|
|
230
270
|
}
|
|
231
271
|
function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, getId, scrollSize, start, end) {
|
|
232
272
|
for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
|
|
233
|
-
const viewabilityState = mapViewabilityConfigCallbackPairs.get(
|
|
273
|
+
const viewabilityState = mapViewabilityConfigCallbackPairs.get(
|
|
274
|
+
viewabilityConfigCallbackPair.viewabilityConfig.id
|
|
275
|
+
);
|
|
234
276
|
viewabilityState.start = start;
|
|
235
277
|
viewabilityState.end = end;
|
|
236
278
|
if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
|
|
@@ -245,13 +287,14 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, getId,
|
|
|
245
287
|
}
|
|
246
288
|
}
|
|
247
289
|
function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getId, state, ctx, scrollSize) {
|
|
248
|
-
|
|
249
|
-
const
|
|
290
|
+
const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
|
|
291
|
+
const configId = viewabilityConfig.id;
|
|
292
|
+
const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
|
|
250
293
|
const { viewableItems: previousViewableItems, start, previousStart, end, previousEnd } = viewabilityState;
|
|
251
294
|
const changed = [];
|
|
252
295
|
if (previousViewableItems) {
|
|
253
296
|
for (const viewToken of previousViewableItems) {
|
|
254
|
-
if (
|
|
297
|
+
if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize)) {
|
|
255
298
|
viewToken.isViewable = false;
|
|
256
299
|
changed.push(viewToken);
|
|
257
300
|
}
|
|
@@ -262,7 +305,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
|
|
|
262
305
|
const item = data[i];
|
|
263
306
|
if (item) {
|
|
264
307
|
const key = getId(i);
|
|
265
|
-
if (isViewable(state, ctx,
|
|
308
|
+
if (isViewable(state, ctx, viewabilityConfig, key, scrollSize)) {
|
|
266
309
|
const viewToken = {
|
|
267
310
|
item,
|
|
268
311
|
key,
|
|
@@ -278,7 +321,15 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
|
|
|
278
321
|
}
|
|
279
322
|
Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
|
|
280
323
|
if (changed.length > 0) {
|
|
281
|
-
|
|
324
|
+
console.log("changed", changed);
|
|
325
|
+
viewabilityState.viewableItems = viewableItems;
|
|
326
|
+
for (let i = 0; i < changed.length; i++) {
|
|
327
|
+
const change = changed[i];
|
|
328
|
+
maybeUpdateViewabilityCallback(configId, change);
|
|
329
|
+
}
|
|
330
|
+
if (onViewableItemsChanged) {
|
|
331
|
+
onViewableItemsChanged({ viewableItems, changed });
|
|
332
|
+
}
|
|
282
333
|
}
|
|
283
334
|
}
|
|
284
335
|
function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
|
|
@@ -298,6 +349,18 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
|
|
|
298
349
|
const percent = 100 * (visibleHeight / (viewAreaMode ? scrollSize : size));
|
|
299
350
|
return percent >= viewablePercentThreshold;
|
|
300
351
|
}
|
|
352
|
+
function maybeUpdateViewabilityCallback(configId, viewToken) {
|
|
353
|
+
const key = viewToken.key + configId;
|
|
354
|
+
const cb = mapViewabilityCallbacks.get(key);
|
|
355
|
+
cb == null ? void 0 : cb(viewToken);
|
|
356
|
+
}
|
|
357
|
+
function registerViewabilityCallback(itemKey, configId, callback) {
|
|
358
|
+
const key = itemKey + configId;
|
|
359
|
+
mapViewabilityCallbacks.set(key, callback);
|
|
360
|
+
return () => {
|
|
361
|
+
mapViewabilityCallbacks.delete(key);
|
|
362
|
+
};
|
|
363
|
+
}
|
|
301
364
|
|
|
302
365
|
// src/LegendList.tsx
|
|
303
366
|
var DEFAULT_SCROLL_BUFFER = 0;
|
|
@@ -315,8 +378,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
315
378
|
style: styleProp,
|
|
316
379
|
contentContainerStyle: contentContainerStyleProp,
|
|
317
380
|
initialNumContainers,
|
|
318
|
-
drawDistance,
|
|
319
|
-
recycleItems =
|
|
381
|
+
drawDistance = 250,
|
|
382
|
+
recycleItems = false,
|
|
320
383
|
onEndReachedThreshold = 0.5,
|
|
321
384
|
maintainScrollAtEnd = false,
|
|
322
385
|
maintainScrollAtEndThreshold = 0.1,
|
|
@@ -399,7 +462,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
399
462
|
refState.current.data = data;
|
|
400
463
|
set$(ctx, "numItems", data.length);
|
|
401
464
|
set$(ctx, "stylePaddingTop", (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
|
|
402
|
-
const addTotalSize = (add) => {
|
|
465
|
+
const addTotalSize = useCallback((add) => {
|
|
403
466
|
const prev = refState.current.totalSize;
|
|
404
467
|
refState.current.totalSize += add;
|
|
405
468
|
const totalSize = refState.current.totalSize;
|
|
@@ -417,17 +480,65 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
417
480
|
} else if (!refState.current.animFrameTotalSize) {
|
|
418
481
|
refState.current.animFrameTotalSize = requestAnimationFrame(doAdd);
|
|
419
482
|
}
|
|
420
|
-
};
|
|
483
|
+
}, []);
|
|
421
484
|
const getRenderedItem = useCallback(
|
|
422
|
-
(index) => {
|
|
485
|
+
(index, containerIndex) => {
|
|
423
486
|
var _a2;
|
|
424
487
|
const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
|
|
425
488
|
if (!data2) {
|
|
426
489
|
return null;
|
|
427
490
|
}
|
|
491
|
+
const itemKey = getId(index);
|
|
492
|
+
const useViewability = (configId, callback) => {
|
|
493
|
+
useEffect(() => registerViewabilityCallback(itemKey, configId, callback), []);
|
|
494
|
+
};
|
|
495
|
+
const useRecyclingEffect = (effect) => {
|
|
496
|
+
useEffect(() => {
|
|
497
|
+
let prevIndex = index;
|
|
498
|
+
let prevItem = data2[index];
|
|
499
|
+
const signal = `containerIndex${containerIndex}`;
|
|
500
|
+
listen$(ctx, signal, () => {
|
|
501
|
+
var _a3;
|
|
502
|
+
const data3 = (_a3 = refState.current) == null ? void 0 : _a3.data;
|
|
503
|
+
if (data3) {
|
|
504
|
+
const newIndex = peek$(ctx, signal);
|
|
505
|
+
const newItem = data3[newIndex];
|
|
506
|
+
if (newItem) {
|
|
507
|
+
effect({
|
|
508
|
+
index: newIndex,
|
|
509
|
+
item: newItem,
|
|
510
|
+
prevIndex,
|
|
511
|
+
prevItem
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
prevIndex = newIndex;
|
|
515
|
+
prevItem = newItem;
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}, []);
|
|
519
|
+
};
|
|
520
|
+
const useRecyclingState = (updateState) => {
|
|
521
|
+
const stateInfo = useState(
|
|
522
|
+
() => updateState({
|
|
523
|
+
index,
|
|
524
|
+
item: data2[index],
|
|
525
|
+
prevIndex: void 0,
|
|
526
|
+
prevItem: void 0
|
|
527
|
+
})
|
|
528
|
+
);
|
|
529
|
+
useRecyclingEffect((state) => {
|
|
530
|
+
const newState = updateState(state);
|
|
531
|
+
console.log("setting state", newState);
|
|
532
|
+
stateInfo[1](newState);
|
|
533
|
+
});
|
|
534
|
+
return stateInfo;
|
|
535
|
+
};
|
|
428
536
|
const renderedItem = renderItem == null ? void 0 : renderItem({
|
|
429
537
|
item: data2[index],
|
|
430
|
-
index
|
|
538
|
+
index,
|
|
539
|
+
useViewability,
|
|
540
|
+
useRecyclingEffect,
|
|
541
|
+
useRecyclingState
|
|
431
542
|
});
|
|
432
543
|
return renderedItem;
|
|
433
544
|
},
|
|
@@ -736,7 +847,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
736
847
|
handleScroll,
|
|
737
848
|
onLayout,
|
|
738
849
|
recycleItems,
|
|
739
|
-
alignItemsAtEnd
|
|
850
|
+
alignItemsAtEnd,
|
|
851
|
+
addTotalSize
|
|
740
852
|
}
|
|
741
853
|
);
|
|
742
854
|
});
|