@react-native-macos/virtualized-lists 0.78.6 → 0.79.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/Lists/FillRateHelper.js +1 -1
- package/Lists/ListMetricsAggregator.js +7 -4
- package/Lists/StateSafePureComponent.js +2 -2
- package/Lists/ViewabilityHelper.js +6 -3
- package/Lists/VirtualizedList.js +57 -46
- package/Lists/VirtualizedListCellRenderer.js +16 -12
- package/Lists/VirtualizedListProps.js +20 -36
- package/Lists/VirtualizedSectionList.js +16 -16
- package/Utilities/clamp.js +1 -1
- package/Utilities/infoLog.js +1 -1
- package/index.js +9 -8
- package/package.json +1 -1
- package/Interaction/Batchinator.js +0 -85
package/Lists/FillRateHelper.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* @format
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type {
|
|
12
|
-
import type {
|
|
11
|
+
import type {VirtualizedListProps} from './VirtualizedListProps';
|
|
12
|
+
import type {LayoutRectangle} from 'react-native/Libraries/Types/CoreEventTypes';
|
|
13
13
|
|
|
14
14
|
import {keyExtractor as defaultKeyExtractor} from './VirtualizeUtils';
|
|
15
15
|
import invariant from 'invariant';
|
|
@@ -82,7 +82,7 @@ export default class ListMetricsAggregator {
|
|
|
82
82
|
cellIndex: number,
|
|
83
83
|
cellKey: string,
|
|
84
84
|
orientation: ListOrientation,
|
|
85
|
-
layout:
|
|
85
|
+
layout: LayoutRectangle,
|
|
86
86
|
}): boolean {
|
|
87
87
|
this._invalidateIfOrientationChanged(orientation);
|
|
88
88
|
|
|
@@ -264,7 +264,10 @@ export default class ListMetricsAggregator {
|
|
|
264
264
|
* Finds the flow-relative offset (e.g. starting from the left in LTR, but
|
|
265
265
|
* right in RTL) from a layout box.
|
|
266
266
|
*/
|
|
267
|
-
flowRelativeOffset(
|
|
267
|
+
flowRelativeOffset(
|
|
268
|
+
layout: LayoutRectangle,
|
|
269
|
+
referenceContentLength?: ?number,
|
|
270
|
+
): number {
|
|
268
271
|
const {horizontal, rtl} = this._orientation;
|
|
269
272
|
|
|
270
273
|
if (horizontal && rtl) {
|
|
@@ -31,8 +31,8 @@ export default class StateSafePureComponent<
|
|
|
31
31
|
this._installSetStateHooks();
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
setState(
|
|
35
|
-
partialState: ?(
|
|
34
|
+
setState<K: $Keys<State>>(
|
|
35
|
+
partialState: ?(Pick<State, K> | ((State, Props) => ?Pick<State, K>)),
|
|
36
36
|
callback?: () => mixed,
|
|
37
37
|
): void {
|
|
38
38
|
if (typeof partialState === 'function') {
|
|
@@ -35,7 +35,10 @@ export type ViewabilityConfigCallbackPair = {
|
|
|
35
35
|
...
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export type
|
|
38
|
+
export type ViewabilityConfigCallbackPairs =
|
|
39
|
+
Array<ViewabilityConfigCallbackPair>;
|
|
40
|
+
|
|
41
|
+
export type ViewabilityConfig = $ReadOnly<{
|
|
39
42
|
/**
|
|
40
43
|
* Minimum amount of time (in milliseconds) that an item must be physically viewable before the
|
|
41
44
|
* viewability callback will be fired. A high number means that scrolling through content without
|
|
@@ -62,7 +65,7 @@ export type ViewabilityConfig = $ReadOnly<{|
|
|
|
62
65
|
* render.
|
|
63
66
|
*/
|
|
64
67
|
waitForInteraction?: boolean,
|
|
65
|
-
|
|
68
|
+
}>;
|
|
66
69
|
|
|
67
70
|
/**
|
|
68
71
|
* A Utility class for calculating viewable items based on current metrics like scroll position and
|
|
@@ -346,4 +349,4 @@ function _isEntirelyVisible(
|
|
|
346
349
|
return top >= 0 && bottom <= viewportHeight && bottom > top;
|
|
347
350
|
}
|
|
348
351
|
|
|
349
|
-
|
|
352
|
+
export default ViewabilityHelper;
|
package/Lists/VirtualizedList.js
CHANGED
|
@@ -12,20 +12,19 @@ import type {CellMetricProps, ListOrientation} from './ListMetricsAggregator';
|
|
|
12
12
|
import type {ViewToken} from './ViewabilityHelper';
|
|
13
13
|
import type {
|
|
14
14
|
Item,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
RenderItemType,
|
|
15
|
+
ListRenderItem,
|
|
16
|
+
ListRenderItemInfo,
|
|
18
17
|
Separators,
|
|
18
|
+
VirtualizedListProps,
|
|
19
19
|
} from './VirtualizedListProps';
|
|
20
20
|
import type {ScrollResponderType} from 'react-native/Libraries/Components/ScrollView/ScrollView';
|
|
21
21
|
import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
|
|
22
22
|
import type {
|
|
23
|
-
|
|
23
|
+
LayoutChangeEvent,
|
|
24
24
|
ScrollEvent,
|
|
25
25
|
} from 'react-native/Libraries/Types/CoreEventTypes';
|
|
26
26
|
import type {KeyEvent} from 'react-native/Libraries/Types/CoreEventTypes'; // [macOS]
|
|
27
27
|
|
|
28
|
-
import Batchinator from '../Interaction/Batchinator';
|
|
29
28
|
import clamp from '../Utilities/clamp';
|
|
30
29
|
import infoLog from '../Utilities/infoLog';
|
|
31
30
|
import {CellRenderMask} from './CellRenderMask';
|
|
@@ -65,7 +64,7 @@ import {
|
|
|
65
64
|
findNodeHandle,
|
|
66
65
|
} from 'react-native';
|
|
67
66
|
|
|
68
|
-
export type {
|
|
67
|
+
export type {ListRenderItemInfo, ListRenderItem, Separators};
|
|
69
68
|
|
|
70
69
|
const ON_EDGE_REACHED_EPSILON = 0.001;
|
|
71
70
|
|
|
@@ -125,7 +124,10 @@ function getScrollingThreshold(threshold: number, visibleLength: number) {
|
|
|
125
124
|
* - As an effort to remove defaultProps, use helper functions when referencing certain props
|
|
126
125
|
*
|
|
127
126
|
*/
|
|
128
|
-
class VirtualizedList extends StateSafePureComponent<
|
|
127
|
+
class VirtualizedList extends StateSafePureComponent<
|
|
128
|
+
VirtualizedListProps,
|
|
129
|
+
State,
|
|
130
|
+
> {
|
|
129
131
|
static contextType: typeof VirtualizedListContext = VirtualizedListContext;
|
|
130
132
|
|
|
131
133
|
// scrollToEnd may be janky without getItemLayout prop
|
|
@@ -262,6 +264,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
262
264
|
return;
|
|
263
265
|
}
|
|
264
266
|
|
|
267
|
+
// $FlowFixMe[incompatible-call]
|
|
265
268
|
scrollRef.scrollTo({
|
|
266
269
|
animated,
|
|
267
270
|
...this._scrollToParamsFromOffset(offset),
|
|
@@ -341,10 +344,10 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
341
344
|
}
|
|
342
345
|
}
|
|
343
346
|
|
|
344
|
-
getScrollRef():
|
|
345
|
-
|
|
346
|
-
| ?React.ElementRef<typeof View> {
|
|
347
|
+
getScrollRef(): ?React.ElementRef<typeof ScrollView> {
|
|
348
|
+
// $FlowFixMe[prop-missing]
|
|
347
349
|
if (this._scrollRef && this._scrollRef.getScrollRef) {
|
|
350
|
+
// $FlowFixMe[not-a-function]
|
|
348
351
|
return this._scrollRef.getScrollRef();
|
|
349
352
|
} else {
|
|
350
353
|
return this._scrollRef;
|
|
@@ -395,15 +398,11 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
395
398
|
|
|
396
399
|
state: State;
|
|
397
400
|
|
|
398
|
-
constructor(props:
|
|
401
|
+
constructor(props: VirtualizedListProps) {
|
|
399
402
|
super(props);
|
|
400
403
|
this._checkProps(props);
|
|
401
404
|
|
|
402
405
|
this._fillRateHelper = new FillRateHelper(this._listMetrics);
|
|
403
|
-
this._updateCellsToRenderBatcher = new Batchinator(
|
|
404
|
-
this._updateCellsToRender,
|
|
405
|
-
this.props.updateCellsBatchingPeriod ?? 50,
|
|
406
|
-
);
|
|
407
406
|
|
|
408
407
|
if (this.props.viewabilityConfigCallbackPairs) {
|
|
409
408
|
this._viewabilityTuples = this.props.viewabilityConfigCallbackPairs.map(
|
|
@@ -446,7 +445,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
446
445
|
};
|
|
447
446
|
}
|
|
448
447
|
|
|
449
|
-
_checkProps(props:
|
|
448
|
+
_checkProps(props: VirtualizedListProps) {
|
|
450
449
|
const {onScroll, windowSize, getItemCount, data, initialScrollIndex} =
|
|
451
450
|
props;
|
|
452
451
|
|
|
@@ -495,7 +494,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
495
494
|
}
|
|
496
495
|
|
|
497
496
|
static _findItemIndexWithKey(
|
|
498
|
-
props:
|
|
497
|
+
props: VirtualizedListProps,
|
|
499
498
|
key: string,
|
|
500
499
|
hint: ?number,
|
|
501
500
|
): ?number {
|
|
@@ -517,9 +516,9 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
517
516
|
|
|
518
517
|
static _getItemKey(
|
|
519
518
|
props: {
|
|
520
|
-
data:
|
|
521
|
-
getItem:
|
|
522
|
-
keyExtractor:
|
|
519
|
+
data: VirtualizedListProps['data'],
|
|
520
|
+
getItem: VirtualizedListProps['getItem'],
|
|
521
|
+
keyExtractor: VirtualizedListProps['keyExtractor'],
|
|
523
522
|
...
|
|
524
523
|
},
|
|
525
524
|
index: number,
|
|
@@ -529,7 +528,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
529
528
|
}
|
|
530
529
|
|
|
531
530
|
static _createRenderMask(
|
|
532
|
-
props:
|
|
531
|
+
props: VirtualizedListProps,
|
|
533
532
|
cellsAroundViewport: {first: number, last: number},
|
|
534
533
|
additionalRegions?: ?$ReadOnlyArray<{first: number, last: number}>,
|
|
535
534
|
): CellRenderMask {
|
|
@@ -572,7 +571,10 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
572
571
|
return renderMask;
|
|
573
572
|
}
|
|
574
573
|
|
|
575
|
-
static _initialRenderRegion(props:
|
|
574
|
+
static _initialRenderRegion(props: VirtualizedListProps): {
|
|
575
|
+
first: number,
|
|
576
|
+
last: number,
|
|
577
|
+
} {
|
|
576
578
|
const itemCount = props.getItemCount(props.data);
|
|
577
579
|
|
|
578
580
|
const firstCellIndex = Math.max(
|
|
@@ -593,7 +595,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
593
595
|
}
|
|
594
596
|
|
|
595
597
|
static _ensureClosestStickyHeader(
|
|
596
|
-
props:
|
|
598
|
+
props: VirtualizedListProps,
|
|
597
599
|
stickyIndicesSet: Set<number>,
|
|
598
600
|
renderMask: CellRenderMask,
|
|
599
601
|
cellIdx: number,
|
|
@@ -609,7 +611,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
609
611
|
}
|
|
610
612
|
|
|
611
613
|
_adjustCellsAroundViewport(
|
|
612
|
-
props:
|
|
614
|
+
props: VirtualizedListProps,
|
|
613
615
|
cellsAroundViewport: {first: number, last: number},
|
|
614
616
|
pendingScrollUpdateCount: number,
|
|
615
617
|
): {first: number, last: number} {
|
|
@@ -713,14 +715,17 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
713
715
|
if (this._isNestedWithSameOrientation()) {
|
|
714
716
|
this.context.unregisterAsNestedChild({ref: this});
|
|
715
717
|
}
|
|
716
|
-
this.
|
|
718
|
+
clearTimeout(this._updateCellsToRenderTimeoutID);
|
|
717
719
|
this._viewabilityTuples.forEach(tuple => {
|
|
718
720
|
tuple.viewabilityHelper.dispose();
|
|
719
721
|
});
|
|
720
722
|
this._fillRateHelper.deactivateAndFlush();
|
|
721
723
|
}
|
|
722
724
|
|
|
723
|
-
static getDerivedStateFromProps(
|
|
725
|
+
static getDerivedStateFromProps(
|
|
726
|
+
newProps: VirtualizedListProps,
|
|
727
|
+
prevState: State,
|
|
728
|
+
): State {
|
|
724
729
|
// first and last could be stale (e.g. if a new, shorter items props is passed in), so we make
|
|
725
730
|
// sure we're rendering a reasonable range here.
|
|
726
731
|
const itemCount = newProps.getItemCount(newProps.data);
|
|
@@ -864,7 +869,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
864
869
|
|
|
865
870
|
static _constrainToItemCount(
|
|
866
871
|
cells: {first: number, last: number},
|
|
867
|
-
props:
|
|
872
|
+
props: VirtualizedListProps,
|
|
868
873
|
): {first: number, last: number} {
|
|
869
874
|
const itemCount = props.getItemCount(props.data);
|
|
870
875
|
const lastPossibleCellIndex = itemCount - 1;
|
|
@@ -993,7 +998,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
993
998
|
collapsable={Platform.OS !== 'macos'} // [macOS]
|
|
994
999
|
key="$empty">
|
|
995
1000
|
{React.cloneElement(element, {
|
|
996
|
-
onLayout: (event:
|
|
1001
|
+
onLayout: (event: LayoutChangeEvent) => {
|
|
997
1002
|
this._onLayoutEmpty(event);
|
|
998
1003
|
// $FlowFixMe[prop-missing] React.Element internal inspection
|
|
999
1004
|
if (element.props.onLayout) {
|
|
@@ -1157,7 +1162,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1157
1162
|
(
|
|
1158
1163
|
this.props.renderScrollComponent ||
|
|
1159
1164
|
this._defaultRenderScrollComponent
|
|
1160
|
-
)(scrollProps)
|
|
1165
|
+
)(scrollProps) as ExactReactElement_DEPRECATED<any>,
|
|
1161
1166
|
{
|
|
1162
1167
|
ref: this._captureScrollRef,
|
|
1163
1168
|
},
|
|
@@ -1202,7 +1207,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1202
1207
|
}
|
|
1203
1208
|
}
|
|
1204
1209
|
|
|
1205
|
-
componentDidUpdate(prevProps:
|
|
1210
|
+
componentDidUpdate(prevProps: VirtualizedListProps) {
|
|
1206
1211
|
const {data, extraData, getItemLayout} = this.props;
|
|
1207
1212
|
if (data !== prevProps.data || extraData !== prevProps.extraData) {
|
|
1208
1213
|
// clear the viewableIndices cache to also trigger
|
|
@@ -1269,15 +1274,13 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1269
1274
|
visibleLength: 0,
|
|
1270
1275
|
zoomScale: 1,
|
|
1271
1276
|
};
|
|
1272
|
-
_scrollRef: ?React.ElementRef<
|
|
1277
|
+
_scrollRef: ?React.ElementRef<typeof ScrollView> = null;
|
|
1273
1278
|
_sentStartForContentLength = 0;
|
|
1274
1279
|
_sentEndForContentLength = 0;
|
|
1275
|
-
|
|
1280
|
+
_updateCellsToRenderTimeoutID: ?TimeoutID = null;
|
|
1276
1281
|
_viewabilityTuples: Array<ViewabilityHelperCallbackTuple> = [];
|
|
1277
1282
|
|
|
1278
|
-
|
|
1279
|
-
* LTI update could not be added via codemod */
|
|
1280
|
-
_captureScrollRef = ref => {
|
|
1283
|
+
_captureScrollRef = (ref: ?React.ElementRef<typeof ScrollView>) => {
|
|
1281
1284
|
this._scrollRef = ref;
|
|
1282
1285
|
};
|
|
1283
1286
|
|
|
@@ -1368,7 +1371,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1368
1371
|
};
|
|
1369
1372
|
|
|
1370
1373
|
_onCellLayout = (
|
|
1371
|
-
e:
|
|
1374
|
+
e: LayoutChangeEvent,
|
|
1372
1375
|
cellKey: string,
|
|
1373
1376
|
cellIndex: number,
|
|
1374
1377
|
): void => {
|
|
@@ -1455,7 +1458,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1455
1458
|
}
|
|
1456
1459
|
}
|
|
1457
1460
|
|
|
1458
|
-
_onLayout = (e:
|
|
1461
|
+
_onLayout = (e: LayoutChangeEvent) => {
|
|
1459
1462
|
if (this._isNestedWithSameOrientation()) {
|
|
1460
1463
|
// Need to adjust our scroll metrics to be relative to our containing
|
|
1461
1464
|
// VirtualizedList before we can make claims about list item viewability
|
|
@@ -1470,7 +1473,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1470
1473
|
this._maybeCallOnEdgeReached();
|
|
1471
1474
|
};
|
|
1472
1475
|
|
|
1473
|
-
_onLayoutEmpty = (e:
|
|
1476
|
+
_onLayoutEmpty = (e: LayoutChangeEvent) => {
|
|
1474
1477
|
this.props.onLayout && this.props.onLayout(e);
|
|
1475
1478
|
};
|
|
1476
1479
|
|
|
@@ -1478,12 +1481,12 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1478
1481
|
return this._getCellKey() + '-footer';
|
|
1479
1482
|
}
|
|
1480
1483
|
|
|
1481
|
-
_onLayoutFooter = (e:
|
|
1484
|
+
_onLayoutFooter = (e: LayoutChangeEvent) => {
|
|
1482
1485
|
this._triggerRemeasureForChildListsInCell(this._getFooterCellKey());
|
|
1483
1486
|
this._footerLength = this._selectLength(e.nativeEvent.layout);
|
|
1484
1487
|
};
|
|
1485
1488
|
|
|
1486
|
-
_onLayoutHeader = (e:
|
|
1489
|
+
_onLayoutHeader = (e: LayoutChangeEvent) => {
|
|
1487
1490
|
this._headerLength = this._selectLength(e.nativeEvent.layout);
|
|
1488
1491
|
};
|
|
1489
1492
|
|
|
@@ -1491,7 +1494,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1491
1494
|
_selectRowAtIndex = (rowIndex: number) => {
|
|
1492
1495
|
const prevIndex = this.state.selectedRowIndex;
|
|
1493
1496
|
const newIndex = rowIndex;
|
|
1494
|
-
this.setState({selectedRowIndex: newIndex});
|
|
1497
|
+
this.setState<'selectedRowIndex'>({selectedRowIndex: newIndex});
|
|
1495
1498
|
|
|
1496
1499
|
this.ensureItemAtIndexIsVisible(newIndex);
|
|
1497
1500
|
if (prevIndex !== newIndex) {
|
|
@@ -1871,7 +1874,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1871
1874
|
zoomScale,
|
|
1872
1875
|
};
|
|
1873
1876
|
if (this.state.pendingScrollUpdateCount > 0) {
|
|
1874
|
-
this.setState(state => ({
|
|
1877
|
+
this.setState<'pendingScrollUpdateCount'>(state => ({
|
|
1875
1878
|
pendingScrollUpdateCount: state.pendingScrollUpdateCount - 1,
|
|
1876
1879
|
}));
|
|
1877
1880
|
}
|
|
@@ -1918,11 +1921,19 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
1918
1921
|
this._hiPriInProgress = true;
|
|
1919
1922
|
// Don't worry about interactions when scrolling quickly; focus on filling content as fast
|
|
1920
1923
|
// as possible.
|
|
1921
|
-
this.
|
|
1924
|
+
if (this._updateCellsToRenderTimeoutID != null) {
|
|
1925
|
+
clearTimeout(this._updateCellsToRenderTimeoutID);
|
|
1926
|
+
this._updateCellsToRenderTimeoutID = null;
|
|
1927
|
+
}
|
|
1922
1928
|
this._updateCellsToRender();
|
|
1923
1929
|
return;
|
|
1924
1930
|
} else {
|
|
1925
|
-
this.
|
|
1931
|
+
if (this._updateCellsToRenderTimeoutID == null) {
|
|
1932
|
+
this._updateCellsToRenderTimeoutID = setTimeout(() => {
|
|
1933
|
+
this._updateCellsToRenderTimeoutID = null;
|
|
1934
|
+
this._updateCellsToRender();
|
|
1935
|
+
}, this.props.updateCellsBatchingPeriod ?? 50);
|
|
1936
|
+
}
|
|
1926
1937
|
}
|
|
1927
1938
|
}
|
|
1928
1939
|
|
|
@@ -2023,7 +2034,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|
|
2023
2034
|
_updateCellsToRender = () => {
|
|
2024
2035
|
this._updateViewableItems(this.props, this.state.cellsAroundViewport);
|
|
2025
2036
|
|
|
2026
|
-
this.setState((state, props) => {
|
|
2037
|
+
this.setState<'cellsAroundViewport' | 'renderMask'>((state, props) => {
|
|
2027
2038
|
const cellsAroundViewport = this._adjustCellsAroundViewport(
|
|
2028
2039
|
props,
|
|
2029
2040
|
state.cellsAroundViewport,
|
|
@@ -2189,4 +2200,4 @@ const styles = StyleSheet.create({
|
|
|
2189
2200
|
},
|
|
2190
2201
|
});
|
|
2191
2202
|
|
|
2192
|
-
|
|
2203
|
+
export default VirtualizedList;
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
* @format
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type {CellRendererProps,
|
|
11
|
+
import type {CellRendererProps, ListRenderItem} from './VirtualizedListProps';
|
|
12
12
|
import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
|
|
13
13
|
import type {
|
|
14
14
|
FocusEvent,
|
|
15
|
-
|
|
15
|
+
LayoutChangeEvent,
|
|
16
16
|
} from 'react-native/Libraries/Types/CoreEventTypes';
|
|
17
17
|
|
|
18
18
|
import {VirtualizedListCellContextProvider} from './VirtualizedListContext.js';
|
|
@@ -32,7 +32,11 @@ export type Props<ItemT> = {
|
|
|
32
32
|
inversionStyle: ViewStyleProp,
|
|
33
33
|
isSelected: ?boolean, // [macOS]
|
|
34
34
|
item: ItemT,
|
|
35
|
-
onCellLayout?: (
|
|
35
|
+
onCellLayout?: (
|
|
36
|
+
event: LayoutChangeEvent,
|
|
37
|
+
cellKey: string,
|
|
38
|
+
index: number,
|
|
39
|
+
) => void,
|
|
36
40
|
onCellFocusCapture?: (cellKey: string) => void,
|
|
37
41
|
onUnmount: (cellKey: string) => void,
|
|
38
42
|
onUpdateSeparators: (
|
|
@@ -40,14 +44,14 @@ export type Props<ItemT> = {
|
|
|
40
44
|
props: Partial<SeparatorProps<ItemT>>,
|
|
41
45
|
) => void,
|
|
42
46
|
prevCellKey: ?string,
|
|
43
|
-
renderItem?: ?
|
|
47
|
+
renderItem?: ?ListRenderItem<ItemT>,
|
|
44
48
|
...
|
|
45
49
|
};
|
|
46
50
|
|
|
47
|
-
type SeparatorProps<ItemT> = $ReadOnly<{
|
|
51
|
+
type SeparatorProps<ItemT> = $ReadOnly<{
|
|
48
52
|
highlighted: boolean,
|
|
49
53
|
leadingItem: ?ItemT,
|
|
50
|
-
|
|
54
|
+
}>;
|
|
51
55
|
|
|
52
56
|
type State<ItemT> = {
|
|
53
57
|
separatorProps: SeparatorProps<ItemT>,
|
|
@@ -65,10 +69,10 @@ export default class CellRenderer<ItemT> extends React.PureComponent<
|
|
|
65
69
|
},
|
|
66
70
|
};
|
|
67
71
|
|
|
68
|
-
static getDerivedStateFromProps(
|
|
69
|
-
props: Props<
|
|
70
|
-
prevState: State<
|
|
71
|
-
): ?State<
|
|
72
|
+
static getDerivedStateFromProps<StaticItemT>(
|
|
73
|
+
props: Props<StaticItemT>,
|
|
74
|
+
prevState: State<StaticItemT>,
|
|
75
|
+
): ?State<StaticItemT> {
|
|
72
76
|
if (props.item !== prevState.separatorProps.leadingItem) {
|
|
73
77
|
return {
|
|
74
78
|
separatorProps: {
|
|
@@ -118,7 +122,7 @@ export default class CellRenderer<ItemT> extends React.PureComponent<
|
|
|
118
122
|
this.props.onUnmount(this.props.cellKey);
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
_onLayout = (nativeEvent:
|
|
125
|
+
_onLayout = (nativeEvent: LayoutChangeEvent): void => {
|
|
122
126
|
this.props.onCellLayout?.(
|
|
123
127
|
nativeEvent,
|
|
124
128
|
this.props.cellKey,
|
|
@@ -131,7 +135,7 @@ export default class CellRenderer<ItemT> extends React.PureComponent<
|
|
|
131
135
|
};
|
|
132
136
|
|
|
133
137
|
_renderElement(
|
|
134
|
-
renderItem: ?
|
|
138
|
+
renderItem: ?ListRenderItem<ItemT>,
|
|
135
139
|
ListItemComponent: any,
|
|
136
140
|
item: ItemT,
|
|
137
141
|
index: number,
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
|
|
17
17
|
import type {
|
|
18
18
|
FocusEvent,
|
|
19
|
-
|
|
19
|
+
LayoutChangeEvent,
|
|
20
20
|
} from 'react-native/Libraries/Types/CoreEventTypes';
|
|
21
21
|
|
|
22
22
|
import * as React from 'react';
|
|
@@ -31,7 +31,7 @@ export type Separators = {
|
|
|
31
31
|
...
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
export type
|
|
34
|
+
export type ListRenderItemInfo<ItemT> = {
|
|
35
35
|
item: ItemT,
|
|
36
36
|
index: number,
|
|
37
37
|
isSelected: ?boolean, // [macOS]
|
|
@@ -45,12 +45,12 @@ export type CellRendererProps<ItemT> = $ReadOnly<{
|
|
|
45
45
|
index: number,
|
|
46
46
|
item: ItemT,
|
|
47
47
|
onFocusCapture?: (event: FocusEvent) => void,
|
|
48
|
-
onLayout?: (event:
|
|
48
|
+
onLayout?: (event: LayoutChangeEvent) => void,
|
|
49
49
|
style: ViewStyleProp,
|
|
50
50
|
}>;
|
|
51
51
|
|
|
52
|
-
export type
|
|
53
|
-
info:
|
|
52
|
+
export type ListRenderItem<ItemT> = (
|
|
53
|
+
info: ListRenderItemInfo<ItemT>,
|
|
54
54
|
) => React.Node;
|
|
55
55
|
|
|
56
56
|
// [macOS
|
|
@@ -59,7 +59,7 @@ export type SelectedRowIndexPathType = {
|
|
|
59
59
|
rowIndex: number,
|
|
60
60
|
}; // macOS]
|
|
61
61
|
|
|
62
|
-
type RequiredProps = {
|
|
62
|
+
type RequiredProps = {
|
|
63
63
|
/**
|
|
64
64
|
* The default accessor functions assume this is an Array<{key: string} | {id: string}> but you can override
|
|
65
65
|
* getItem, getItemCount, and keyExtractor to handle any type of index-based data.
|
|
@@ -73,9 +73,9 @@ type RequiredProps = {|
|
|
|
73
73
|
* Determines how many items are in the data blob.
|
|
74
74
|
*/
|
|
75
75
|
getItemCount: (data: any) => number,
|
|
76
|
-
|
|
77
|
-
type OptionalProps = {
|
|
78
|
-
renderItem?: ?
|
|
76
|
+
};
|
|
77
|
+
type OptionalProps = {
|
|
78
|
+
renderItem?: ?ListRenderItem<Item>,
|
|
79
79
|
/**
|
|
80
80
|
* `debug` will turn on extra logging and visual overlays to aid with debugging both usage and
|
|
81
81
|
* implementation, but with a significant perf hit.
|
|
@@ -162,26 +162,17 @@ type OptionalProps = {|
|
|
|
162
162
|
* `highlight` and `unhighlight` (which set the `highlighted: boolean` prop) are insufficient for
|
|
163
163
|
* your use-case.
|
|
164
164
|
*/
|
|
165
|
-
ListItemComponent?: ?(
|
|
166
|
-
| React.ComponentType<any>
|
|
167
|
-
| ExactReactElement_DEPRECATED<any>
|
|
168
|
-
),
|
|
165
|
+
ListItemComponent?: ?(React.ComponentType<any> | React.MixedElement),
|
|
169
166
|
/**
|
|
170
167
|
* Rendered when the list is empty. Can be a React Component Class, a render function, or
|
|
171
168
|
* a rendered element.
|
|
172
169
|
*/
|
|
173
|
-
ListEmptyComponent?: ?(
|
|
174
|
-
| React.ComponentType<any>
|
|
175
|
-
| ExactReactElement_DEPRECATED<any>
|
|
176
|
-
),
|
|
170
|
+
ListEmptyComponent?: ?(React.ComponentType<any> | React.MixedElement),
|
|
177
171
|
/**
|
|
178
172
|
* Rendered at the bottom of all the items. Can be a React Component Class, a render function, or
|
|
179
173
|
* a rendered element.
|
|
180
174
|
*/
|
|
181
|
-
ListFooterComponent?: ?(
|
|
182
|
-
| React.ComponentType<any>
|
|
183
|
-
| ExactReactElement_DEPRECATED<any>
|
|
184
|
-
),
|
|
175
|
+
ListFooterComponent?: ?(React.ComponentType<any> | React.MixedElement),
|
|
185
176
|
/**
|
|
186
177
|
* Styling for internal View for ListFooterComponent
|
|
187
178
|
*/
|
|
@@ -190,10 +181,7 @@ type OptionalProps = {|
|
|
|
190
181
|
* Rendered at the top of all the items. Can be a React Component Class, a render function, or
|
|
191
182
|
* a rendered element.
|
|
192
183
|
*/
|
|
193
|
-
ListHeaderComponent?: ?(
|
|
194
|
-
| React.ComponentType<any>
|
|
195
|
-
| ExactReactElement_DEPRECATED<any>
|
|
196
|
-
),
|
|
184
|
+
ListHeaderComponent?: ?(React.ComponentType<any> | React.MixedElement),
|
|
197
185
|
/**
|
|
198
186
|
* Styling for internal View for ListHeaderComponent
|
|
199
187
|
*/
|
|
@@ -263,7 +251,7 @@ type OptionalProps = {|
|
|
|
263
251
|
* <RefreshControl> component built internally. The onRefresh and refreshing
|
|
264
252
|
* props are also ignored. Only works for vertical VirtualizedList.
|
|
265
253
|
*/
|
|
266
|
-
refreshControl?: ?
|
|
254
|
+
refreshControl?: ?React.MixedElement,
|
|
267
255
|
/**
|
|
268
256
|
* Set this true while waiting for new data from a refresh.
|
|
269
257
|
*/
|
|
@@ -277,7 +265,7 @@ type OptionalProps = {|
|
|
|
277
265
|
/**
|
|
278
266
|
* Render a custom scroll component, e.g. with a differently styled `RefreshControl`.
|
|
279
267
|
*/
|
|
280
|
-
renderScrollComponent?: (props: Object) =>
|
|
268
|
+
renderScrollComponent?: (props: Object) => React.MixedElement,
|
|
281
269
|
/**
|
|
282
270
|
* Amount of time between low-pri item render batches, e.g. for rendering items quite a ways off
|
|
283
271
|
* screen. Similar fill rate/responsiveness tradeoff as `maxToRenderPerBatch`.
|
|
@@ -300,13 +288,9 @@ type OptionalProps = {|
|
|
|
300
288
|
* chance that fast scrolling may reveal momentary blank areas of unrendered content.
|
|
301
289
|
*/
|
|
302
290
|
windowSize?: ?number,
|
|
303
|
-
|
|
304
|
-
* The legacy implementation is no longer supported.
|
|
305
|
-
*/
|
|
306
|
-
legacyImplementation?: empty,
|
|
307
|
-
|};
|
|
291
|
+
};
|
|
308
292
|
// [macOS
|
|
309
|
-
type MacOSProps = {
|
|
293
|
+
type MacOSProps = {
|
|
310
294
|
/**
|
|
311
295
|
* Allows you to 'select' a row using arrow keys. The selected row will have the prop `isSelected`
|
|
312
296
|
* passed in as true to it's renderItem / ListItemComponent. You can also imperatively select a row
|
|
@@ -347,15 +331,15 @@ type MacOSProps = {|
|
|
|
347
331
|
|
|
348
332
|
sectionIndex?: number,
|
|
349
333
|
rowIndex?: number,
|
|
350
|
-
|
|
334
|
+
};
|
|
351
335
|
// macOS]
|
|
352
336
|
|
|
353
|
-
export type
|
|
337
|
+
export type VirtualizedListProps = {
|
|
354
338
|
...React.ElementConfig<ScrollView>,
|
|
355
339
|
...RequiredProps,
|
|
356
340
|
...OptionalProps,
|
|
357
341
|
...MacOSProps, // [macOS]
|
|
358
|
-
|
|
342
|
+
};
|
|
359
343
|
|
|
360
344
|
/**
|
|
361
345
|
* Default Props Helper Functions
|
|
@@ -44,11 +44,11 @@ export type SectionBase<SectionItemT> = {
|
|
|
44
44
|
...
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
type RequiredProps<SectionT: SectionBase<any>> = {
|
|
47
|
+
type RequiredProps<SectionT: SectionBase<any>> = {
|
|
48
48
|
sections: $ReadOnlyArray<SectionT>,
|
|
49
|
-
|
|
49
|
+
};
|
|
50
50
|
|
|
51
|
-
type OptionalProps<SectionT: SectionBase<any>> = {
|
|
51
|
+
type OptionalProps<SectionT: SectionBase<any>> = {
|
|
52
52
|
/**
|
|
53
53
|
* Default renderer for every item in every section.
|
|
54
54
|
*/
|
|
@@ -87,11 +87,11 @@ type OptionalProps<SectionT: SectionBase<any>> = {|
|
|
|
87
87
|
*/
|
|
88
88
|
stickySectionHeadersEnabled?: boolean,
|
|
89
89
|
onEndReached?: ?({distanceFromEnd: number, ...}) => void,
|
|
90
|
-
|
|
90
|
+
};
|
|
91
91
|
|
|
92
92
|
type VirtualizedListProps = React.ElementConfig<typeof VirtualizedList>;
|
|
93
93
|
|
|
94
|
-
export type
|
|
94
|
+
export type VirtualizedSectionListProps<SectionT: SectionBase<any>> = {
|
|
95
95
|
...RequiredProps<SectionT>,
|
|
96
96
|
...OptionalProps<SectionT>,
|
|
97
97
|
...$Diff<
|
|
@@ -102,14 +102,14 @@ export type Props<SectionT> = {|
|
|
|
102
102
|
...
|
|
103
103
|
},
|
|
104
104
|
>,
|
|
105
|
-
|
|
106
|
-
export type ScrollToLocationParamsType = {
|
|
105
|
+
};
|
|
106
|
+
export type ScrollToLocationParamsType = {
|
|
107
107
|
animated?: ?boolean,
|
|
108
108
|
itemIndex: number,
|
|
109
109
|
sectionIndex: number,
|
|
110
110
|
viewOffset?: number,
|
|
111
111
|
viewPosition?: number,
|
|
112
|
-
|
|
112
|
+
};
|
|
113
113
|
|
|
114
114
|
type State = {childProps: VirtualizedListProps, ...};
|
|
115
115
|
|
|
@@ -120,7 +120,7 @@ type State = {childProps: VirtualizedListProps, ...};
|
|
|
120
120
|
*/
|
|
121
121
|
class VirtualizedSectionList<
|
|
122
122
|
SectionT: SectionBase<any>,
|
|
123
|
-
> extends React.PureComponent<
|
|
123
|
+
> extends React.PureComponent<VirtualizedSectionListProps<SectionT>, State> {
|
|
124
124
|
scrollToLocation(params: ScrollToLocationParamsType) {
|
|
125
125
|
let index = params.itemIndex;
|
|
126
126
|
for (let i = 0; i < params.sectionIndex; i++) {
|
|
@@ -203,7 +203,7 @@ class VirtualizedSectionList<
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
_getItem(
|
|
206
|
-
props:
|
|
206
|
+
props: VirtualizedSectionListProps<SectionT>,
|
|
207
207
|
sections: ?$ReadOnlyArray<Item>,
|
|
208
208
|
index: number,
|
|
209
209
|
): ?Item {
|
|
@@ -453,15 +453,15 @@ class VirtualizedSectionList<
|
|
|
453
453
|
};
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
-
type ItemWithSeparatorCommonProps = $ReadOnly<{
|
|
456
|
+
type ItemWithSeparatorCommonProps = $ReadOnly<{
|
|
457
457
|
leadingItem: ?Item,
|
|
458
458
|
leadingSection: ?Object,
|
|
459
459
|
section: Object,
|
|
460
460
|
trailingItem: ?Item,
|
|
461
461
|
trailingSection: ?Object,
|
|
462
|
-
|
|
462
|
+
}>;
|
|
463
463
|
|
|
464
|
-
type ItemWithSeparatorProps = $ReadOnly<{
|
|
464
|
+
type ItemWithSeparatorProps = $ReadOnly<{
|
|
465
465
|
...ItemWithSeparatorCommonProps,
|
|
466
466
|
LeadingSeparatorComponent: ?React.ComponentType<any>,
|
|
467
467
|
SeparatorComponent: ?React.ComponentType<any>,
|
|
@@ -481,7 +481,7 @@ type ItemWithSeparatorProps = $ReadOnly<{|
|
|
|
481
481
|
updatePropsFor: (prevCellKey: string, value: Object) => void,
|
|
482
482
|
renderItem: Function,
|
|
483
483
|
inverted: boolean,
|
|
484
|
-
|
|
484
|
+
}>;
|
|
485
485
|
|
|
486
486
|
function ItemWithSeparator(props: ItemWithSeparatorProps): React.Node {
|
|
487
487
|
const {
|
|
@@ -598,12 +598,12 @@ function ItemWithSeparator(props: ItemWithSeparatorProps): React.Node {
|
|
|
598
598
|
);
|
|
599
599
|
}
|
|
600
600
|
|
|
601
|
-
|
|
601
|
+
export default VirtualizedSectionList as component(
|
|
602
602
|
ref: React.RefSetter<
|
|
603
603
|
interface {
|
|
604
604
|
getListRef(): ?VirtualizedList,
|
|
605
605
|
scrollToLocation(params: ScrollToLocationParamsType): void,
|
|
606
606
|
},
|
|
607
607
|
>,
|
|
608
|
-
...
|
|
608
|
+
...VirtualizedSectionListProps<SectionBase<any>>
|
|
609
609
|
);
|
package/Utilities/clamp.js
CHANGED
package/Utilities/infoLog.js
CHANGED
package/index.js
CHANGED
|
@@ -22,37 +22,38 @@ export type {
|
|
|
22
22
|
ViewToken,
|
|
23
23
|
ViewabilityConfig,
|
|
24
24
|
ViewabilityConfigCallbackPair,
|
|
25
|
+
ViewabilityConfigCallbackPairs,
|
|
25
26
|
} from './Lists/ViewabilityHelper';
|
|
26
27
|
export type {
|
|
27
28
|
CellRendererProps,
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
ListRenderItemInfo,
|
|
30
|
+
ListRenderItem,
|
|
30
31
|
Separators,
|
|
31
32
|
} from './Lists/VirtualizedListProps';
|
|
32
33
|
export type {
|
|
33
|
-
|
|
34
|
+
VirtualizedSectionListProps,
|
|
34
35
|
ScrollToLocationParamsType,
|
|
35
36
|
SectionBase,
|
|
36
37
|
} from './Lists/VirtualizedSectionList';
|
|
37
38
|
export type {FillRateInfo} from './Lists/FillRateHelper';
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
export default {
|
|
40
41
|
keyExtractor,
|
|
41
42
|
|
|
42
43
|
get VirtualizedList(): VirtualizedList {
|
|
43
|
-
return require('./Lists/VirtualizedList');
|
|
44
|
+
return require('./Lists/VirtualizedList').default;
|
|
44
45
|
},
|
|
45
46
|
get VirtualizedSectionList(): VirtualizedSectionList {
|
|
46
|
-
return require('./Lists/VirtualizedSectionList');
|
|
47
|
+
return require('./Lists/VirtualizedSectionList').default;
|
|
47
48
|
},
|
|
48
49
|
get VirtualizedListContextResetter(): VirtualizedListContextResetter {
|
|
49
50
|
const VirtualizedListContext = require('./Lists/VirtualizedListContext');
|
|
50
51
|
return VirtualizedListContext.VirtualizedListContextResetter;
|
|
51
52
|
},
|
|
52
53
|
get ViewabilityHelper(): ViewabilityHelper {
|
|
53
|
-
return require('./Lists/ViewabilityHelper');
|
|
54
|
+
return require('./Lists/ViewabilityHelper').default;
|
|
54
55
|
},
|
|
55
56
|
get FillRateHelper(): FillRateHelper {
|
|
56
|
-
return require('./Lists/FillRateHelper');
|
|
57
|
+
return require('./Lists/FillRateHelper').default;
|
|
57
58
|
},
|
|
58
59
|
};
|
package/package.json
CHANGED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
* @flow strict-local
|
|
8
|
-
* @format
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import {InteractionManager} from 'react-native';
|
|
12
|
-
import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A simple class for batching up invocations of a low-pri callback. A timeout is set to run the
|
|
16
|
-
* callback once after a delay, no matter how many times it's scheduled. Once the delay is reached,
|
|
17
|
-
* InteractionManager.runAfterInteractions is used to invoke the callback after any hi-pri
|
|
18
|
-
* interactions are done running.
|
|
19
|
-
*
|
|
20
|
-
* Make sure to cleanup with dispose(). Example:
|
|
21
|
-
*
|
|
22
|
-
* class Widget extends React.Component {
|
|
23
|
-
* _batchedSave: new Batchinator(() => this._saveState, 1000);
|
|
24
|
-
* _saveSate() {
|
|
25
|
-
* // save this.state to disk
|
|
26
|
-
* }
|
|
27
|
-
* componentDidUpdate() {
|
|
28
|
-
* this._batchedSave.schedule();
|
|
29
|
-
* }
|
|
30
|
-
* componentWillUnmount() {
|
|
31
|
-
* this._batchedSave.dispose();
|
|
32
|
-
* }
|
|
33
|
-
* ...
|
|
34
|
-
* }
|
|
35
|
-
*/
|
|
36
|
-
class Batchinator {
|
|
37
|
-
_callback: () => void;
|
|
38
|
-
_delay: number;
|
|
39
|
-
_taskHandle: ?{cancel: () => void, ...};
|
|
40
|
-
|
|
41
|
-
constructor(callback: () => void, delay: number) {
|
|
42
|
-
this._delay = delay;
|
|
43
|
-
this._callback = callback;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/*
|
|
47
|
-
* Cleanup any pending tasks.
|
|
48
|
-
*
|
|
49
|
-
* By default, if there is a pending task the callback is run immediately. Set the option abort to
|
|
50
|
-
* true to not call the callback if it was pending.
|
|
51
|
-
*/
|
|
52
|
-
dispose(): void {
|
|
53
|
-
if (this._taskHandle) {
|
|
54
|
-
this._taskHandle.cancel();
|
|
55
|
-
this._taskHandle = null;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
schedule(): void {
|
|
60
|
-
if (this._taskHandle) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const invokeCallback = () => {
|
|
64
|
-
// Note that we clear the handle before invoking the callback so that if the callback calls
|
|
65
|
-
// schedule again, it will actually schedule another task.
|
|
66
|
-
this._taskHandle = null;
|
|
67
|
-
this._callback();
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const timeoutHandle = setTimeout(
|
|
71
|
-
// NOTE: When shipping this, delete `Batchinator` instead of only these
|
|
72
|
-
// lines of code. Without `InteractionManager`, it's just a `setTimeout`.
|
|
73
|
-
ReactNativeFeatureFlags.disableInteractionManagerInBatchinator()
|
|
74
|
-
? invokeCallback
|
|
75
|
-
: () => {
|
|
76
|
-
this._taskHandle =
|
|
77
|
-
InteractionManager.runAfterInteractions(invokeCallback);
|
|
78
|
-
},
|
|
79
|
-
this._delay,
|
|
80
|
-
);
|
|
81
|
-
this._taskHandle = {cancel: () => clearTimeout(timeoutHandle)};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
module.exports = Batchinator;
|