@shopify/flash-list 1.3.1 → 1.4.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 +7 -0
- package/dist/FlashList.d.ts +0 -1
- package/dist/FlashList.d.ts.map +1 -1
- package/dist/FlashList.js +17 -41
- package/dist/FlashList.js.map +1 -1
- package/dist/FlashListProps.d.ts +0 -1
- package/dist/FlashListProps.d.ts.map +1 -1
- package/dist/GridLayoutProviderWithProps.d.ts +9 -1
- package/dist/GridLayoutProviderWithProps.d.ts.map +1 -1
- package/dist/GridLayoutProviderWithProps.js +30 -1
- package/dist/GridLayoutProviderWithProps.js.map +1 -1
- package/dist/MasonryFlashList.d.ts +1 -1
- package/dist/MasonryFlashList.d.ts.map +1 -1
- package/dist/MasonryFlashList.js +13 -4
- package/dist/MasonryFlashList.js.map +1 -1
- package/dist/__tests__/ContentContainerUtils.test.d.ts +2 -0
- package/dist/__tests__/ContentContainerUtils.test.d.ts.map +1 -0
- package/dist/__tests__/ContentContainerUtils.test.js +85 -0
- package/dist/__tests__/ContentContainerUtils.test.js.map +1 -0
- package/dist/__tests__/FlashList.test.js +32 -0
- package/dist/__tests__/FlashList.test.js.map +1 -1
- package/dist/__tests__/GridLayoutProviderWithProps.test.js +10 -0
- package/dist/__tests__/GridLayoutProviderWithProps.test.js.map +1 -1
- package/dist/__tests__/MasonryFlashList.test.js +28 -0
- package/dist/__tests__/MasonryFlashList.test.js.map +1 -1
- package/dist/__tests__/helpers/mountMasonryFlashList.d.ts.map +1 -1
- package/dist/__tests__/helpers/mountMasonryFlashList.js +7 -2
- package/dist/__tests__/helpers/mountMasonryFlashList.js.map +1 -1
- package/dist/errors/Warnings.d.ts +0 -1
- package/dist/errors/Warnings.d.ts.map +1 -1
- package/dist/errors/Warnings.js +1 -3
- package/dist/errors/Warnings.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/ContentContainerUtils.d.ts +27 -0
- package/dist/utils/ContentContainerUtils.d.ts.map +1 -0
- package/dist/utils/ContentContainerUtils.js +48 -0
- package/dist/utils/ContentContainerUtils.js.map +1 -0
- package/package.json +3 -3
- package/src/FlashList.tsx +29 -50
- package/src/FlashListProps.ts +0 -1
- package/src/GridLayoutProviderWithProps.ts +39 -3
- package/src/MasonryFlashList.tsx +18 -4
- package/src/__tests__/ContentContainerUtils.test.ts +130 -0
- package/src/__tests__/FlashList.test.tsx +32 -0
- package/src/__tests__/GridLayoutProviderWithProps.test.ts +29 -0
- package/src/__tests__/MasonryFlashList.test.ts +28 -0
- package/src/__tests__/helpers/mountMasonryFlashList.tsx +6 -1
- package/src/errors/Warnings.ts +1 -4
- package/src/utils/ContentContainerUtils.ts +92 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ViewStyle } from "react-native";
|
|
2
|
+
import { Dimension } from "recyclerlistview";
|
|
3
|
+
import { ContentStyle } from "../FlashListProps";
|
|
4
|
+
export interface ContentStyleExplicit {
|
|
5
|
+
paddingTop: number;
|
|
6
|
+
paddingBottom: number;
|
|
7
|
+
paddingLeft: number;
|
|
8
|
+
paddingRight: number;
|
|
9
|
+
backgroundColor?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const updateContentStyle: (contentStyle: ContentStyle, contentContainerStyleSource: ContentStyle | undefined) => ContentStyleExplicit;
|
|
12
|
+
export declare const hasUnsupportedKeysInContentContainerStyle: (contentContainerStyleSource: ViewStyle | undefined) => boolean;
|
|
13
|
+
/** Applies padding corrections to given dimension. Mutates the dim object that was passed and returns it. */
|
|
14
|
+
export declare const applyContentContainerInsetForLayoutManager: (dim: Dimension, contentContainerStyle: ViewStyle | undefined, horizontal: boolean | undefined | null) => Dimension;
|
|
15
|
+
/** Returns padding to be applied on content container and will ignore paddings that have already been handled. */
|
|
16
|
+
export declare const getContentContainerPadding: (contentStyle: ContentStyleExplicit, horizontal: boolean | undefined | null) => {
|
|
17
|
+
paddingTop: number;
|
|
18
|
+
paddingBottom: number;
|
|
19
|
+
paddingLeft?: undefined;
|
|
20
|
+
paddingRight?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
paddingLeft: number;
|
|
23
|
+
paddingRight: number;
|
|
24
|
+
paddingTop?: undefined;
|
|
25
|
+
paddingBottom?: undefined;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=ContentContainerUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContentContainerUtils.d.ts","sourceRoot":"","sources":["../../src/utils/ContentContainerUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,kBAAkB,iBACf,YAAY,+BACG,YAAY,GAAG,SAAS,KACpD,oBAyBF,CAAC;AAEF,eAAO,MAAM,yCAAyC,gCACvB,SAAS,GAAG,SAAS,YAcnD,CAAC;AAEF,6GAA6G;AAC7G,eAAO,MAAM,0CAA0C,QAChD,SAAS,yBACS,SAAS,GAAG,SAAS,cAChC,OAAO,GAAG,SAAS,GAAG,IAAI,cASvC,CAAC;AAEF,kHAAkH;AAClH,eAAO,MAAM,0BAA0B,iBACvB,oBAAoB,cACtB,OAAO,GAAG,SAAS,GAAG,IAAI;;;;;;;;;;CAavC,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getContentContainerPadding = exports.applyContentContainerInsetForLayoutManager = exports.hasUnsupportedKeysInContentContainerStyle = exports.updateContentStyle = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var updateContentStyle = function (contentStyle, contentContainerStyleSource) {
|
|
6
|
+
var _a = (contentContainerStyleSource !== null && contentContainerStyleSource !== void 0 ? contentContainerStyleSource : {}), paddingTop = _a.paddingTop, paddingRight = _a.paddingRight, paddingBottom = _a.paddingBottom, paddingLeft = _a.paddingLeft, padding = _a.padding, paddingVertical = _a.paddingVertical, paddingHorizontal = _a.paddingHorizontal, backgroundColor = _a.backgroundColor;
|
|
7
|
+
contentStyle.paddingLeft = Number(paddingLeft || paddingHorizontal || padding || 0);
|
|
8
|
+
contentStyle.paddingRight = Number(paddingRight || paddingHorizontal || padding || 0);
|
|
9
|
+
contentStyle.paddingTop = Number(paddingTop || paddingVertical || padding || 0);
|
|
10
|
+
contentStyle.paddingBottom = Number(paddingBottom || paddingVertical || padding || 0);
|
|
11
|
+
contentStyle.backgroundColor = backgroundColor;
|
|
12
|
+
return contentStyle;
|
|
13
|
+
};
|
|
14
|
+
exports.updateContentStyle = updateContentStyle;
|
|
15
|
+
var hasUnsupportedKeysInContentContainerStyle = function (contentContainerStyleSource) {
|
|
16
|
+
var _a = (contentContainerStyleSource !== null && contentContainerStyleSource !== void 0 ? contentContainerStyleSource : {}), paddingTop = _a.paddingTop, paddingRight = _a.paddingRight, paddingBottom = _a.paddingBottom, paddingLeft = _a.paddingLeft, padding = _a.padding, paddingVertical = _a.paddingVertical, paddingHorizontal = _a.paddingHorizontal, backgroundColor = _a.backgroundColor, rest = tslib_1.__rest(_a, ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft", "padding", "paddingVertical", "paddingHorizontal", "backgroundColor"]);
|
|
17
|
+
return Object.keys(rest).length > 0;
|
|
18
|
+
};
|
|
19
|
+
exports.hasUnsupportedKeysInContentContainerStyle = hasUnsupportedKeysInContentContainerStyle;
|
|
20
|
+
/** Applies padding corrections to given dimension. Mutates the dim object that was passed and returns it. */
|
|
21
|
+
var applyContentContainerInsetForLayoutManager = function (dim, contentContainerStyle, horizontal) {
|
|
22
|
+
var contentStyle = (0, exports.updateContentStyle)({}, contentContainerStyle);
|
|
23
|
+
if (horizontal) {
|
|
24
|
+
dim.height -= contentStyle.paddingTop + contentStyle.paddingBottom;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
dim.width -= contentStyle.paddingLeft + contentStyle.paddingRight;
|
|
28
|
+
}
|
|
29
|
+
return dim;
|
|
30
|
+
};
|
|
31
|
+
exports.applyContentContainerInsetForLayoutManager = applyContentContainerInsetForLayoutManager;
|
|
32
|
+
/** Returns padding to be applied on content container and will ignore paddings that have already been handled. */
|
|
33
|
+
var getContentContainerPadding = function (contentStyle, horizontal) {
|
|
34
|
+
if (horizontal) {
|
|
35
|
+
return {
|
|
36
|
+
paddingTop: contentStyle.paddingTop,
|
|
37
|
+
paddingBottom: contentStyle.paddingBottom,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return {
|
|
42
|
+
paddingLeft: contentStyle.paddingLeft,
|
|
43
|
+
paddingRight: contentStyle.paddingRight,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
exports.getContentContainerPadding = getContentContainerPadding;
|
|
48
|
+
//# sourceMappingURL=ContentContainerUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContentContainerUtils.js","sourceRoot":"","sources":["../../src/utils/ContentContainerUtils.ts"],"names":[],"mappings":";;;;AAaO,IAAM,kBAAkB,GAAG,UAChC,YAA0B,EAC1B,2BAAqD;IAE/C,IAAA,KASF,CAAC,2BAA2B,aAA3B,2BAA2B,cAA3B,2BAA2B,GAAI,EAAE,CAAc,EARlD,UAAU,gBAAA,EACV,YAAY,kBAAA,EACZ,aAAa,mBAAA,EACb,WAAW,iBAAA,EACX,OAAO,aAAA,EACP,eAAe,qBAAA,EACf,iBAAiB,uBAAA,EACjB,eAAe,qBACmC,CAAC;IACrD,YAAY,CAAC,WAAW,GAAG,MAAM,CAC/B,WAAW,IAAI,iBAAiB,IAAI,OAAO,IAAI,CAAC,CACjD,CAAC;IACF,YAAY,CAAC,YAAY,GAAG,MAAM,CAChC,YAAY,IAAI,iBAAiB,IAAI,OAAO,IAAI,CAAC,CAClD,CAAC;IACF,YAAY,CAAC,UAAU,GAAG,MAAM,CAC9B,UAAU,IAAI,eAAe,IAAI,OAAO,IAAI,CAAC,CAC9C,CAAC;IACF,YAAY,CAAC,aAAa,GAAG,MAAM,CACjC,aAAa,IAAI,eAAe,IAAI,OAAO,IAAI,CAAC,CACjD,CAAC;IACF,YAAY,CAAC,eAAe,GAAG,eAAe,CAAC;IAC/C,OAAO,YAAoC,CAAC;AAC9C,CAAC,CAAC;AA5BW,QAAA,kBAAkB,sBA4B7B;AAEK,IAAM,yCAAyC,GAAG,UACvD,2BAAkD;IAElD,IAAM,KAUF,CAAC,2BAA2B,aAA3B,2BAA2B,cAA3B,2BAA2B,GAAI,EAAE,CAAc,EATlD,UAAU,gBAAA,EACV,YAAY,kBAAA,EACZ,aAAa,mBAAA,EACb,WAAW,iBAAA,EACX,OAAO,aAAA,EACP,eAAe,qBAAA,EACf,iBAAiB,uBAAA,EACjB,eAAe,qBAAA,EACZ,IAAI,sBATH,oIAUL,CAAmD,CAAC;IACrD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AACtC,CAAC,CAAC;AAfW,QAAA,yCAAyC,6CAepD;AAEF,6GAA6G;AACtG,IAAM,0CAA0C,GAAG,UACxD,GAAc,EACd,qBAA4C,EAC5C,UAAsC;IAEtC,IAAM,YAAY,GAAG,IAAA,0BAAkB,EAAC,EAAE,EAAE,qBAAqB,CAAC,CAAC;IACnE,IAAI,UAAU,EAAE;QACd,GAAG,CAAC,MAAM,IAAI,YAAY,CAAC,UAAU,GAAG,YAAY,CAAC,aAAa,CAAC;KACpE;SAAM;QACL,GAAG,CAAC,KAAK,IAAI,YAAY,CAAC,WAAW,GAAG,YAAY,CAAC,YAAY,CAAC;KACnE;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAZW,QAAA,0CAA0C,8CAYrD;AAEF,kHAAkH;AAC3G,IAAM,0BAA0B,GAAG,UACxC,YAAkC,EAClC,UAAsC;IAEtC,IAAI,UAAU,EAAE;QACd,OAAO;YACL,UAAU,EAAE,YAAY,CAAC,UAAU;YACnC,aAAa,EAAE,YAAY,CAAC,aAAa;SAC1C,CAAC;KACH;SAAM;QACL,OAAO;YACL,WAAW,EAAE,YAAY,CAAC,WAAW;YACrC,YAAY,EAAE,YAAY,CAAC,YAAY;SACxC,CAAC;KACH;AACH,CAAC,CAAC;AAfW,QAAA,0BAA0B,8BAerC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopify/flash-list",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"react-native",
|
|
6
6
|
"recyclerview",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"metro-react-native-babel-preset": "^0.71.1",
|
|
73
73
|
"prettier": "^2.7.1",
|
|
74
74
|
"react": "17.0.2",
|
|
75
|
-
"react-native": "0.68.
|
|
75
|
+
"react-native": "0.68.5",
|
|
76
76
|
"typescript": "^4.7.4"
|
|
77
77
|
},
|
|
78
78
|
"files": [
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"jestSetup.js"
|
|
85
85
|
],
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"recyclerlistview": "4.
|
|
87
|
+
"recyclerlistview": "4.2.0",
|
|
88
88
|
"tslib": "2.4.0"
|
|
89
89
|
}
|
|
90
90
|
}
|
package/src/FlashList.tsx
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
View,
|
|
4
4
|
RefreshControl,
|
|
5
5
|
LayoutChangeEvent,
|
|
6
|
-
ViewStyle,
|
|
7
6
|
NativeSyntheticEvent,
|
|
8
7
|
NativeScrollEvent,
|
|
9
8
|
} from "react-native";
|
|
@@ -27,7 +26,6 @@ import WarningList from "./errors/Warnings";
|
|
|
27
26
|
import ViewabilityManager from "./viewability/ViewabilityManager";
|
|
28
27
|
import {
|
|
29
28
|
FlashListProps,
|
|
30
|
-
ContentStyle,
|
|
31
29
|
RenderTarget,
|
|
32
30
|
RenderTargetOptions,
|
|
33
31
|
} from "./FlashListProps";
|
|
@@ -37,6 +35,12 @@ import {
|
|
|
37
35
|
getItemAnimator,
|
|
38
36
|
PlatformConfig,
|
|
39
37
|
} from "./native/config/PlatformHelper";
|
|
38
|
+
import {
|
|
39
|
+
ContentStyleExplicit,
|
|
40
|
+
getContentContainerPadding,
|
|
41
|
+
hasUnsupportedKeysInContentContainerStyle,
|
|
42
|
+
updateContentStyle,
|
|
43
|
+
} from "./utils/ContentContainerUtils";
|
|
40
44
|
|
|
41
45
|
interface StickyProps extends StickyContainerProps {
|
|
42
46
|
children: any;
|
|
@@ -67,7 +71,13 @@ class FlashList<T> extends React.PureComponent<
|
|
|
67
71
|
private transformStyle = { transform: [{ scaleY: -1 }] };
|
|
68
72
|
private transformStyleHorizontal = { transform: [{ scaleX: -1 }] };
|
|
69
73
|
private distanceFromWindow = 0;
|
|
70
|
-
private contentStyle:
|
|
74
|
+
private contentStyle: ContentStyleExplicit = {
|
|
75
|
+
paddingBottom: 0,
|
|
76
|
+
paddingTop: 0,
|
|
77
|
+
paddingLeft: 0,
|
|
78
|
+
paddingRight: 0,
|
|
79
|
+
};
|
|
80
|
+
|
|
71
81
|
private loadStartTime = 0;
|
|
72
82
|
private isListLoaded = false;
|
|
73
83
|
private windowCorrectionConfig: WindowCorrectionConfig = {
|
|
@@ -130,13 +140,13 @@ class FlashList<T> extends React.PureComponent<
|
|
|
130
140
|
if (Object.keys(this.props.style || {}).length > 0) {
|
|
131
141
|
console.warn(WarningList.styleUnsupported);
|
|
132
142
|
}
|
|
133
|
-
|
|
134
|
-
|
|
143
|
+
if (
|
|
144
|
+
hasUnsupportedKeysInContentContainerStyle(
|
|
145
|
+
this.props.contentContainerStyle
|
|
146
|
+
)
|
|
147
|
+
) {
|
|
135
148
|
console.warn(WarningList.styleContentContainerUnsupported);
|
|
136
149
|
}
|
|
137
|
-
if (contentStyleInfo.paddingIgnored) {
|
|
138
|
-
console.warn(WarningList.styleUnsupportedPaddingType);
|
|
139
|
-
}
|
|
140
150
|
}
|
|
141
151
|
|
|
142
152
|
// Some of the state variables need to update when props change
|
|
@@ -151,6 +161,14 @@ class FlashList<T> extends React.PureComponent<
|
|
|
151
161
|
newState.numColumns,
|
|
152
162
|
nextProps
|
|
153
163
|
);
|
|
164
|
+
} else if (prevState.layoutProvider.updateProps(nextProps).hasExpired) {
|
|
165
|
+
newState.layoutProvider = FlashList.getLayoutProvider<T>(
|
|
166
|
+
newState.numColumns,
|
|
167
|
+
nextProps
|
|
168
|
+
);
|
|
169
|
+
// RLV retries to reposition the first visible item on layout provider change.
|
|
170
|
+
// It's not required in our case so we're disabling it
|
|
171
|
+
newState.layoutProvider.shouldRefreshWithAnchoring = false;
|
|
154
172
|
}
|
|
155
173
|
if (nextProps.data !== prevState.data) {
|
|
156
174
|
newState.data = nextProps.data;
|
|
@@ -165,7 +183,6 @@ class FlashList<T> extends React.PureComponent<
|
|
|
165
183
|
newState.extraData = { value: nextProps.extraData };
|
|
166
184
|
}
|
|
167
185
|
newState.renderItem = nextProps.renderItem;
|
|
168
|
-
newState.layoutProvider.updateProps(nextProps);
|
|
169
186
|
return newState;
|
|
170
187
|
}
|
|
171
188
|
|
|
@@ -270,7 +287,7 @@ class FlashList<T> extends React.PureComponent<
|
|
|
270
287
|
|
|
271
288
|
render() {
|
|
272
289
|
this.isEmptyList = this.state.dataProvider.getSize() === 0;
|
|
273
|
-
this.contentStyle
|
|
290
|
+
updateContentStyle(this.contentStyle, this.props.contentContainerStyle);
|
|
274
291
|
|
|
275
292
|
const {
|
|
276
293
|
drawDistance,
|
|
@@ -329,6 +346,8 @@ class FlashList<T> extends React.PureComponent<
|
|
|
329
346
|
// Required to handle a scrollview bug. Check: https://github.com/Shopify/flash-list/pull/187
|
|
330
347
|
minHeight: 1,
|
|
331
348
|
minWidth: 1,
|
|
349
|
+
|
|
350
|
+
...getContentContainerPadding(this.contentStyle, horizontal),
|
|
332
351
|
},
|
|
333
352
|
...this.props.overrideProps,
|
|
334
353
|
}}
|
|
@@ -507,46 +526,6 @@ class FlashList<T> extends React.PureComponent<
|
|
|
507
526
|
return (this.props.inverted && transformStyle) || undefined;
|
|
508
527
|
}
|
|
509
528
|
|
|
510
|
-
private getContentContainerInfo() {
|
|
511
|
-
const {
|
|
512
|
-
paddingTop,
|
|
513
|
-
paddingRight,
|
|
514
|
-
paddingBottom,
|
|
515
|
-
paddingLeft,
|
|
516
|
-
padding,
|
|
517
|
-
paddingVertical,
|
|
518
|
-
paddingHorizontal,
|
|
519
|
-
backgroundColor,
|
|
520
|
-
...rest
|
|
521
|
-
} = (this.props.contentContainerStyle || {}) as ViewStyle;
|
|
522
|
-
const unsupportedKeys = Object.keys(rest).length > 0;
|
|
523
|
-
if (this.props.horizontal) {
|
|
524
|
-
const paddingIgnored =
|
|
525
|
-
padding || paddingVertical || paddingTop || paddingBottom;
|
|
526
|
-
return {
|
|
527
|
-
style: {
|
|
528
|
-
paddingLeft: paddingLeft || paddingHorizontal || padding || 0,
|
|
529
|
-
paddingRight: paddingRight || paddingHorizontal || padding || 0,
|
|
530
|
-
backgroundColor,
|
|
531
|
-
},
|
|
532
|
-
unsupportedKeys,
|
|
533
|
-
paddingIgnored,
|
|
534
|
-
};
|
|
535
|
-
} else {
|
|
536
|
-
const paddingIgnored =
|
|
537
|
-
padding || paddingHorizontal || paddingLeft || paddingRight;
|
|
538
|
-
return {
|
|
539
|
-
style: {
|
|
540
|
-
paddingTop: paddingTop || paddingVertical || padding || 0,
|
|
541
|
-
paddingBottom: paddingBottom || paddingVertical || padding || 0,
|
|
542
|
-
backgroundColor,
|
|
543
|
-
},
|
|
544
|
-
unsupportedKeys,
|
|
545
|
-
paddingIgnored,
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
529
|
private separator = (index: number) => {
|
|
551
530
|
// Make sure we have data and don't read out of bounds
|
|
552
531
|
if (
|
package/src/FlashListProps.ts
CHANGED
|
@@ -148,7 +148,6 @@ export interface FlashListProps<TItem> extends ScrollViewProps {
|
|
|
148
148
|
/**
|
|
149
149
|
* You can use `contentContainerStyle` to apply padding that will be applied to the whole content itself.
|
|
150
150
|
* For example, you can apply this padding, so that all of your items have leading and trailing space.
|
|
151
|
-
* Note: horizontal padding is ignored on vertical lists and vertical padding on horizontal ones.
|
|
152
151
|
*/
|
|
153
152
|
contentContainerStyle?: ContentStyle;
|
|
154
153
|
|
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
|
|
8
8
|
import { FlashListProps } from "./FlashListProps";
|
|
9
9
|
import { AverageWindow } from "./utils/AverageWindow";
|
|
10
|
+
import { applyContentContainerInsetForLayoutManager } from "./utils/ContentContainerUtils";
|
|
10
11
|
|
|
11
12
|
export default class GridLayoutProviderWithProps<T> extends GridLayoutProvider {
|
|
12
13
|
private props: FlashListProps<T>;
|
|
13
14
|
private layoutObject = { span: undefined, size: undefined };
|
|
14
|
-
|
|
15
15
|
private averageWindow: AverageWindow;
|
|
16
|
+
private renderWindowInsets: Dimension = { width: 0, height: 0 };
|
|
17
|
+
private _hasExpired = false;
|
|
16
18
|
public defaultEstimatedItemSize = 100;
|
|
17
19
|
|
|
18
20
|
constructor(
|
|
@@ -57,10 +59,36 @@ export default class GridLayoutProviderWithProps<T> extends GridLayoutProvider {
|
|
|
57
59
|
1,
|
|
58
60
|
props.estimatedItemSize ?? this.defaultEstimatedItemSize
|
|
59
61
|
);
|
|
62
|
+
this.renderWindowInsets = this.getAdjustedRenderWindowSize(
|
|
63
|
+
this.renderWindowInsets
|
|
64
|
+
);
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
public updateProps(props: FlashListProps<T>) {
|
|
67
|
+
public updateProps(props: FlashListProps<T>): GridLayoutProviderWithProps<T> {
|
|
68
|
+
this._hasExpired = this.props.numColumns !== props.numColumns;
|
|
69
|
+
const newInsetValues = applyContentContainerInsetForLayoutManager(
|
|
70
|
+
{
|
|
71
|
+
height: 0,
|
|
72
|
+
width: 0,
|
|
73
|
+
},
|
|
74
|
+
props.contentContainerStyle,
|
|
75
|
+
Boolean(props.horizontal)
|
|
76
|
+
);
|
|
77
|
+
this._hasExpired =
|
|
78
|
+
this._hasExpired ||
|
|
79
|
+
newInsetValues.height !== this.renderWindowInsets.height ||
|
|
80
|
+
newInsetValues.width !== this.renderWindowInsets.width;
|
|
81
|
+
this.renderWindowInsets = newInsetValues;
|
|
63
82
|
this.props = props;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* This methods returns true if the layout provider has expired and needs to be recreated.
|
|
88
|
+
* This can happen if the number of columns has changed or the render window size has changed in a way that cannot be handled by the layout provider internally.
|
|
89
|
+
*/
|
|
90
|
+
public get hasExpired() {
|
|
91
|
+
return this._hasExpired;
|
|
64
92
|
}
|
|
65
93
|
|
|
66
94
|
/**
|
|
@@ -104,7 +132,7 @@ export default class GridLayoutProviderWithProps<T> extends GridLayoutProvider {
|
|
|
104
132
|
this.averageWindow.currentValue
|
|
105
133
|
);
|
|
106
134
|
const newLayoutManager = super.newLayoutManager(
|
|
107
|
-
renderWindowSize,
|
|
135
|
+
this.getAdjustedRenderWindowSize(renderWindowSize),
|
|
108
136
|
isHorizontal,
|
|
109
137
|
cachedLayouts
|
|
110
138
|
);
|
|
@@ -134,4 +162,12 @@ export default class GridLayoutProviderWithProps<T> extends GridLayoutProvider {
|
|
|
134
162
|
this.layoutObject.span = undefined;
|
|
135
163
|
return this.layoutObject;
|
|
136
164
|
}
|
|
165
|
+
|
|
166
|
+
private getAdjustedRenderWindowSize(renderWindowSize: Dimension) {
|
|
167
|
+
return applyContentContainerInsetForLayoutManager(
|
|
168
|
+
{ ...renderWindowSize },
|
|
169
|
+
this.props.contentContainerStyle,
|
|
170
|
+
Boolean(this.props.horizontal)
|
|
171
|
+
);
|
|
172
|
+
}
|
|
137
173
|
}
|
package/src/MasonryFlashList.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import CustomError from "./errors/CustomError";
|
|
|
12
12
|
import ExceptionList from "./errors/ExceptionList";
|
|
13
13
|
import FlashList from "./FlashList";
|
|
14
14
|
import { FlashListProps, ListRenderItemInfo } from "./FlashListProps";
|
|
15
|
+
import { applyContentContainerInsetForLayoutManager } from "./utils/ContentContainerUtils";
|
|
15
16
|
import ViewToken from "./viewability/ViewToken";
|
|
16
17
|
|
|
17
18
|
export interface MasonryListRenderItemInfo<TItem>
|
|
@@ -31,6 +32,7 @@ export interface MasonryFlashListProps<T>
|
|
|
31
32
|
| "initialScrollIndex"
|
|
32
33
|
| "inverted"
|
|
33
34
|
| "onBlankArea"
|
|
35
|
+
| "renderItem"
|
|
34
36
|
| "viewabilityConfigCallbackPairs"
|
|
35
37
|
> {
|
|
36
38
|
/**
|
|
@@ -175,6 +177,12 @@ const MasonryFlashListComponent = React.forwardRef(
|
|
|
175
177
|
(dataSet[0]?.length ?? 0) *
|
|
176
178
|
(props.estimatedItemSize ?? defaultEstimatedItemSize);
|
|
177
179
|
|
|
180
|
+
const insetForLayoutManager = applyContentContainerInsetForLayoutManager(
|
|
181
|
+
{ height: 0, width: 0 },
|
|
182
|
+
props.contentContainerStyle,
|
|
183
|
+
false
|
|
184
|
+
);
|
|
185
|
+
|
|
178
186
|
return (
|
|
179
187
|
<FlashList
|
|
180
188
|
ref={getFlashList}
|
|
@@ -227,8 +235,9 @@ const MasonryFlashListComponent = React.forwardRef(
|
|
|
227
235
|
estimatedListSize={{
|
|
228
236
|
height: estimatedListSize.height,
|
|
229
237
|
width:
|
|
230
|
-
((getListRenderedSize(parentFlashList)?.width ||
|
|
231
|
-
estimatedListSize.width)
|
|
238
|
+
(((getListRenderedSize(parentFlashList)?.width ||
|
|
239
|
+
estimatedListSize.width) +
|
|
240
|
+
insetForLayoutManager.width) /
|
|
232
241
|
totalColumnFlex) *
|
|
233
242
|
(getColumnFlex?.(
|
|
234
243
|
args.item,
|
|
@@ -430,8 +439,13 @@ const updateViewTokens = (tokens: ViewToken[]) => {
|
|
|
430
439
|
for (let i = 0; i < length; i++) {
|
|
431
440
|
const token = tokens[i];
|
|
432
441
|
if (token.index !== null && token.index !== undefined) {
|
|
433
|
-
|
|
434
|
-
|
|
442
|
+
if (token.item) {
|
|
443
|
+
token.index = token.item.originalIndex;
|
|
444
|
+
token.item = token.item.originalItem;
|
|
445
|
+
} else {
|
|
446
|
+
token.index = null;
|
|
447
|
+
token.item = undefined;
|
|
448
|
+
}
|
|
435
449
|
}
|
|
436
450
|
}
|
|
437
451
|
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyContentContainerInsetForLayoutManager,
|
|
3
|
+
getContentContainerPadding,
|
|
4
|
+
hasUnsupportedKeysInContentContainerStyle,
|
|
5
|
+
updateContentStyle,
|
|
6
|
+
} from "../utils/ContentContainerUtils";
|
|
7
|
+
|
|
8
|
+
describe("ContentContainerUtils", () => {
|
|
9
|
+
it("detects unsupported keys in style", () => {
|
|
10
|
+
expect(hasUnsupportedKeysInContentContainerStyle({ flex: 1 })).toBe(true);
|
|
11
|
+
expect(hasUnsupportedKeysInContentContainerStyle({ paddingTop: 0 })).toBe(
|
|
12
|
+
false
|
|
13
|
+
);
|
|
14
|
+
expect(
|
|
15
|
+
hasUnsupportedKeysInContentContainerStyle({
|
|
16
|
+
paddingTop: 1,
|
|
17
|
+
paddingVertical: 1,
|
|
18
|
+
})
|
|
19
|
+
).toBe(false);
|
|
20
|
+
expect(
|
|
21
|
+
hasUnsupportedKeysInContentContainerStyle({
|
|
22
|
+
paddingTop: 1,
|
|
23
|
+
paddingVertical: 1,
|
|
24
|
+
padding: 1,
|
|
25
|
+
paddingLeft: 1,
|
|
26
|
+
paddingRight: 1,
|
|
27
|
+
paddingBottom: 1,
|
|
28
|
+
backgroundColor: "red",
|
|
29
|
+
paddingHorizontal: 1,
|
|
30
|
+
})
|
|
31
|
+
).toBe(false);
|
|
32
|
+
expect(hasUnsupportedKeysInContentContainerStyle({ margin: 1 })).toBe(true);
|
|
33
|
+
expect(hasUnsupportedKeysInContentContainerStyle({ padding: 1 })).toBe(
|
|
34
|
+
false
|
|
35
|
+
);
|
|
36
|
+
expect(
|
|
37
|
+
hasUnsupportedKeysInContentContainerStyle({ backgroundColor: "red" })
|
|
38
|
+
).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it("updated content style to have all supported styles defined", () => {
|
|
41
|
+
expect(
|
|
42
|
+
updateContentStyle({}, { padding: 1, backgroundColor: "red" })
|
|
43
|
+
).toEqual({
|
|
44
|
+
paddingTop: 1,
|
|
45
|
+
paddingBottom: 1,
|
|
46
|
+
paddingLeft: 1,
|
|
47
|
+
paddingRight: 1,
|
|
48
|
+
backgroundColor: "red",
|
|
49
|
+
});
|
|
50
|
+
expect(updateContentStyle({}, { paddingHorizontal: 1 })).toEqual({
|
|
51
|
+
paddingTop: 0,
|
|
52
|
+
paddingBottom: 0,
|
|
53
|
+
paddingLeft: 1,
|
|
54
|
+
paddingRight: 1,
|
|
55
|
+
});
|
|
56
|
+
expect(updateContentStyle({}, { paddingVertical: 1 })).toEqual({
|
|
57
|
+
paddingTop: 1,
|
|
58
|
+
paddingBottom: 1,
|
|
59
|
+
paddingLeft: 0,
|
|
60
|
+
paddingRight: 0,
|
|
61
|
+
});
|
|
62
|
+
expect(
|
|
63
|
+
updateContentStyle({}, { paddingLeft: "1", paddingVertical: "1" })
|
|
64
|
+
).toEqual({
|
|
65
|
+
paddingTop: 1,
|
|
66
|
+
paddingBottom: 1,
|
|
67
|
+
paddingLeft: 1,
|
|
68
|
+
paddingRight: 0,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
it("computes correct layout manager insets", () => {
|
|
72
|
+
expect(
|
|
73
|
+
applyContentContainerInsetForLayoutManager(
|
|
74
|
+
{ height: 0, width: 0 },
|
|
75
|
+
{ padding: 1 },
|
|
76
|
+
false
|
|
77
|
+
)
|
|
78
|
+
).toEqual({ height: 0, width: -2 });
|
|
79
|
+
expect(
|
|
80
|
+
applyContentContainerInsetForLayoutManager(
|
|
81
|
+
{ height: 0, width: 0 },
|
|
82
|
+
{ padding: 1 },
|
|
83
|
+
true
|
|
84
|
+
)
|
|
85
|
+
).toEqual({ height: -2, width: 0 });
|
|
86
|
+
expect(
|
|
87
|
+
applyContentContainerInsetForLayoutManager(
|
|
88
|
+
{ height: 0, width: 0 },
|
|
89
|
+
{ paddingVertical: 1 },
|
|
90
|
+
true
|
|
91
|
+
)
|
|
92
|
+
).toEqual({ height: -2, width: 0 });
|
|
93
|
+
});
|
|
94
|
+
it("calculated correct padding for scrollview content", () => {
|
|
95
|
+
expect(
|
|
96
|
+
getContentContainerPadding(
|
|
97
|
+
{
|
|
98
|
+
paddingLeft: 1,
|
|
99
|
+
paddingTop: 1,
|
|
100
|
+
paddingBottom: 1,
|
|
101
|
+
paddingRight: 1,
|
|
102
|
+
backgroundColor: "red",
|
|
103
|
+
},
|
|
104
|
+
true
|
|
105
|
+
)
|
|
106
|
+
).toEqual({
|
|
107
|
+
paddingTop: 1,
|
|
108
|
+
paddingBottom: 1,
|
|
109
|
+
paddingLeft: undefined,
|
|
110
|
+
paddingRight: undefined,
|
|
111
|
+
});
|
|
112
|
+
expect(
|
|
113
|
+
getContentContainerPadding(
|
|
114
|
+
{
|
|
115
|
+
paddingLeft: 1,
|
|
116
|
+
paddingTop: 1,
|
|
117
|
+
paddingBottom: 1,
|
|
118
|
+
paddingRight: 1,
|
|
119
|
+
backgroundColor: "red",
|
|
120
|
+
},
|
|
121
|
+
false
|
|
122
|
+
)
|
|
123
|
+
).toEqual({
|
|
124
|
+
paddingTop: undefined,
|
|
125
|
+
paddingBottom: undefined,
|
|
126
|
+
paddingLeft: 1,
|
|
127
|
+
paddingRight: 1,
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -824,4 +824,36 @@ describe("FlashList", () => {
|
|
|
824
824
|
expect(scrollToOffset).toBeCalledWith(1800, 1800, false, true);
|
|
825
825
|
flashList.unmount();
|
|
826
826
|
});
|
|
827
|
+
it("applies horizontal content container padding for vertical list", () => {
|
|
828
|
+
const flashList = mountFlashList({
|
|
829
|
+
numColumns: 4,
|
|
830
|
+
contentContainerStyle: { paddingHorizontal: 10 },
|
|
831
|
+
});
|
|
832
|
+
let hasLayoutItems = false;
|
|
833
|
+
flashList.instance.state.layoutProvider
|
|
834
|
+
.getLayoutManager()!
|
|
835
|
+
.getLayouts()
|
|
836
|
+
.forEach((layout) => {
|
|
837
|
+
hasLayoutItems = true;
|
|
838
|
+
expect(layout.width).toBe(95);
|
|
839
|
+
});
|
|
840
|
+
expect(hasLayoutItems).toBe(true);
|
|
841
|
+
flashList.unmount();
|
|
842
|
+
});
|
|
843
|
+
it("applies vertical content container padding for horizontal list", () => {
|
|
844
|
+
const flashList = mountFlashList({
|
|
845
|
+
horizontal: true,
|
|
846
|
+
contentContainerStyle: { paddingVertical: 10 },
|
|
847
|
+
});
|
|
848
|
+
let hasLayoutItems = false;
|
|
849
|
+
flashList.instance.state.layoutProvider
|
|
850
|
+
.getLayoutManager()!
|
|
851
|
+
.getLayouts()
|
|
852
|
+
.forEach((layout) => {
|
|
853
|
+
hasLayoutItems = true;
|
|
854
|
+
expect(layout.height).toBe(880);
|
|
855
|
+
});
|
|
856
|
+
expect(hasLayoutItems).toBe(true);
|
|
857
|
+
flashList.unmount();
|
|
858
|
+
});
|
|
827
859
|
});
|
|
@@ -147,4 +147,33 @@ describe("GridLayoutProviderWithProps", () => {
|
|
|
147
147
|
// horizontal list
|
|
148
148
|
runCacheUpdateTest(true);
|
|
149
149
|
});
|
|
150
|
+
it("expires if column count or padding changes", () => {
|
|
151
|
+
const flashList = mountFlashList();
|
|
152
|
+
const baseProps = flashList.instance.props;
|
|
153
|
+
expect(
|
|
154
|
+
flashList.instance.state.layoutProvider.updateProps({
|
|
155
|
+
...baseProps,
|
|
156
|
+
contentContainerStyle: { paddingTop: 10 },
|
|
157
|
+
}).hasExpired
|
|
158
|
+
).toBe(false);
|
|
159
|
+
expect(
|
|
160
|
+
flashList.instance.state.layoutProvider.updateProps({
|
|
161
|
+
...baseProps,
|
|
162
|
+
contentContainerStyle: { paddingBottom: 10 },
|
|
163
|
+
}).hasExpired
|
|
164
|
+
).toBe(false);
|
|
165
|
+
expect(
|
|
166
|
+
flashList.instance.state.layoutProvider.updateProps({
|
|
167
|
+
...baseProps,
|
|
168
|
+
contentContainerStyle: { paddingLeft: 10 },
|
|
169
|
+
}).hasExpired
|
|
170
|
+
).toBe(true);
|
|
171
|
+
flashList.instance.state.layoutProvider["_hasExpired"] = false;
|
|
172
|
+
expect(
|
|
173
|
+
flashList.instance.state.layoutProvider.updateProps({
|
|
174
|
+
...baseProps,
|
|
175
|
+
numColumns: 2,
|
|
176
|
+
}).hasExpired
|
|
177
|
+
).toBe(true);
|
|
178
|
+
});
|
|
150
179
|
});
|
|
@@ -261,4 +261,32 @@ describe("MasonryFlashList", () => {
|
|
|
261
261
|
)
|
|
262
262
|
).toBe(35339);
|
|
263
263
|
});
|
|
264
|
+
it("applies horizontal content container padding to the list", () => {
|
|
265
|
+
const masonryFlashList = mountMasonryFlashList({
|
|
266
|
+
numColumns: 4,
|
|
267
|
+
contentContainerStyle: { paddingHorizontal: 10 },
|
|
268
|
+
});
|
|
269
|
+
expect(masonryFlashList.findAll(ProgressiveListView).length).toBe(5);
|
|
270
|
+
masonryFlashList.findAll(ProgressiveListView).forEach((list, index) => {
|
|
271
|
+
if (index === 0) {
|
|
272
|
+
expect(list.instance.getRenderedSize().width).toBe(400);
|
|
273
|
+
expect(list.instance.getRenderedSize().height).toBe(900);
|
|
274
|
+
} else {
|
|
275
|
+
expect(list.instance.getRenderedSize().width).toBe(95);
|
|
276
|
+
expect(list.instance.getRenderedSize().height).toBe(900);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
masonryFlashList.unmount();
|
|
280
|
+
});
|
|
281
|
+
it("divides columns equally if no getColumnFlex is passed", () => {
|
|
282
|
+
const masonryFlashList = mountMasonryFlashList({
|
|
283
|
+
numColumns: 4,
|
|
284
|
+
});
|
|
285
|
+
const progressiveListView =
|
|
286
|
+
masonryFlashList.find(ProgressiveListView)!.instance;
|
|
287
|
+
expect(progressiveListView.getLayout(0).width).toBe(100);
|
|
288
|
+
expect(progressiveListView.getLayout(1).width).toBe(100);
|
|
289
|
+
expect(progressiveListView.getLayout(2).width).toBe(100);
|
|
290
|
+
expect(progressiveListView.getLayout(3).width).toBe(100);
|
|
291
|
+
});
|
|
264
292
|
});
|
|
@@ -16,7 +16,12 @@ jest.mock("../../FlashList", () => {
|
|
|
16
16
|
componentDidMount() {
|
|
17
17
|
super.componentDidMount();
|
|
18
18
|
this.rlvRef?._scrollComponent?._scrollViewRef?.props.onLayout({
|
|
19
|
-
nativeEvent: {
|
|
19
|
+
nativeEvent: {
|
|
20
|
+
layout: {
|
|
21
|
+
height: this.props.estimatedListSize?.height ?? 900,
|
|
22
|
+
width: this.props.estimatedListSize?.width ?? 400,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
20
25
|
});
|
|
21
26
|
}
|
|
22
27
|
}
|
package/src/errors/Warnings.ts
CHANGED
|
@@ -2,10 +2,7 @@ const WarningList = {
|
|
|
2
2
|
styleUnsupported:
|
|
3
3
|
"You have passed a style to FlashList. This list doesn't support styling, use contentContainerStyle or wrap the list in a parent and apply style to it instead.",
|
|
4
4
|
styleContentContainerUnsupported:
|
|
5
|
-
"FlashList only supports padding related props and backgroundColor in contentContainerStyle."
|
|
6
|
-
" Please remove other values as they're not used. In case of vertical lists horizontal padding is ignored and vice versa, if you need it apply padding to your items instead.",
|
|
7
|
-
styleUnsupportedPaddingType:
|
|
8
|
-
"FlashList will ignore horizontal padding in case of vertical lists and vertical padding if the list is horizontal. If you need to have it apply relevant padding to your items instead.",
|
|
5
|
+
"FlashList only supports padding related props and backgroundColor in contentContainerStyle. Please remove other values as they're not used.",
|
|
9
6
|
unusableRenderedSize:
|
|
10
7
|
"FlashList's rendered size is not usable. Either the height or width is too small (<2px). " +
|
|
11
8
|
"Please make sure that the parent view of the list has a valid size. FlashList will match the size of the parent.",
|