@shopify/flash-list 1.0.3 → 1.2.0
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/CHANGELOG.md +36 -0
- package/RNFlashList.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutShadow.kt +5 -0
- package/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt +78 -7
- package/dist/FlashList.d.ts +4 -2
- package/dist/FlashList.d.ts.map +1 -1
- package/dist/FlashList.js +54 -28
- package/dist/FlashList.js.map +1 -1
- package/dist/FlashListProps.d.ts +15 -1
- package/dist/FlashListProps.d.ts.map +1 -1
- package/dist/FlashListProps.js +6 -0
- package/dist/FlashListProps.js.map +1 -1
- package/dist/__tests__/FlashList.test.js +84 -2
- package/dist/__tests__/FlashList.test.js.map +1 -1
- package/dist/__tests__/PlatformHelper.web.test.js +9 -1
- package/dist/__tests__/PlatformHelper.web.test.js.map +1 -1
- package/dist/__tests__/helpers/mountFlashList.d.ts +1 -2
- package/dist/__tests__/helpers/mountFlashList.d.ts.map +1 -1
- package/dist/__tests__/helpers/mountFlashList.js.map +1 -1
- package/dist/benchmark/AutoScrollHelper.d.ts.map +1 -1
- package/dist/benchmark/AutoScrollHelper.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.android.d.ts +4 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.android.d.ts.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.android.js +6 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.android.js.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.d.ts +2 -1
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.d.ts.map +1 -1
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.ios.d.ts +4 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.ios.d.ts.map +1 -0
- package/dist/native/auto-layout/{AutoLayoutViewNativeComponent.web.js → AutoLayoutViewNativeComponent.ios.js} +2 -2
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.ios.js.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.js +1 -1
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.js.map +1 -1
- package/dist/native/cell-container/CellContainer.android.d.ts +6 -0
- package/dist/native/cell-container/CellContainer.android.d.ts.map +1 -0
- package/dist/native/cell-container/CellContainer.android.js +9 -0
- package/dist/native/cell-container/CellContainer.android.js.map +1 -0
- package/dist/native/cell-container/CellContainer.d.ts +6 -4
- package/dist/native/cell-container/CellContainer.d.ts.map +1 -1
- package/dist/native/cell-container/CellContainer.ios.d.ts +6 -0
- package/dist/native/cell-container/CellContainer.ios.d.ts.map +1 -0
- package/dist/native/cell-container/CellContainer.ios.js +9 -0
- package/dist/native/cell-container/CellContainer.ios.js.map +1 -0
- package/dist/native/cell-container/CellContainer.js +6 -4
- package/dist/native/cell-container/CellContainer.js.map +1 -1
- package/dist/native/config/PlatformHelper.android.d.ts +16 -0
- package/dist/native/config/PlatformHelper.android.d.ts.map +1 -0
- package/dist/native/config/PlatformHelper.android.js +20 -0
- package/dist/native/config/PlatformHelper.android.js.map +1 -0
- package/dist/{utils → native/config}/PlatformHelper.d.ts +3 -1
- package/dist/native/config/PlatformHelper.d.ts.map +1 -0
- package/dist/{utils/PlatformHelper.web.d.ts → native/config/PlatformHelper.ios.d.ts} +4 -2
- package/dist/native/config/PlatformHelper.ios.d.ts.map +1 -0
- package/dist/{utils/PlatformHelper.js → native/config/PlatformHelper.ios.js} +6 -2
- package/dist/native/config/PlatformHelper.ios.js.map +1 -0
- package/dist/native/config/PlatformHelper.js +21 -0
- package/dist/native/config/PlatformHelper.js.map +1 -0
- package/dist/native/config/PlatformHelper.web.d.ts +17 -0
- package/dist/native/config/PlatformHelper.web.d.ts.map +1 -0
- package/dist/{utils → native/config}/PlatformHelper.web.js +7 -2
- package/dist/native/config/PlatformHelper.web.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/AverageWindow.d.ts.map +1 -1
- package/dist/utils/AverageWindow.js.map +1 -1
- package/dist/viewability/ViewabilityHelper.d.ts.map +1 -1
- package/dist/viewability/ViewabilityHelper.js.map +1 -1
- package/ios/Sources/AutoLayoutView.swift +61 -8
- package/package.json +10 -4
- package/src/FlashList.tsx +82 -23
- package/src/FlashListProps.ts +24 -1
- package/src/__tests__/FlashList.test.tsx +92 -3
- package/src/__tests__/PlatformHelper.web.test.ts +17 -1
- package/src/__tests__/helpers/mountFlashList.tsx +2 -2
- package/src/benchmark/AutoScrollHelper.ts +1 -1
- package/src/index.ts +2 -0
- package/src/native/auto-layout/{AutoLayoutViewNativeComponent.web.ts → AutoLayoutViewNativeComponent.android.ts} +2 -3
- package/src/native/auto-layout/AutoLayoutViewNativeComponent.ios.ts +7 -0
- package/src/native/auto-layout/AutoLayoutViewNativeComponent.ts +2 -2
- package/src/native/cell-container/{CellContainer.ts → CellContainer.android.ts} +0 -0
- package/src/native/cell-container/CellContainer.ios.ts +7 -0
- package/src/native/cell-container/CellContainer.tsx +14 -0
- package/src/{utils/PlatformHelper.ts → native/config/PlatformHelper.android.ts} +12 -2
- package/src/native/config/PlatformHelper.ios.ts +26 -0
- package/src/native/config/PlatformHelper.ts +27 -0
- package/src/{utils → native/config}/PlatformHelper.web.ts +15 -3
- package/src/utils/AverageWindow.ts +1 -1
- package/src/viewability/ViewabilityHelper.ts +1 -1
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.web.d.ts +0 -5
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.web.d.ts.map +0 -1
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.web.js.map +0 -1
- package/dist/utils/PlatformHelper.d.ts.map +0 -1
- package/dist/utils/PlatformHelper.js.map +0 -1
- package/dist/utils/PlatformHelper.web.d.ts.map +0 -1
- package/dist/utils/PlatformHelper.web.js.map +0 -1
package/src/FlashList.tsx
CHANGED
|
@@ -25,12 +25,18 @@ import CustomError from "./errors/CustomError";
|
|
|
25
25
|
import ExceptionList from "./errors/ExceptionList";
|
|
26
26
|
import WarningList from "./errors/Warnings";
|
|
27
27
|
import ViewabilityManager from "./viewability/ViewabilityManager";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
FlashListProps,
|
|
30
|
+
ContentStyle,
|
|
31
|
+
RenderTarget,
|
|
32
|
+
RenderTargetOptions,
|
|
33
|
+
} from "./FlashListProps";
|
|
29
34
|
import {
|
|
30
35
|
getCellContainerPlatformStyles,
|
|
36
|
+
getFooterContainer,
|
|
31
37
|
getItemAnimator,
|
|
32
38
|
PlatformConfig,
|
|
33
|
-
} from "./
|
|
39
|
+
} from "./native/config/PlatformHelper";
|
|
34
40
|
|
|
35
41
|
interface StickyProps extends StickyContainerProps {
|
|
36
42
|
children: any;
|
|
@@ -59,6 +65,7 @@ class FlashList<T> extends React.PureComponent<
|
|
|
59
65
|
private stickyContentContainerRef?: PureComponentWrapper;
|
|
60
66
|
private listFixedDimensionSize = 0;
|
|
61
67
|
private transformStyle = { transform: [{ scaleY: -1 }] };
|
|
68
|
+
private transformStyleHorizontal = { transform: [{ scaleX: -1 }] };
|
|
62
69
|
private distanceFromWindow = 0;
|
|
63
70
|
private contentStyle: ContentStyle = {};
|
|
64
71
|
private loadStartTime = 0;
|
|
@@ -73,7 +80,6 @@ class FlashList<T> extends React.PureComponent<
|
|
|
73
80
|
applyToInitialOffset: true,
|
|
74
81
|
};
|
|
75
82
|
|
|
76
|
-
private emptyObject = {};
|
|
77
83
|
private postLoadTimeoutId?: ReturnType<typeof setTimeout>;
|
|
78
84
|
private sizeWarningTimeoutId?: ReturnType<typeof setTimeout>;
|
|
79
85
|
|
|
@@ -192,7 +198,7 @@ class FlashList<T> extends React.PureComponent<
|
|
|
192
198
|
// Using only grid layout provider as it can also act as a listview, sizeProvider is a function to support future overrides
|
|
193
199
|
private static getLayoutProvider<T>(
|
|
194
200
|
numColumns: number,
|
|
195
|
-
|
|
201
|
+
flashListProps: FlashListProps<T>
|
|
196
202
|
) {
|
|
197
203
|
return new GridLayoutProviderWithProps<T>(
|
|
198
204
|
// max span or, total columns
|
|
@@ -228,7 +234,7 @@ class FlashList<T> extends React.PureComponent<
|
|
|
228
234
|
);
|
|
229
235
|
return mutableLayout?.size;
|
|
230
236
|
},
|
|
231
|
-
|
|
237
|
+
flashListProps
|
|
232
238
|
);
|
|
233
239
|
}
|
|
234
240
|
|
|
@@ -276,6 +282,7 @@ class FlashList<T> extends React.PureComponent<
|
|
|
276
282
|
initialScrollIndex,
|
|
277
283
|
style,
|
|
278
284
|
contentContainerStyle,
|
|
285
|
+
renderScrollComponent,
|
|
279
286
|
...restProps
|
|
280
287
|
} = this.props;
|
|
281
288
|
|
|
@@ -291,12 +298,12 @@ class FlashList<T> extends React.PureComponent<
|
|
|
291
298
|
|
|
292
299
|
return (
|
|
293
300
|
<StickyHeaderContainer
|
|
294
|
-
overrideRowRenderer={this.
|
|
301
|
+
overrideRowRenderer={this.stickyOverrideRowRenderer}
|
|
295
302
|
applyWindowCorrection={this.applyWindowCorrection}
|
|
296
303
|
stickyHeaderIndices={stickyHeaderIndices}
|
|
297
304
|
style={
|
|
298
305
|
this.props.horizontal
|
|
299
|
-
? this.
|
|
306
|
+
? { ...this.getTransform() }
|
|
300
307
|
: { flex: 1, ...this.getTransform() }
|
|
301
308
|
}
|
|
302
309
|
>
|
|
@@ -350,6 +357,9 @@ class FlashList<T> extends React.PureComponent<
|
|
|
350
357
|
windowCorrectionConfig={this.getUpdatedWindowCorrectionConfig()}
|
|
351
358
|
itemAnimator={this.itemAnimator}
|
|
352
359
|
suppressBoundedSizeException
|
|
360
|
+
externalScrollView={
|
|
361
|
+
renderScrollComponent as RecyclerListViewProps["externalScrollView"]
|
|
362
|
+
}
|
|
353
363
|
/>
|
|
354
364
|
</StickyHeaderContainer>
|
|
355
365
|
);
|
|
@@ -486,7 +496,10 @@ class FlashList<T> extends React.PureComponent<
|
|
|
486
496
|
};
|
|
487
497
|
|
|
488
498
|
private getTransform() {
|
|
489
|
-
|
|
499
|
+
const transformStyle = this.props.horizontal
|
|
500
|
+
? this.transformStyleHorizontal
|
|
501
|
+
: this.transformStyle;
|
|
502
|
+
return (this.props.inverted && transformStyle) || undefined;
|
|
490
503
|
}
|
|
491
504
|
|
|
492
505
|
private getContentContainerInfo() {
|
|
@@ -530,11 +543,18 @@ class FlashList<T> extends React.PureComponent<
|
|
|
530
543
|
}
|
|
531
544
|
|
|
532
545
|
private separator = (index: number) => {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
546
|
+
// Make sure we have data and don't read out of bounds
|
|
547
|
+
if (
|
|
548
|
+
this.props.data === null ||
|
|
549
|
+
this.props.data === undefined ||
|
|
550
|
+
index + 1 >= this.props.data.length
|
|
551
|
+
) {
|
|
536
552
|
return null;
|
|
537
553
|
}
|
|
554
|
+
|
|
555
|
+
const leadingItem = this.props.data[index];
|
|
556
|
+
const trailingItem = this.props.data[index + 1];
|
|
557
|
+
|
|
538
558
|
const props = {
|
|
539
559
|
leadingItem,
|
|
540
560
|
trailingItem,
|
|
@@ -565,13 +585,18 @@ class FlashList<T> extends React.PureComponent<
|
|
|
565
585
|
};
|
|
566
586
|
|
|
567
587
|
private footer = () => {
|
|
588
|
+
/** The web version of CellContainer uses a div directly which doesn't compose styles the way a View does.
|
|
589
|
+
* We will skip using CellContainer on web to avoid this issue. `getFooterContainer` on web will
|
|
590
|
+
* return a View. */
|
|
591
|
+
const FooterContainer = getFooterContainer() ?? CellContainer;
|
|
568
592
|
return (
|
|
569
593
|
<>
|
|
570
|
-
<
|
|
594
|
+
<FooterContainer
|
|
595
|
+
index={-1}
|
|
571
596
|
style={[this.props.ListFooterComponentStyle, this.getTransform()]}
|
|
572
597
|
>
|
|
573
598
|
{this.getValidComponent(this.props.ListFooterComponent)}
|
|
574
|
-
</
|
|
599
|
+
</FooterContainer>
|
|
575
600
|
<View
|
|
576
601
|
style={{
|
|
577
602
|
paddingBottom: this.contentStyle.paddingBottom,
|
|
@@ -589,7 +614,8 @@ class FlashList<T> extends React.PureComponent<
|
|
|
589
614
|
this.state.dataProvider.getSize() > 0 ? (
|
|
590
615
|
<View style={{ opacity: 0 }} pointerEvents="none">
|
|
591
616
|
{this.rowRendererWithIndex(
|
|
592
|
-
Math.min(this.state.dataProvider.getSize() - 1, 1)
|
|
617
|
+
Math.min(this.state.dataProvider.getSize() - 1, 1),
|
|
618
|
+
RenderTargetOptions.Measurement
|
|
593
619
|
)}
|
|
594
620
|
</View>
|
|
595
621
|
) : null;
|
|
@@ -615,13 +641,18 @@ class FlashList<T> extends React.PureComponent<
|
|
|
615
641
|
this.stickyContentContainerRef?.setEnabled(this.isStickyEnabled);
|
|
616
642
|
};
|
|
617
643
|
|
|
618
|
-
private
|
|
644
|
+
private rowRendererSticky = (index: number) => {
|
|
645
|
+
return this.rowRendererWithIndex(index, RenderTargetOptions.StickyHeader);
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
private rowRendererWithIndex = (index: number, target: RenderTarget) => {
|
|
619
649
|
// known issue: expected to pass separators which isn't available in RLV
|
|
620
650
|
return this.props.renderItem?.({
|
|
621
|
-
item: this.props.data
|
|
651
|
+
item: this.props.data![index],
|
|
622
652
|
index,
|
|
653
|
+
target,
|
|
623
654
|
extraData: this.state.extraData?.value,
|
|
624
|
-
}
|
|
655
|
+
}) as JSX.Element;
|
|
625
656
|
};
|
|
626
657
|
|
|
627
658
|
/**
|
|
@@ -644,7 +675,7 @@ class FlashList<T> extends React.PureComponent<
|
|
|
644
675
|
: "row",
|
|
645
676
|
}}
|
|
646
677
|
>
|
|
647
|
-
{this.rowRendererWithIndex(index)}
|
|
678
|
+
{this.rowRendererWithIndex(index, RenderTargetOptions.Cell)}
|
|
648
679
|
</View>
|
|
649
680
|
{this.separator(index)}
|
|
650
681
|
</>
|
|
@@ -659,13 +690,18 @@ class FlashList<T> extends React.PureComponent<
|
|
|
659
690
|
this.stickyContentContainerRef = ref;
|
|
660
691
|
};
|
|
661
692
|
|
|
662
|
-
private
|
|
693
|
+
private stickyOverrideRowRenderer = (
|
|
694
|
+
_: any,
|
|
695
|
+
__: any,
|
|
696
|
+
index: number,
|
|
697
|
+
___: any
|
|
698
|
+
) => {
|
|
663
699
|
return (
|
|
664
700
|
<PureComponentWrapper
|
|
665
701
|
ref={this.stickyContentRef}
|
|
666
702
|
enabled={this.isStickyEnabled}
|
|
667
703
|
arg={index}
|
|
668
|
-
renderer={this.
|
|
704
|
+
renderer={this.rowRendererSticky}
|
|
669
705
|
/>
|
|
670
706
|
);
|
|
671
707
|
};
|
|
@@ -748,16 +784,39 @@ class FlashList<T> extends React.PureComponent<
|
|
|
748
784
|
viewOffset?: number | undefined;
|
|
749
785
|
viewPosition?: number | undefined;
|
|
750
786
|
}) {
|
|
751
|
-
|
|
752
|
-
this.rlvRef?.
|
|
787
|
+
const layout = this.rlvRef?.getLayout(params.index);
|
|
788
|
+
const listSize = this.rlvRef?.getRenderedSize();
|
|
789
|
+
|
|
790
|
+
if (layout && listSize) {
|
|
791
|
+
const itemOffset = this.props.horizontal ? layout.x : layout.y;
|
|
792
|
+
const fixedDimension = this.props.horizontal
|
|
793
|
+
? listSize.width
|
|
794
|
+
: listSize.height;
|
|
795
|
+
const itemSize = this.props.horizontal ? layout.width : layout.height;
|
|
796
|
+
const scrollOffset =
|
|
797
|
+
Math.max(
|
|
798
|
+
0,
|
|
799
|
+
itemOffset - (params.viewPosition ?? 0) * (fixedDimension - itemSize)
|
|
800
|
+
) - (params.viewOffset ?? 0);
|
|
801
|
+
this.rlvRef?.scrollToOffset(
|
|
802
|
+
scrollOffset,
|
|
803
|
+
scrollOffset,
|
|
804
|
+
Boolean(params.animated),
|
|
805
|
+
true
|
|
806
|
+
);
|
|
807
|
+
}
|
|
753
808
|
}
|
|
754
809
|
|
|
755
810
|
public scrollToItem(params: {
|
|
756
811
|
animated?: boolean | null | undefined;
|
|
757
812
|
item: any;
|
|
758
813
|
viewPosition?: number | undefined;
|
|
814
|
+
viewOffset?: number | undefined;
|
|
759
815
|
}) {
|
|
760
|
-
this.
|
|
816
|
+
const index = this.props.data?.indexOf(params.item) ?? -1;
|
|
817
|
+
if (index >= 0) {
|
|
818
|
+
this.scrollToIndex({ ...params, index });
|
|
819
|
+
}
|
|
761
820
|
}
|
|
762
821
|
|
|
763
822
|
public scrollToOffset(params: {
|
package/src/FlashListProps.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type React from "react";
|
|
1
2
|
import {
|
|
2
3
|
StyleProp,
|
|
3
4
|
ScrollViewProps,
|
|
@@ -12,10 +13,25 @@ import ViewToken from "./viewability/ViewToken";
|
|
|
12
13
|
|
|
13
14
|
export interface ListRenderItemInfo<TItem> {
|
|
14
15
|
item: TItem;
|
|
15
|
-
|
|
16
16
|
index: number;
|
|
17
|
+
/**
|
|
18
|
+
* FlashList may render your items for multiple reasons.
|
|
19
|
+
* Cell - This is for your list item
|
|
20
|
+
* Measurement - Might be invoked for size measurement and won't be visible. You can ignore this in analytics.
|
|
21
|
+
* StickyHeader - This is for your sticky header. Use this to change your item's appearance while it's being used as a sticky header.
|
|
22
|
+
*/
|
|
23
|
+
target: RenderTarget;
|
|
24
|
+
extraData?: any;
|
|
17
25
|
}
|
|
18
26
|
|
|
27
|
+
export type RenderTarget = "Cell" | "StickyHeader" | "Measurement";
|
|
28
|
+
|
|
29
|
+
export const RenderTargetOptions: Record<string, RenderTarget> = {
|
|
30
|
+
Cell: "Cell",
|
|
31
|
+
StickyHeader: "StickyHeader",
|
|
32
|
+
Measurement: "Measurement",
|
|
33
|
+
};
|
|
34
|
+
|
|
19
35
|
export type ListRenderItem<TItem> = (
|
|
20
36
|
info: ListRenderItemInfo<TItem>
|
|
21
37
|
) => React.ReactElement | null;
|
|
@@ -122,6 +138,13 @@ export interface FlashListProps<TItem> extends ScrollViewProps {
|
|
|
122
138
|
*/
|
|
123
139
|
ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
124
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Rendered as the main scrollview.
|
|
143
|
+
*/
|
|
144
|
+
renderScrollComponent?:
|
|
145
|
+
| React.ComponentType<ScrollViewProps>
|
|
146
|
+
| React.FC<ScrollViewProps>;
|
|
147
|
+
|
|
125
148
|
/**
|
|
126
149
|
* You can use `contentContainerStyle` to apply padding that will be applied to the whole content itself.
|
|
127
150
|
* For example, you can apply this padding, so that all of your items have leading and trailing space.
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import React, { useEffect } from "react";
|
|
2
|
-
import { ScrollView, Text } from "react-native";
|
|
2
|
+
import { Animated, ScrollView, Text, View } from "react-native";
|
|
3
3
|
import "@quilted/react-testing/matchers";
|
|
4
4
|
import { ProgressiveListView } from "recyclerlistview";
|
|
5
5
|
|
|
6
6
|
import Warnings from "../errors/Warnings";
|
|
7
7
|
import AutoLayoutView from "../native/auto-layout/AutoLayoutView";
|
|
8
8
|
import CellContainer from "../native/cell-container/CellContainer";
|
|
9
|
+
import { ListRenderItemInfo, RenderTargetOptions } from "../FlashListProps";
|
|
9
10
|
|
|
10
11
|
import { mountFlashList } from "./helpers/mountFlashList";
|
|
11
12
|
|
|
13
|
+
jest.mock("../native/cell-container/CellContainer", () => {
|
|
14
|
+
return jest.requireActual("../native/cell-container/CellContainer.ios.ts")
|
|
15
|
+
.default;
|
|
16
|
+
});
|
|
17
|
+
|
|
12
18
|
describe("FlashList", () => {
|
|
13
19
|
beforeEach(() => {
|
|
14
20
|
jest.clearAllMocks();
|
|
@@ -600,7 +606,9 @@ describe("FlashList", () => {
|
|
|
600
606
|
|
|
601
607
|
// items widths before layout manager change should be 400
|
|
602
608
|
flashList.findAll(CellContainer).forEach((cell) => {
|
|
603
|
-
|
|
609
|
+
if (cell.props.index !== -1) {
|
|
610
|
+
expect(cell.instance.props.style.width).toBe(400);
|
|
611
|
+
}
|
|
604
612
|
});
|
|
605
613
|
|
|
606
614
|
// This will cause a layout manager change
|
|
@@ -613,7 +621,9 @@ describe("FlashList", () => {
|
|
|
613
621
|
|
|
614
622
|
// items widths after layout manager change should be 900
|
|
615
623
|
flashList.findAll(CellContainer).forEach((cell) => {
|
|
616
|
-
|
|
624
|
+
if (cell.props.index !== -1) {
|
|
625
|
+
expect(cell.instance.props.style.width).toBe(900);
|
|
626
|
+
}
|
|
617
627
|
});
|
|
618
628
|
|
|
619
629
|
flashList.unmount();
|
|
@@ -712,6 +722,31 @@ describe("FlashList", () => {
|
|
|
712
722
|
).toBe(250);
|
|
713
723
|
flashList.unmount();
|
|
714
724
|
});
|
|
725
|
+
it("forwards correct renderTarget", () => {
|
|
726
|
+
const renderItem = ({ target }: ListRenderItemInfo<string>) => {
|
|
727
|
+
return <Text>{target}</Text>;
|
|
728
|
+
};
|
|
729
|
+
const flashList = mountFlashList({
|
|
730
|
+
data: ["0"],
|
|
731
|
+
stickyHeaderIndices: [0],
|
|
732
|
+
renderItem,
|
|
733
|
+
});
|
|
734
|
+
expect(flashList.find(Animated.View)?.find(Text)?.props.children).toBe(
|
|
735
|
+
RenderTargetOptions.StickyHeader
|
|
736
|
+
);
|
|
737
|
+
expect(flashList.find(View)?.find(Text)?.props.children).toBe(
|
|
738
|
+
RenderTargetOptions.Cell
|
|
739
|
+
);
|
|
740
|
+
const flashListHorizontal = mountFlashList({
|
|
741
|
+
renderItem,
|
|
742
|
+
horizontal: true,
|
|
743
|
+
});
|
|
744
|
+
expect(
|
|
745
|
+
flashListHorizontal
|
|
746
|
+
.findAllWhere((node: any) => node?.props?.style?.opacity === 0)[0]
|
|
747
|
+
.find(Text)?.props.children
|
|
748
|
+
).toBe("Measurement");
|
|
749
|
+
});
|
|
715
750
|
it("force updates items only when renderItem change", () => {
|
|
716
751
|
const renderItem = jest.fn(() => <Text>Test</Text>);
|
|
717
752
|
const flashList = mountFlashList({
|
|
@@ -735,4 +770,58 @@ describe("FlashList", () => {
|
|
|
735
770
|
flashList.setProps({ disableAutoLayout: true });
|
|
736
771
|
expect(flashList.find(AutoLayoutView)?.props.disableAutoLayout).toBe(true);
|
|
737
772
|
});
|
|
773
|
+
it("computes correct scrollTo offset when view position is specified", () => {
|
|
774
|
+
const flashList = mountFlashList({
|
|
775
|
+
data: new Array(40).fill(1).map((_, index) => {
|
|
776
|
+
return index.toString();
|
|
777
|
+
}),
|
|
778
|
+
});
|
|
779
|
+
const plv = flashList.find(ProgressiveListView)
|
|
780
|
+
?.instance as ProgressiveListView;
|
|
781
|
+
const scrollToOffset = jest.spyOn(plv, "scrollToOffset");
|
|
782
|
+
flashList.instance.scrollToIndex({ index: 10, viewPosition: 0.5 });
|
|
783
|
+
|
|
784
|
+
// Each item is 200px in height and to position it in the middle of the window (900 x 400), its offset needs to be
|
|
785
|
+
// reduced by 350px. That gives us 1650. Other test cases follow the same logic.
|
|
786
|
+
expect(scrollToOffset).toBeCalledWith(1650, 1650, false, true);
|
|
787
|
+
flashList.instance.scrollToItem({
|
|
788
|
+
item: "10",
|
|
789
|
+
viewPosition: 0.5,
|
|
790
|
+
});
|
|
791
|
+
expect(scrollToOffset).toBeCalledWith(1650, 1650, false, true);
|
|
792
|
+
flashList.setProps({ horizontal: true });
|
|
793
|
+
flashList.instance.scrollToItem({
|
|
794
|
+
item: "10",
|
|
795
|
+
viewPosition: 0.5,
|
|
796
|
+
});
|
|
797
|
+
expect(scrollToOffset).toBeCalledWith(1900, 1900, false, true);
|
|
798
|
+
flashList.unmount();
|
|
799
|
+
});
|
|
800
|
+
it("computes correct scrollTo offset when view offset is specified", () => {
|
|
801
|
+
const flashList = mountFlashList({
|
|
802
|
+
data: new Array(40).fill(1).map((_, index) => {
|
|
803
|
+
return index.toString();
|
|
804
|
+
}),
|
|
805
|
+
});
|
|
806
|
+
const plv = flashList.find(ProgressiveListView)
|
|
807
|
+
?.instance as ProgressiveListView;
|
|
808
|
+
const scrollToOffset = jest.spyOn(plv, "scrollToOffset");
|
|
809
|
+
|
|
810
|
+
// Each item is 200px in height and to position it in the middle of the window (900 x 400), it's offset needs to be
|
|
811
|
+
// reduced by 350px + 100px offset. That gives us 1550. Other test cases follow the same logic.
|
|
812
|
+
flashList.instance.scrollToIndex({
|
|
813
|
+
index: 10,
|
|
814
|
+
viewPosition: 0.5,
|
|
815
|
+
viewOffset: 100,
|
|
816
|
+
});
|
|
817
|
+
expect(scrollToOffset).toBeCalledWith(1550, 1550, false, true);
|
|
818
|
+
flashList.setProps({ horizontal: true });
|
|
819
|
+
flashList.instance.scrollToItem({
|
|
820
|
+
item: "10",
|
|
821
|
+
viewPosition: 0.5,
|
|
822
|
+
viewOffset: 100,
|
|
823
|
+
});
|
|
824
|
+
expect(scrollToOffset).toBeCalledWith(1800, 1800, false, true);
|
|
825
|
+
flashList.unmount();
|
|
826
|
+
});
|
|
738
827
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getCellContainerPlatformStyles,
|
|
3
3
|
getItemAnimator,
|
|
4
|
-
} from "../
|
|
4
|
+
} from "../native/config/PlatformHelper.web";
|
|
5
5
|
|
|
6
6
|
describe("Platform Helper Web", () => {
|
|
7
7
|
it("can compute right transform for web", () => {
|
|
@@ -13,8 +13,18 @@ describe("Platform Helper Web", () => {
|
|
|
13
13
|
x: 30,
|
|
14
14
|
y: 30,
|
|
15
15
|
});
|
|
16
|
+
const transformHorizontalInvertedStyle = getCellContainerPlatformStyles(
|
|
17
|
+
true,
|
|
18
|
+
{
|
|
19
|
+
x: 30,
|
|
20
|
+
y: 30,
|
|
21
|
+
isHorizontal: true,
|
|
22
|
+
}
|
|
23
|
+
);
|
|
16
24
|
const expectedTransform = "translate(20px,70px)";
|
|
17
25
|
const expectedTransformInverted = "translate(30px,30px) scaleY(-1)";
|
|
26
|
+
const expectedTransformHorizontalInverted =
|
|
27
|
+
"translate(30px,30px) scaleX(-1)";
|
|
18
28
|
|
|
19
29
|
expect(transformStyle?.transform).toBe(expectedTransform);
|
|
20
30
|
expect(transformStyle?.WebkitTransform).toBe(expectedTransform);
|
|
@@ -22,6 +32,12 @@ describe("Platform Helper Web", () => {
|
|
|
22
32
|
expect(transformInvertedStyle?.WebkitTransform).toBe(
|
|
23
33
|
expectedTransformInverted
|
|
24
34
|
);
|
|
35
|
+
expect(transformHorizontalInvertedStyle?.transform).toBe(
|
|
36
|
+
expectedTransformHorizontalInverted
|
|
37
|
+
);
|
|
38
|
+
expect(transformHorizontalInvertedStyle?.WebkitTransform).toBe(
|
|
39
|
+
expectedTransformHorizontalInverted
|
|
40
|
+
);
|
|
25
41
|
});
|
|
26
42
|
it("can return an animator", () => {
|
|
27
43
|
expect(getItemAnimator()!["animateWillMount"]).toBeDefined();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { Text } from "react-native";
|
|
3
3
|
import "@quilted/react-testing/matchers";
|
|
4
4
|
import { mount, Root } from "@quilted/react-testing";
|
|
5
5
|
|
|
6
6
|
import FlashList from "../../FlashList";
|
|
7
|
-
import { FlashListProps } from "../../FlashListProps";
|
|
7
|
+
import { FlashListProps, ListRenderItem } from "../../FlashListProps";
|
|
8
8
|
|
|
9
9
|
jest.mock("../../FlashList", () => {
|
|
10
10
|
const ActualFlashList = jest.requireActual("../../FlashList").default;
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { View } from "react-native";
|
|
1
|
+
import { requireNativeComponent } from "react-native";
|
|
3
2
|
|
|
4
3
|
import { AutoLayoutViewNativeComponentProps } from "./AutoLayoutViewNativeComponentProps";
|
|
5
4
|
|
|
6
5
|
const AutoLayoutViewNativeComponent =
|
|
7
|
-
|
|
6
|
+
requireNativeComponent<AutoLayoutViewNativeComponentProps>("AutoLayoutView");
|
|
8
7
|
export default AutoLayoutViewNativeComponent;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { requireNativeComponent } from "react-native";
|
|
2
|
+
|
|
3
|
+
import { AutoLayoutViewNativeComponentProps } from "./AutoLayoutViewNativeComponentProps";
|
|
4
|
+
|
|
5
|
+
const AutoLayoutViewNativeComponent =
|
|
6
|
+
requireNativeComponent<AutoLayoutViewNativeComponentProps>("AutoLayoutView");
|
|
7
|
+
export default AutoLayoutViewNativeComponent;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HostComponent, View } from "react-native";
|
|
2
2
|
|
|
3
3
|
import { AutoLayoutViewNativeComponentProps } from "./AutoLayoutViewNativeComponentProps";
|
|
4
4
|
|
|
5
5
|
const AutoLayoutViewNativeComponent =
|
|
6
|
-
|
|
6
|
+
View as any as HostComponent<AutoLayoutViewNativeComponentProps>;
|
|
7
7
|
export default AutoLayoutViewNativeComponent;
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, ViewProps } from "react-native";
|
|
3
|
+
|
|
4
|
+
export interface CellContainerProps extends ViewProps {
|
|
5
|
+
index: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const CellContainer = React.forwardRef(
|
|
9
|
+
(props: CellContainerProps, ref: any) => {
|
|
10
|
+
return <View ref={ref} {...props} />;
|
|
11
|
+
}
|
|
12
|
+
);
|
|
13
|
+
CellContainer.displayName = "CellContainer";
|
|
14
|
+
export default CellContainer;
|
|
@@ -5,7 +5,7 @@ const PlatformConfig = {
|
|
|
5
5
|
};
|
|
6
6
|
const getCellContainerPlatformStyles = (
|
|
7
7
|
inverted: boolean,
|
|
8
|
-
parentProps: { x: number; y: number }
|
|
8
|
+
parentProps: { x: number; y: number; isHorizontal?: boolean }
|
|
9
9
|
): { transform: string; WebkitTransform: string } | undefined => {
|
|
10
10
|
return undefined;
|
|
11
11
|
};
|
|
@@ -13,4 +13,14 @@ const getCellContainerPlatformStyles = (
|
|
|
13
13
|
const getItemAnimator = (): BaseItemAnimator | undefined => {
|
|
14
14
|
return undefined;
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
const getFooterContainer = (): React.ComponentClass | undefined => {
|
|
18
|
+
return undefined;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
PlatformConfig,
|
|
23
|
+
getCellContainerPlatformStyles,
|
|
24
|
+
getItemAnimator,
|
|
25
|
+
getFooterContainer,
|
|
26
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BaseItemAnimator } from "recyclerlistview";
|
|
2
|
+
|
|
3
|
+
const PlatformConfig = {
|
|
4
|
+
defaultDrawDistance: 250,
|
|
5
|
+
};
|
|
6
|
+
const getCellContainerPlatformStyles = (
|
|
7
|
+
inverted: boolean,
|
|
8
|
+
parentProps: { x: number; y: number; isHorizontal?: boolean }
|
|
9
|
+
): { transform: string; WebkitTransform: string } | undefined => {
|
|
10
|
+
return undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const getItemAnimator = (): BaseItemAnimator | undefined => {
|
|
14
|
+
return undefined;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const getFooterContainer = (): React.ComponentClass | undefined => {
|
|
18
|
+
return undefined;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
PlatformConfig,
|
|
23
|
+
getCellContainerPlatformStyles,
|
|
24
|
+
getItemAnimator,
|
|
25
|
+
getFooterContainer,
|
|
26
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BaseItemAnimator } from "recyclerlistview";
|
|
2
|
+
import { DefaultJSItemAnimator } from "recyclerlistview/dist/reactnative/platform/reactnative/itemanimators/defaultjsanimator/DefaultJSItemAnimator";
|
|
3
|
+
|
|
4
|
+
const PlatformConfig = {
|
|
5
|
+
defaultDrawDistance: 250,
|
|
6
|
+
};
|
|
7
|
+
const getCellContainerPlatformStyles = (
|
|
8
|
+
inverted: boolean,
|
|
9
|
+
parentProps: { x: number; y: number; isHorizontal?: boolean }
|
|
10
|
+
): { transform: string; WebkitTransform: string } | undefined => {
|
|
11
|
+
return undefined;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getItemAnimator = (): BaseItemAnimator | undefined => {
|
|
15
|
+
return new DefaultJSItemAnimator();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getFooterContainer = (): React.ComponentClass | undefined => {
|
|
19
|
+
return undefined;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
PlatformConfig,
|
|
24
|
+
getCellContainerPlatformStyles,
|
|
25
|
+
getItemAnimator,
|
|
26
|
+
getFooterContainer,
|
|
27
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
1
3
|
import { BaseItemAnimator } from "recyclerlistview";
|
|
2
4
|
import { DefaultJSItemAnimator } from "recyclerlistview/dist/reactnative/platform/reactnative/itemanimators/defaultjsanimator/DefaultJSItemAnimator";
|
|
3
5
|
|
|
@@ -6,10 +8,10 @@ const PlatformConfig = {
|
|
|
6
8
|
};
|
|
7
9
|
const getCellContainerPlatformStyles = (
|
|
8
10
|
inverted: boolean,
|
|
9
|
-
parentProps: { x: number; y: number }
|
|
11
|
+
parentProps: { x: number; y: number; isHorizontal?: boolean }
|
|
10
12
|
): { transform: string; WebkitTransform: string } | undefined => {
|
|
11
13
|
const transformValue = `translate(${parentProps.x}px,${parentProps.y}px)${
|
|
12
|
-
inverted ? ` scaleY(-1)` : ``
|
|
14
|
+
inverted ? ` ${parentProps.isHorizontal ? `scaleX` : `scaleY`}(-1)` : ``
|
|
13
15
|
}`;
|
|
14
16
|
return { transform: transformValue, WebkitTransform: transformValue };
|
|
15
17
|
};
|
|
@@ -17,4 +19,14 @@ const getCellContainerPlatformStyles = (
|
|
|
17
19
|
const getItemAnimator = (): BaseItemAnimator | undefined => {
|
|
18
20
|
return new DefaultJSItemAnimator();
|
|
19
21
|
};
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
const getFooterContainer = (): React.ComponentClass | undefined => {
|
|
24
|
+
return View;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
PlatformConfig,
|
|
29
|
+
getCellContainerPlatformStyles,
|
|
30
|
+
getItemAnimator,
|
|
31
|
+
getFooterContainer,
|
|
32
|
+
};
|
|
@@ -5,7 +5,7 @@ export class AverageWindow {
|
|
|
5
5
|
private currentAverage: number;
|
|
6
6
|
private currentCount: number;
|
|
7
7
|
private inputValues: (number | undefined)[];
|
|
8
|
-
private nextIndex
|
|
8
|
+
private nextIndex = 0;
|
|
9
9
|
constructor(size: number, startValue?: number) {
|
|
10
10
|
this.inputValues = new Array<number>(Math.max(1, size));
|
|
11
11
|
this.currentAverage = startValue ?? 0;
|