-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added
MasonryFlashList
which adds support for rendering masonry lay…
…outs (#587) * Added support for masonry * Fixed index calculations * simplified on end reached * Fix refs * Fix viewability events * Added masonry sample * Empty list added * unused items removed * fix display name * Fixed viewability precision * Added some comments * Fix header unmount * Initial doc * fix link * Fixed docs * Update masonry-layout.md * Improve docs * Remove unused variables * Added key extractor support * Improve data set compute * set horizontal to false * log removed * Added tests * Added e2e test * Add changelog * test fixes * Finished unit tests * Updated docs * Added autoOptimize algorithm * Added test for auto arrangement * improve e2e * fix default layout value * update podfile * fix onend reached callback * Fixed some review comments * change to getColumnFlex * Ignore lint * Added some more comments * Update documentation/docs/guides/masonry-layout.md Co-authored-by: Marek Fořt <[email protected]> * Update documentation/docs/guides/masonry-layout.md Co-authored-by: Marek Fořt <[email protected]> * Update documentation/docs/guides/masonry-layout.md Co-authored-by: Marek Fořt <[email protected]> * Update documentation/docs/guides/masonry-layout.md Co-authored-by: Marek Fořt <[email protected]> * Update documentation/docs/guides/masonry-layout.md Co-authored-by: Marek Fořt <[email protected]> * Update documentation/docs/guides/masonry-layout.md Co-authored-by: Marek Fořt <[email protected]> * Update documentation/docs/guides/masonry-layout.md Co-authored-by: Marek Fořt <[email protected]> * Address some more review comments * Added code sample Co-authored-by: Marek Fořt <[email protected]>
- Loading branch information
1 parent
456d4f5
commit b58b5a6
Showing
23 changed files
with
1,041 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
--- | ||
id: masonry | ||
title: Masonry Layout | ||
--- | ||
|
||
Masonry Layout allows you to create a grid of items with different heights. It is a great way to display a collection of images with different sizes. | ||
|
||
<div align="center"> | ||
<img src="https://user-images.githubusercontent.com/7811728/188055598-41f5c961-0dd0-4bb9-bc6e-22d78596a036.png" height="500"/> | ||
</div> | ||
|
||
To get started, import `MasonryFlashList` from `@shopify/flash-list` and use it just like you would use `FlashList`: | ||
|
||
```tsx | ||
import React from "react"; | ||
import { View, Text, StatusBar } from "react-native"; | ||
import { MasonryFlashList } from "@shopify/flash-list"; | ||
import { DATA } from "./data"; | ||
|
||
const MyMasonryList = () => { | ||
return ( | ||
<MasonryFlashList | ||
data={DATA} | ||
numColumns={2} | ||
renderItem={({ item }) => <Text>{item.title}</Text>} | ||
estimatedItemSize={200} | ||
/> | ||
); | ||
}; | ||
``` | ||
|
||
**Note:** If you want `MasonryFlashList` to optimize item arrangement, enable `optimizeItemArrangement` and pass a valid [`overrideItemLayout`](../fundamentals/usage.md#overrideitemlayout) function. | ||
|
||
## Unsupported Props | ||
|
||
There are some props that `MasonryFlashList` does not support when compared to `FlashList`: | ||
|
||
- [`horizontal`](../fundamentals/usage.md#horizontal) | ||
- [`inverted`](../fundamentals/usage.md#inverted) | ||
- [`initialScrollIndex`](../fundamentals/usage.md#initialscrollindex) | ||
- [`viewabilityConfigCallbackPairs`](../fundamentals/usage.md#viewabilityconfigcallbackpairs) | ||
- [`onBlankArea`](../fundamentals/usage.md#onblankarea) | ||
|
||
## Additional Props | ||
|
||
`MasonryFlashList` supports these additional props on top of `FlashList`: | ||
|
||
### `optimizeItemArrangement` | ||
|
||
```tsx | ||
optimizeItemArrangement?: boolean; | ||
``` | ||
|
||
If enabled, MasonryFlashList will try to reduce difference in column height by modifying item order. If `true`, specifying [`overrideItemLayout`](../fundamentals/usage.md#overrideitemlayout) is required. Default value is `false`. | ||
|
||
### `getColumnFlex` | ||
|
||
```tsx | ||
getColumnFlex?: ( | ||
items: T[], | ||
columnIndex: number, | ||
maxColumns: number, | ||
extraData?: any | ||
) => number; | ||
``` | ||
|
||
`getColumnFlex` allows you to change the column widths of the list. This is helpful if you want some columns to be wider than the others. | ||
|
||
Example: | ||
|
||
```tsx | ||
// if `numColumns` is `3`, you can return `2` for `index 1` and `1` for the rest to achieve a `1:2:1` split by width. | ||
getColumnFlex={(items, index, maxColumns, extraData) => { | ||
return index === 1 ? 2 : 1; | ||
}} | ||
``` | ||
|
||
## Methods | ||
|
||
`MasonryFlashList` exposes the some methods that `FlashList` does. These are: | ||
|
||
### `scrollToEnd()` | ||
|
||
```tsx | ||
scrollToEnd?: (params?: { animated?: boolean | null | undefined }); | ||
``` | ||
|
||
Scrolls to the end of the content. | ||
|
||
### `scrollToOffset()` | ||
|
||
```tsx | ||
scrollToOffset(params: { | ||
animated?: boolean | null | undefined; | ||
offset: number; | ||
}); | ||
``` | ||
|
||
Scroll to a specific content pixel offset in the list. | ||
|
||
Parameter `offset` expects the offset to scroll to. | ||
|
||
Parameter `animated` (`false` by default) defines whether the list should animate while scrolling. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { assertSnapshot } from "./utils/SnapshotAsserts"; | ||
import { wipeArtifactsLocation } from "./utils/SnapshotLocation"; | ||
|
||
describe("Masonry", () => { | ||
const testNameLoad = "Masonry_with_FlashList_can_load"; | ||
const testNameScroll = "Masonry_with_FlashList_can_scroll"; | ||
|
||
beforeAll(async () => { | ||
await device.launchApp({ newInstance: true }); | ||
wipeArtifactsLocation("diffs"); | ||
}); | ||
|
||
beforeEach(async () => { | ||
await device.reloadReactNative(); | ||
await device.setOrientation("portrait"); | ||
}); | ||
|
||
it("can render columns correctly", async () => { | ||
await element(by.id("Masonry")).tap(); | ||
|
||
const testRunScreenshotPath = await element( | ||
by.id("MasonryList") | ||
).takeScreenshot(testNameLoad); | ||
|
||
assertSnapshot(testRunScreenshotPath, testNameLoad); | ||
}); | ||
it("can scroll", async () => { | ||
await element(by.id("Masonry")).tap(); | ||
await element(by.id("MasonryList")).scroll(2000, "down"); | ||
const testRunScreenshotPath = await element( | ||
by.id("MasonryList") | ||
).takeScreenshot(testNameScroll); | ||
|
||
assertSnapshot(testRunScreenshotPath, testNameScroll); | ||
}); | ||
}); |
Binary file added
BIN
+75.9 KB
...tifacts/ios/Masonry_with_FlashList_can_load/Masonry_with_FlashList_can_load.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+86.9 KB
...cts/ios/Masonry_with_FlashList_can_scroll/Masonry_with_FlashList_can_scroll.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
+79 Bytes
(100%)
...weet_cell_is_tapped/Twitter_goes_to_Tweet_detail_after_tweet_cell_is_tapped.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
-84 Bytes
(100%)
...om_of_the_list/Twitter_loads_a_new_page_when_gets_to_the_bottom_of_the_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
+277 Bytes
(100%)
...Twitter_with_FlashList_looks_the_same/Twitter_with_FlashList_looks_the_same.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
+277 Bytes
(100%)
...me_as_with_FlashList/Twitter_with_FlatList_looks_the_same_as_with_FlashList.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import React from "react"; | ||
import { Text, View, StyleSheet, Platform } from "react-native"; | ||
import { MasonryFlashList } from "@shopify/flash-list"; | ||
|
||
interface MasonryData { | ||
index: number; | ||
height: number; | ||
} | ||
|
||
export function Masonry() { | ||
const columnCount = 3; | ||
const data: MasonryData[] = new Array(999).fill(null).map((_, index) => { | ||
return { | ||
index, | ||
height: ((index * 10) % 100) + 100 / ((index % columnCount) + 1), | ||
}; | ||
}); | ||
return ( | ||
<View style={styles.container}> | ||
<MasonryFlashList | ||
testID="MasonryList" | ||
data={data} | ||
optimizeItemArrangement | ||
overrideItemLayout={(layout, item) => { | ||
layout.size = item.height; | ||
}} | ||
numColumns={columnCount} | ||
estimatedItemSize={150} | ||
ListHeaderComponent={ | ||
<Component | ||
item={{ index: 0, height: 100 }} | ||
text="Header" | ||
backgroundColor="red" | ||
/> | ||
} | ||
ListFooterComponent={ | ||
<Component | ||
item={{ index: 0, height: 100 }} | ||
text="Footer" | ||
backgroundColor="lightblue" | ||
/> | ||
} | ||
ListEmptyComponent={ | ||
<Component | ||
item={{ index: 0, height: 100 }} | ||
text="Empty" | ||
backgroundColor="black" | ||
/> | ||
} | ||
onViewableItemsChanged={(info) => { | ||
info.changed.forEach((item) => { | ||
if (item.isViewable) { | ||
console.log("Viewable:", item.index); | ||
} | ||
}); | ||
}} | ||
keyExtractor={(item, index) => { | ||
if (item.index !== index) { | ||
console.log("Key Extractor issue @", index); | ||
} | ||
return item.index.toString(); | ||
}} | ||
getItemType={(item, index) => { | ||
if (item.index !== index) { | ||
console.log(index); | ||
} | ||
return undefined; | ||
}} | ||
renderItem={({ item }) => { | ||
return <Component item={item} />; | ||
}} | ||
getColumnFlex={(_, index) => { | ||
return index === 1 ? 2 : 1; | ||
}} | ||
onLoad={({ elapsedTimeInMs }) => { | ||
console.log("List Load Time", elapsedTimeInMs); | ||
}} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
const Component = (props: { | ||
item: MasonryData; | ||
text?: string; | ||
backgroundColor?: string; | ||
}) => { | ||
return ( | ||
<View | ||
style={{ | ||
height: props.item.height, | ||
backgroundColor: props.backgroundColor ?? "darkgray", | ||
margin: 2, | ||
alignItems: "center", | ||
justifyContent: "center", | ||
borderRadius: 10, | ||
}} | ||
> | ||
<Text>{props.text ?? props.item.index}</Text> | ||
</View> | ||
); | ||
}; | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: Platform.OS === "web" ? undefined : 1, | ||
height: Platform.OS === "web" ? window.innerHeight : undefined, | ||
justifyContent: "center", | ||
backgroundColor: "#ecf0f1", | ||
paddingHorizontal: 2, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.