@shopify/flash-list 1.0.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.
Files changed (206) hide show
  1. package/CHANGELOG.md +159 -0
  2. package/LICENSE.md +7 -0
  3. package/README.md +65 -0
  4. package/RNFlashList.podspec +26 -0
  5. package/android/build.gradle +59 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutShadow.kt +94 -0
  8. package/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt +79 -0
  9. package/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt +69 -0
  10. package/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainer.java +16 -0
  11. package/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerImpl.kt +16 -0
  12. package/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt +27 -0
  13. package/android/src/main/kotlin/com/shopify/reactnative/flash_list/FlashListPackage.kt +19 -0
  14. package/android/src/test/java/com/shopify/reactnative/flash_list/AutoLayoutShadowTest.kt +146 -0
  15. package/android/src/test/java/com/shopify/reactnative/flash_list/models/Rect.kt +59 -0
  16. package/android/src/test/java/com/shopify/reactnative/flash_list/models/TestCollection.kt +6 -0
  17. package/android/src/test/java/com/shopify/reactnative/flash_list/models/TestDataModel.kt +8 -0
  18. package/android/src/test/resources/LayoutTestData.json +708 -0
  19. package/dist/AnimatedFlashList.d.ts +6 -0
  20. package/dist/AnimatedFlashList.d.ts.map +1 -0
  21. package/dist/AnimatedFlashList.js +8 -0
  22. package/dist/AnimatedFlashList.js.map +1 -0
  23. package/dist/FlashList.d.ts +121 -0
  24. package/dist/FlashList.d.ts.map +1 -0
  25. package/dist/FlashList.js +502 -0
  26. package/dist/FlashList.js.map +1 -0
  27. package/dist/FlashListProps.d.ts +251 -0
  28. package/dist/FlashListProps.d.ts.map +1 -0
  29. package/dist/FlashListProps.js +3 -0
  30. package/dist/FlashListProps.js.map +1 -0
  31. package/dist/GridLayoutProviderWithProps.d.ts +30 -0
  32. package/dist/GridLayoutProviderWithProps.d.ts.map +1 -0
  33. package/dist/GridLayoutProviderWithProps.js +80 -0
  34. package/dist/GridLayoutProviderWithProps.js.map +1 -0
  35. package/dist/PureComponentWrapper.d.ts +22 -0
  36. package/dist/PureComponentWrapper.d.ts.map +1 -0
  37. package/dist/PureComponentWrapper.js +37 -0
  38. package/dist/PureComponentWrapper.js.map +1 -0
  39. package/dist/__tests__/AverageWindow.test.d.ts +2 -0
  40. package/dist/__tests__/AverageWindow.test.d.ts.map +1 -0
  41. package/dist/__tests__/AverageWindow.test.js +69 -0
  42. package/dist/__tests__/AverageWindow.test.js.map +1 -0
  43. package/dist/__tests__/FlashList.test.d.ts +2 -0
  44. package/dist/__tests__/FlashList.test.d.ts.map +1 -0
  45. package/dist/__tests__/FlashList.test.js +656 -0
  46. package/dist/__tests__/FlashList.test.js.map +1 -0
  47. package/dist/__tests__/GridLayoutProviderWithProps.test.d.ts +2 -0
  48. package/dist/__tests__/GridLayoutProviderWithProps.test.d.ts.map +1 -0
  49. package/dist/__tests__/GridLayoutProviderWithProps.test.js +133 -0
  50. package/dist/__tests__/GridLayoutProviderWithProps.test.js.map +1 -0
  51. package/dist/__tests__/PlatformHelper.web.test.d.ts +2 -0
  52. package/dist/__tests__/PlatformHelper.web.test.d.ts.map +1 -0
  53. package/dist/__tests__/PlatformHelper.web.test.js +25 -0
  54. package/dist/__tests__/PlatformHelper.web.test.js.map +1 -0
  55. package/dist/__tests__/ViewabilityHelper.test.d.ts +2 -0
  56. package/dist/__tests__/ViewabilityHelper.test.d.ts.map +1 -0
  57. package/dist/__tests__/ViewabilityHelper.test.js +187 -0
  58. package/dist/__tests__/ViewabilityHelper.test.js.map +1 -0
  59. package/dist/__tests__/helpers/mountFlashList.d.ts +20 -0
  60. package/dist/__tests__/helpers/mountFlashList.d.ts.map +1 -0
  61. package/dist/__tests__/helpers/mountFlashList.js +44 -0
  62. package/dist/__tests__/helpers/mountFlashList.js.map +1 -0
  63. package/dist/__tests__/useBlankAreaTracker.test.d.ts +2 -0
  64. package/dist/__tests__/useBlankAreaTracker.test.d.ts.map +1 -0
  65. package/dist/__tests__/useBlankAreaTracker.test.js +179 -0
  66. package/dist/__tests__/useBlankAreaTracker.test.js.map +1 -0
  67. package/dist/benchmark/AutoScrollHelper.d.ts +18 -0
  68. package/dist/benchmark/AutoScrollHelper.d.ts.map +1 -0
  69. package/dist/benchmark/AutoScrollHelper.js +68 -0
  70. package/dist/benchmark/AutoScrollHelper.js.map +1 -0
  71. package/dist/benchmark/JSFPSMonitor.d.ts +23 -0
  72. package/dist/benchmark/JSFPSMonitor.d.ts.map +1 -0
  73. package/dist/benchmark/JSFPSMonitor.js +65 -0
  74. package/dist/benchmark/JSFPSMonitor.js.map +1 -0
  75. package/dist/benchmark/roundToDecimalPlaces.d.ts +2 -0
  76. package/dist/benchmark/roundToDecimalPlaces.d.ts.map +1 -0
  77. package/dist/benchmark/roundToDecimalPlaces.js +9 -0
  78. package/dist/benchmark/roundToDecimalPlaces.js.map +1 -0
  79. package/dist/benchmark/useBenchmark.d.ts +35 -0
  80. package/dist/benchmark/useBenchmark.d.ts.map +1 -0
  81. package/dist/benchmark/useBenchmark.js +167 -0
  82. package/dist/benchmark/useBenchmark.js.map +1 -0
  83. package/dist/benchmark/useBlankAreaTracker.d.ts +34 -0
  84. package/dist/benchmark/useBlankAreaTracker.d.ts.map +1 -0
  85. package/dist/benchmark/useBlankAreaTracker.js +67 -0
  86. package/dist/benchmark/useBlankAreaTracker.js.map +1 -0
  87. package/dist/benchmark/useDataMultiplier.d.ts +9 -0
  88. package/dist/benchmark/useDataMultiplier.d.ts.map +1 -0
  89. package/dist/benchmark/useDataMultiplier.js +25 -0
  90. package/dist/benchmark/useDataMultiplier.js.map +1 -0
  91. package/dist/benchmark/useFlatListBenchmark.d.ts +13 -0
  92. package/dist/benchmark/useFlatListBenchmark.d.ts.map +1 -0
  93. package/dist/benchmark/useFlatListBenchmark.js +100 -0
  94. package/dist/benchmark/useFlatListBenchmark.js.map +1 -0
  95. package/dist/errors/CustomError.d.ts +8 -0
  96. package/dist/errors/CustomError.d.ts.map +1 -0
  97. package/dist/errors/CustomError.js +14 -0
  98. package/dist/errors/CustomError.js.map +1 -0
  99. package/dist/errors/ExceptionList.d.ts +20 -0
  100. package/dist/errors/ExceptionList.d.ts.map +1 -0
  101. package/dist/errors/ExceptionList.js +22 -0
  102. package/dist/errors/ExceptionList.js.map +1 -0
  103. package/dist/errors/Warnings.d.ts +10 -0
  104. package/dist/errors/Warnings.d.ts.map +1 -0
  105. package/dist/errors/Warnings.js +15 -0
  106. package/dist/errors/Warnings.js.map +1 -0
  107. package/dist/index.d.ts +13 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +28 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/native/auto-layout/AutoLayoutView.d.ts +21 -0
  112. package/dist/native/auto-layout/AutoLayoutView.d.ts.map +1 -0
  113. package/dist/native/auto-layout/AutoLayoutView.js +48 -0
  114. package/dist/native/auto-layout/AutoLayoutView.js.map +1 -0
  115. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.d.ts +4 -0
  116. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.d.ts.map +1 -0
  117. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.js +6 -0
  118. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.js.map +1 -0
  119. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.web.d.ts +5 -0
  120. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.web.d.ts.map +1 -0
  121. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.web.js +6 -0
  122. package/dist/native/auto-layout/AutoLayoutViewNativeComponent.web.js.map +1 -0
  123. package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.d.ts +14 -0
  124. package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.d.ts.map +1 -0
  125. package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.js +3 -0
  126. package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.js.map +1 -0
  127. package/dist/native/cell-container/CellContainer.d.ts +6 -0
  128. package/dist/native/cell-container/CellContainer.d.ts.map +1 -0
  129. package/dist/native/cell-container/CellContainer.js +9 -0
  130. package/dist/native/cell-container/CellContainer.js.map +1 -0
  131. package/dist/native/cell-container/CellContainer.web.d.ts +7 -0
  132. package/dist/native/cell-container/CellContainer.web.d.ts.map +1 -0
  133. package/dist/native/cell-container/CellContainer.web.js +13 -0
  134. package/dist/native/cell-container/CellContainer.web.js.map +1 -0
  135. package/dist/tsconfig.tsbuildinfo +1 -0
  136. package/dist/utils/AverageWindow.d.ts +21 -0
  137. package/dist/utils/AverageWindow.d.ts.map +1 -0
  138. package/dist/utils/AverageWindow.js +49 -0
  139. package/dist/utils/AverageWindow.js.map +1 -0
  140. package/dist/utils/PlatformHelper.d.ts +14 -0
  141. package/dist/utils/PlatformHelper.d.ts.map +1 -0
  142. package/dist/utils/PlatformHelper.js +16 -0
  143. package/dist/utils/PlatformHelper.js.map +1 -0
  144. package/dist/utils/PlatformHelper.web.d.ts +14 -0
  145. package/dist/utils/PlatformHelper.web.d.ts.map +1 -0
  146. package/dist/utils/PlatformHelper.web.js +18 -0
  147. package/dist/utils/PlatformHelper.web.js.map +1 -0
  148. package/dist/viewability/ViewToken.d.ts +8 -0
  149. package/dist/viewability/ViewToken.d.ts.map +1 -0
  150. package/dist/viewability/ViewToken.js +3 -0
  151. package/dist/viewability/ViewToken.js.map +1 -0
  152. package/dist/viewability/ViewabilityHelper.d.ts +25 -0
  153. package/dist/viewability/ViewabilityHelper.d.ts.map +1 -0
  154. package/dist/viewability/ViewabilityHelper.js +104 -0
  155. package/dist/viewability/ViewabilityHelper.js.map +1 -0
  156. package/dist/viewability/ViewabilityManager.d.ts +24 -0
  157. package/dist/viewability/ViewabilityManager.d.ts.map +1 -0
  158. package/dist/viewability/ViewabilityManager.js +94 -0
  159. package/dist/viewability/ViewabilityManager.js.map +1 -0
  160. package/ios/RNFlashList.xcodeproj/project.pbxproj +3 -0
  161. package/ios/RNFlashList.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  162. package/ios/Sources/AutoLayoutView.swift +218 -0
  163. package/ios/Sources/AutoLayoutViewManager.m +14 -0
  164. package/ios/Sources/AutoLayoutViewManager.swift +12 -0
  165. package/ios/Sources/CellContainer.swift +9 -0
  166. package/ios/Sources/CellContainerManager.m +8 -0
  167. package/ios/Sources/CellContainerManager.swift +12 -0
  168. package/ios/Sources/FlatListPro-Bridging-Header.h +8 -0
  169. package/ios/Tests/AutoLayoutViewTests.swift +113 -0
  170. package/jestSetup.js +15 -0
  171. package/package.json +75 -0
  172. package/src/AnimatedFlashList.ts +11 -0
  173. package/src/FlashList.tsx +801 -0
  174. package/src/FlashListProps.ts +312 -0
  175. package/src/GridLayoutProviderWithProps.ts +137 -0
  176. package/src/PureComponentWrapper.tsx +42 -0
  177. package/src/__tests__/AverageWindow.test.ts +80 -0
  178. package/src/__tests__/FlashList.test.tsx +738 -0
  179. package/src/__tests__/GridLayoutProviderWithProps.test.ts +150 -0
  180. package/src/__tests__/PlatformHelper.web.test.ts +29 -0
  181. package/src/__tests__/ViewabilityHelper.test.ts +283 -0
  182. package/src/__tests__/helpers/mountFlashList.tsx +62 -0
  183. package/src/__tests__/useBlankAreaTracker.test.tsx +206 -0
  184. package/src/benchmark/AutoScrollHelper.ts +70 -0
  185. package/src/benchmark/JSFPSMonitor.ts +74 -0
  186. package/src/benchmark/roundToDecimalPlaces.ts +4 -0
  187. package/src/benchmark/useBenchmark.ts +240 -0
  188. package/src/benchmark/useBlankAreaTracker.ts +117 -0
  189. package/src/benchmark/useDataMultiplier.ts +19 -0
  190. package/src/benchmark/useFlatListBenchmark.ts +107 -0
  191. package/src/errors/CustomError.ts +10 -0
  192. package/src/errors/ExceptionList.ts +23 -0
  193. package/src/errors/Warnings.ts +18 -0
  194. package/src/index.ts +32 -0
  195. package/src/native/auto-layout/AutoLayoutView.tsx +72 -0
  196. package/src/native/auto-layout/AutoLayoutViewNativeComponent.ts +7 -0
  197. package/src/native/auto-layout/AutoLayoutViewNativeComponent.web.ts +8 -0
  198. package/src/native/auto-layout/AutoLayoutViewNativeComponentProps.ts +14 -0
  199. package/src/native/cell-container/CellContainer.ts +7 -0
  200. package/src/native/cell-container/CellContainer.web.tsx +9 -0
  201. package/src/utils/AverageWindow.ts +49 -0
  202. package/src/utils/PlatformHelper.ts +16 -0
  203. package/src/utils/PlatformHelper.web.ts +20 -0
  204. package/src/viewability/ViewToken.ts +7 -0
  205. package/src/viewability/ViewabilityHelper.ts +162 -0
  206. package/src/viewability/ViewabilityManager.ts +133 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,159 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
+ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ - `data` prop change will force update items only if `renderItem` is also updated
11
+ - https://github.com/Shopify/flash-list/pull/453
12
+
13
+ ## [1.0.0] - 2022-06-17
14
+
15
+ - Upgrade recyclerlistview to v3.3.0-beta.2
16
+ - https://github.com/Shopify/flash-list/pull/445
17
+ - Added web support
18
+ - https://github.com/Shopify/flash-list/pull/444
19
+ - Added `disableAutoLayout` prop to prevent conflicts with custom `CellRendererComponent`
20
+ - https://github.com/Shopify/flash-list/pull/452
21
+
22
+ ## [0.6.1] - 2022-05-26
23
+
24
+ - Fix amending layout on iOS
25
+ - https://github.com/Shopify/flash-list/pull/412
26
+ - Define `FlashList` props previously inherited from `VirtualizedList` and `FlatList` explicitly
27
+ - https://github.com/Shopify/flash-list/pull/386
28
+ - Make `estimatedItemSize` optional
29
+ - https://github.com/Shopify/flash-list/pull/378
30
+ - Change `overrideItemType` prop name to `getItemType`
31
+ - https://github.com/Shopify/flash-list/pull/369
32
+ - Added `useBlankAreaTracker` hook for tracking blank area in production
33
+ - https://github.com/Shopify/flash-list/pull/411
34
+ - Added `CellRendererComponent` prop
35
+ - https://github.com/Shopify/flash-list/pull/362
36
+ - Added automatic height measurement for horizontal lists even when parent isn't deterministic
37
+ - https://github.com/Shopify/flash-list/pull/409
38
+
39
+ ## [0.5.0] - 2022-04-29
40
+
41
+ - Fix finding props with testId
42
+ - https://github.com/Shopify/flash-list/pull/357
43
+ - Reuse cached layouts on orientation change
44
+ - https://github.com/Shopify/flash-list/pull/319
45
+
46
+ ## [0.4.6] - 2022-04-13
47
+
48
+ - Match FlashList's empty list behavior with FlatList
49
+ - https://github.com/Shopify/flash-list/pull/312
50
+
51
+ ## [0.4.5] - 2022-04-13
52
+
53
+ - Upgrade recyclerlistview to v3.2.0-beta.4
54
+
55
+ - https://github.com/Shopify/flash-list/pull/315
56
+
57
+ - Add viewability callbacks
58
+
59
+ - https://github.com/Shopify/flash-list/pull/301
60
+
61
+ - Calculate average item sizes automatically
62
+ - https://github.com/Shopify/flash-list/pull/296
63
+
64
+ ## [0.4.4] - 2022-04-06
65
+
66
+ - Fix `FlashList` mock when no data is provided
67
+ - https://github.com/Shopify/flash-list/pull/295
68
+
69
+ ## [0.4.3] - 2022-04-04
70
+
71
+ - Reduce number of render item calls
72
+
73
+ - https://github.com/Shopify/flash-list/pull/253
74
+
75
+ - Upgrade recyclerlistview to v3.2.0-beta.2
76
+ - https://github.com/Shopify/flash-list/pull/284
77
+
78
+ ## [0.4.2] - 2022-04-04
79
+
80
+ - Minor changes
81
+
82
+ ## [0.4.1] - 2022-03-29
83
+
84
+ - Crash fix for android activity switching (#256)
85
+
86
+ - https://github.com/Shopify/flash-list/pull/257
87
+
88
+ - initialScrollIndex, scrollTo methods will now account for size of header
89
+
90
+ - https://github.com/Shopify/flash-list/pull/194
91
+
92
+ - Added a new mock for easier testing of components with `FlashList`
93
+ - https://github.com/Shopify/flash-list/pull/236
94
+
95
+ ## [0.4.0] - 2022-03-23
96
+
97
+ - Add support for layout animations
98
+
99
+ - https://github.com/Shopify/flash-list/pull/183
100
+
101
+ - Suppress recyclerlistview's bounded size exception for some missing cases.
102
+
103
+ - https://github.com/Shopify/flash-list/pull/192
104
+
105
+ - Expose reference to recyclerlistview and firstItemOffset
106
+
107
+ - https://github.com/Shopify/flash-list/pull/217
108
+
109
+ - recyclerlistview upgraded to v3.1.0-alpha.9
110
+ - https://github.com/Shopify/flash-list/pull/227
111
+
112
+ ## [0.3.3] - 2022-03-16
113
+
114
+ - Prevent implicit scroll to top on device orientation change
115
+ - Change recyclerlistview's bounded size exception to a warning
116
+ - https://github.com/Shopify/flash-list/pull/187
117
+
118
+ ## [0.3.2] - 2022-03-15
119
+
120
+ - Minor changes
121
+
122
+ ## [0.3.1] - 2022-03-15
123
+
124
+ - Revert react-native-safe-area upgrade and minSdkVersion bump
125
+ - https://github.com/Shopify/flash-list/pull/184
126
+
127
+ ## [0.3.0] - 2022-03-15
128
+
129
+ - Fixed untranspiled library code by enforcing stricter TS rules.
130
+ - https://github.com/Shopify/flash-list/pull/181
131
+
132
+ ## [0.2.4] - 2022-03-14
133
+
134
+ - Added `onLoad` event that is called once the list has rendered items. This is required because FlashList doesn't render items in the first cycle.
135
+ - https://github.com/Shopify/flash-list/pull/180
136
+
137
+ ## [0.2.3] - 2022-03-10
138
+
139
+ - Fixing publish steps for transpiled code
140
+ - https://github.com/Shopify/flash-list/pull/150
141
+
142
+ ## [0.2.2] - 2022-03-10
143
+
144
+ - Fixing publish steps for transpiled code
145
+ - https://github.com/Shopify/flash-list/pull/149
146
+
147
+ ## [0.2.1] - 2022-03-09
148
+
149
+ - Bug fix for style and last separator
150
+ - https://github.com/Shopify/flash-list/pull/141
151
+
152
+ ## [0.2.0] - 2022-03-08
153
+
154
+ - Rename the component from `RecyclerFlatList` to `FlashList`
155
+ - https://github.com/Shopify/flash-list/pull/140
156
+
157
+ ## [0.1.0] - 2022-03-02
158
+
159
+ - Initial release
package/LICENSE.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2022-present, Shopify Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ [![Build status](./FlashList.png)](https://buildkite.com/shopify/flash-list)
2
+
3
+ <div align="center">
4
+ <a href="https://shopify.github.io/flash-list/">Website</a> •
5
+ <a href="https://discord.gg/k2gzABTfav">Discord</a> •
6
+ <a href="https://shopify.github.io/flash-list/docs/">Getting started</a> •
7
+ <a href="https://shopify.github.io/flash-list/docs/usage">Usage</a> •
8
+ <a href="https://shopify.github.io/flash-list/docs/performance-troubleshooting">Performance</a> •
9
+ <a href="https://shopify.github.io/flash-list/performant-components">Writing performant components</a> •
10
+ <a href="https://shopify.github.io/flash-list/docs/known-issues">Known Issues</a>
11
+ <br><br>
12
+
13
+ **Fast & performant React Native list. No more blank cells.**
14
+
15
+ Swap from FlatList in seconds. Get instant performance.
16
+
17
+ </div>
18
+
19
+ ## Installation
20
+
21
+ Add the package to your project via `yarn add @shopify/flash-list` and run `pod install` in the `ios` directory.
22
+
23
+ If you get the error `Plugin with id 'kotlin-android' not found` while building on Android, go to `android/build.gradle` and add `classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0")` inside your `dependencies` block. Please change the plugin version as per your needs.
24
+
25
+ ## Usage
26
+
27
+ We recommend reading the detailed documentation for using `FlashList` [here](https://shopify.github.io/flash-list/docs/usage).
28
+
29
+ But if you are familiar with [FlatList](https://reactnative.dev/docs/flatlist), you already know how to use `FlashList`. You can try out `FlashList` by changing the component name and adding the `estimatedItemSize` prop or refer to the example below:
30
+
31
+ ```jsx
32
+ import React from "react";
33
+ import { View, Text } from "react-native";
34
+ import { FlashList } from "@shopify/flash-list";
35
+
36
+ const DATA = [
37
+ {
38
+ title: "First Item",
39
+ },
40
+ {
41
+ title: "Second Item",
42
+ },
43
+ ];
44
+
45
+ const MyList = () => {
46
+ return (
47
+ <FlashList
48
+ data={DATA}
49
+ renderItem={({ item }) => <Text>{item.title}</Text>}
50
+ estimatedItemSize={200}
51
+ />
52
+ );
53
+ };
54
+ ```
55
+
56
+ To avoid common pitfalls, you can also follow these steps for migrating from `FlatList`, based on our own experiences:
57
+
58
+ 1. Switch from `FlatList` to `FlashList` and render the list once. You should see a warning about missing `estimatedItemSize` and a suggestion. Set this value as the prop directly.
59
+ 2. **Important**: Scan your [`renderItem`](https://shopify.github.io/flash-list/docs/usage/#renderitem) hierarchy for explicit `key` prop definitions and remove them. If you’re doing a `.map()` use indices as keys.
60
+ 3. If your list has heterogenous views, pass their types to `FlashList` using [`getItemType`](https://shopify.github.io/flash-list/docs/usage/#getitemtype) prop to improve performance.
61
+ 4. Do not test performance with JS dev mode on. Make sure you’re in release mode. `FlashList` can appear slower while in dev mode due to a small render buffer.
62
+
63
+ ## App / Playground
64
+
65
+ The [fixture](https://github.com/Shopify/flash-list/tree/main/fixture) is an example app showing how to use the library.
@@ -0,0 +1,26 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'RNFlashList'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.homepage = package['homepage']
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.platform = :ios, '11.0'
13
+ s.source = { git: 'https://github.com/shopify/flash-list.git', tag: 'v#{s.version}' }
14
+ s.source_files = 'ios/Sources/**/*'
15
+ s.requires_arc = true
16
+ s.swift_version = '5.0'
17
+
18
+ # Dependencies
19
+ s.dependency 'React-Core'
20
+
21
+ # Tests spec
22
+ s.test_spec 'Tests' do |test_spec|
23
+ test_spec.source_files = 'ios/Tests/**/*'
24
+ test_spec.framework = 'XCTest'
25
+ end
26
+ end
@@ -0,0 +1,59 @@
1
+ apply plugin: 'com.android.library'
2
+
3
+ apply plugin: 'kotlin-android'
4
+
5
+ def _ext = rootProject.ext
6
+
7
+ def _reactNativeVersion = _ext.has('reactNative') ? _ext.reactNative : '+'
8
+ def _compileSdkVersion = _ext.has('compileSdkVersion') ? _ext.compileSdkVersion : 30
9
+ def _buildToolsVersion = _ext.has('buildToolsVersion') ? _ext.buildToolsVersion : '30.0.2'
10
+ def _minSdkVersion = _ext.has('minSdkVersion') ? _ext.minSdkVersion : 21
11
+ def _targetSdkVersion = _ext.has('targetSdkVersion') ? _ext.targetSdkVersion : 30
12
+ def _kotlinVersion = _ext.has('kotlinVersion') ? _ext.kotlinVersion : '1.5.30'
13
+ def _junitVersion = _ext.has('junitVersion') ? _ext.junitVersion : '4.13.2'
14
+ def _mockitoVersion = _ext.has('mockitoVersion') ? _ext.mockitoVersion : '3.2.0'
15
+ def _androidTestRunnerVersion = _ext.has('androidTestRunnerVersion') ? _ext.androidTestRunnerVersion : '1.1.0'
16
+
17
+ android {
18
+ compileSdkVersion _compileSdkVersion
19
+ buildToolsVersion _buildToolsVersion
20
+
21
+ compileOptions {
22
+ sourceCompatibility JavaVersion.VERSION_1_8
23
+ targetCompatibility JavaVersion.VERSION_1_8
24
+ }
25
+
26
+ sourceSets {
27
+ main.java.srcDirs += 'src/main/kotlin'
28
+ debug.java.srcDirs += 'src/debug/kotlin'
29
+ test.java.srcDirs += 'src/test/kotlin'
30
+ androidTest.java.srcDirs += 'src/androidTest/kotlin'
31
+ }
32
+
33
+ defaultConfig {
34
+ minSdkVersion _minSdkVersion
35
+ targetSdkVersion _targetSdkVersion
36
+ versionCode 1
37
+ versionName "1.0"
38
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
39
+ }
40
+
41
+ lintOptions {
42
+ abortOnError false
43
+ }
44
+
45
+ testOptions {
46
+ unitTests.returnDefaultValues = true
47
+ }
48
+ }
49
+
50
+ dependencies {
51
+ compileOnly "com.facebook.react:react-native:${_reactNativeVersion}"
52
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${_kotlinVersion}"
53
+ testImplementation "junit:junit:${_junitVersion}"
54
+ testImplementation "org.mockito.kotlin:mockito-kotlin:${_mockitoVersion}"
55
+ testImplementation "org.mockito:mockito-inline:${_mockitoVersion}"
56
+ testImplementation 'com.google.code.gson:gson:2.8.9'
57
+ androidTestImplementation("androidx.test:runner:${_androidTestRunnerVersion}")
58
+ androidTestImplementation("androidx.test:rules:${_androidTestRunnerVersion}")
59
+ }
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.shopify.reactnative.flash_list">
3
+ </manifest>
@@ -0,0 +1,94 @@
1
+ package com.shopify.reactnative.flash_list
2
+
3
+ class AutoLayoutShadow {
4
+ var horizontal: Boolean = false
5
+ var scrollOffset: Int = 0
6
+ var offsetFromStart: Int = 0
7
+ var windowSize: Int = 0
8
+ var renderOffset = 0
9
+
10
+ var blankOffsetAtStart = 0 // Tracks blank area from the top
11
+ var blankOffsetAtEnd = 0 // Tracks blank area from the bottom
12
+
13
+ private var lastMaxBound = 0 // Tracks where the last pixel is drawn in the visible window
14
+ private var lastMinBound = 0 // Tracks where first pixel is drawn in the visible window
15
+
16
+ /** Checks for overlaps or gaps between adjacent items and then applies a correction (Only Grid layouts with varying spans)
17
+ * Performance: RecyclerListView renders very small number of views and this is not going to trigger multiple layouts on Android side. Not expecting any major perf issue. */
18
+ fun clearGapsAndOverlaps(sortedItems: Array<CellContainer>) {
19
+ var maxBound = 0
20
+ var minBound = Int.MAX_VALUE
21
+ var maxBoundNeighbour = 0
22
+ for (i in 0 until sortedItems.size - 1) {
23
+ val cell = sortedItems[i]
24
+ val neighbour = sortedItems[i + 1]
25
+ if (isWithinBounds(cell)) {
26
+ if (!horizontal) {
27
+ maxBound = kotlin.math.max(maxBound, cell.bottom);
28
+ minBound = kotlin.math.min(minBound, cell.top);
29
+ maxBoundNeighbour = maxBound
30
+ if (cell.left < neighbour.left) {
31
+ if (cell.right != neighbour.left) {
32
+ neighbour.right = cell.right + neighbour.width
33
+ neighbour.left = cell.right
34
+ }
35
+ if (cell.top != neighbour.top) {
36
+ neighbour.bottom = cell.top + neighbour.height
37
+ neighbour.top = cell.top
38
+ }
39
+ } else {
40
+ neighbour.bottom = maxBound + neighbour.height
41
+ neighbour.top = maxBound
42
+ }
43
+ if (isWithinBounds(neighbour)) {
44
+ maxBoundNeighbour = kotlin.math.max(maxBound, neighbour.bottom)
45
+ }
46
+ } else {
47
+ maxBound = kotlin.math.max(maxBound, cell.right);
48
+ minBound = kotlin.math.min(minBound, cell.left);
49
+ maxBoundNeighbour = maxBound
50
+ if (cell.top < neighbour.top) {
51
+ if (cell.bottom != neighbour.top) {
52
+ neighbour.bottom = cell.bottom + neighbour.height
53
+ neighbour.top = cell.bottom
54
+ }
55
+ if (cell.left != neighbour.left) {
56
+ neighbour.right = cell.left + neighbour.width
57
+ neighbour.left = cell.left
58
+ }
59
+ } else {
60
+ neighbour.right = maxBound + neighbour.width
61
+ neighbour.left = maxBound
62
+ }
63
+ if (isWithinBounds(neighbour)) {
64
+ maxBoundNeighbour = kotlin.math.max(maxBound, neighbour.right)
65
+ }
66
+ }
67
+ }
68
+ }
69
+ lastMaxBound = maxBoundNeighbour
70
+ lastMinBound = minBound
71
+ }
72
+
73
+ /** Offset provided by react can be one frame behind the real one, it's important that this method is called with offset taken directly from
74
+ * scrollview object */
75
+ fun computeBlankFromGivenOffset(actualScrollOffset: Int, distanceFromWindowStart: Int, distanceFromWindowEnd: Int): Int {
76
+ val actualScrollOffset = actualScrollOffset - offsetFromStart;
77
+ blankOffsetAtStart = lastMinBound - actualScrollOffset - distanceFromWindowStart
78
+ blankOffsetAtEnd = actualScrollOffset + windowSize - renderOffset - lastMaxBound - distanceFromWindowEnd
79
+ return kotlin.math.max(blankOffsetAtStart, blankOffsetAtEnd)
80
+ }
81
+
82
+ /** It's important to avoid correcting views outside the render window. An item that isn't being recycled might still remain in the view tree. If views outside get considered then gaps between
83
+ * unused items will cause algorithm to fail.*/
84
+ private fun isWithinBounds(cell: CellContainer): Boolean {
85
+ val scrollOffset = scrollOffset - offsetFromStart;
86
+ return if (!horizontal) {
87
+ (cell.top >= (scrollOffset - renderOffset) || cell.bottom >= (scrollOffset - renderOffset)) &&
88
+ (cell.top <= scrollOffset + windowSize || cell.bottom <= scrollOffset + windowSize)
89
+ } else {
90
+ (cell.left >= (scrollOffset - renderOffset) || cell.right >= (scrollOffset - renderOffset)) &&
91
+ (cell.left <= scrollOffset + windowSize || cell.right <= scrollOffset + windowSize)
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,79 @@
1
+ package com.shopify.reactnative.flash_list
2
+
3
+ import android.content.Context
4
+ import android.graphics.Canvas
5
+ import android.util.DisplayMetrics
6
+ import android.view.View
7
+ import com.facebook.react.bridge.Arguments
8
+ import com.facebook.react.bridge.ReactContext
9
+ import com.facebook.react.bridge.WritableMap
10
+ import com.facebook.react.uimanager.events.RCTEventEmitter
11
+ import com.facebook.react.views.view.ReactViewGroup
12
+
13
+
14
+ /** Container for all RecyclerListView children. This will automatically remove all gaps and overlaps for GridLayouts with flexible spans.
15
+ * Note: This cannot work for masonry layouts i.e, pinterest like layout */
16
+ class AutoLayoutView(context: Context) : ReactViewGroup(context) {
17
+ val alShadow = AutoLayoutShadow()
18
+ var enableInstrumentation = false
19
+ var disableAutoLayout = false
20
+
21
+ var pixelDensity = 1.0;
22
+
23
+ /** Overriding draw instead of onLayout. RecyclerListView uses absolute positions for each and every item which means that changes in child layouts may not trigger onLayout on this container. The same layout
24
+ * can still cause views to overlap. Therefore, it makes sense to override draw to do correction. */
25
+ override fun dispatchDraw(canvas: Canvas?) {
26
+ fixLayout()
27
+ super.dispatchDraw(canvas)
28
+
29
+ if (enableInstrumentation && parent?.parent != null) {
30
+ /** Since we need to call this method with scrollOffset on the UI thread and not with the one react has we're querying parent's parent
31
+ directly which will be a ScrollView. If it isn't reported values will be incorrect but the component will not break.
32
+ RecyclerListView is expected not to change the hierarchy of children. */
33
+
34
+ val scrollContainerSize = (parent.parent as View).let {
35
+ if (alShadow.horizontal) it.width else it.height
36
+ }
37
+ val scrollOffset = (parent.parent as View).let {
38
+ if (alShadow.horizontal) it.scrollX else it.scrollY
39
+ }
40
+ val startOffset = if (alShadow.horizontal) left else top
41
+ val endOffset = if (alShadow.horizontal) right else bottom
42
+
43
+ val distanceFromWindowStart = kotlin.math.max(startOffset - scrollOffset, 0)
44
+ val distanceFromWindowEnd = kotlin.math.max(scrollOffset + scrollContainerSize - endOffset, 0)
45
+
46
+ alShadow.computeBlankFromGivenOffset(scrollOffset, distanceFromWindowStart, distanceFromWindowEnd)
47
+ emitBlankAreaEvent()
48
+ }
49
+ }
50
+
51
+ /** Sorts views by index and then invokes clearGaps which does the correction.
52
+ * Performance: Sort is needed. Given relatively low number of views in RecyclerListView render tree this should be a non issue.*/
53
+ private fun fixLayout() {
54
+ if (childCount > 1 && !disableAutoLayout) {
55
+ val positionSortedViews: Array<CellContainer> = Array(childCount) {
56
+ val child = getChildAt(it)
57
+ if (child is CellContainer) {
58
+ child
59
+ } else {
60
+ throw IllegalStateException("CellRendererComponent outer view should always be CellContainer. Learn more here: https://shopify.github.io/flash-list/docs/usage#cellrenderercomponent.")
61
+ }
62
+ }
63
+ positionSortedViews.sortBy { it.index }
64
+ alShadow.offsetFromStart = if (alShadow.horizontal) left else top
65
+ alShadow.clearGapsAndOverlaps(positionSortedViews)
66
+ }
67
+ }
68
+
69
+ /** TODO: Check migration to Fabric */
70
+ private fun emitBlankAreaEvent() {
71
+ val event: WritableMap = Arguments.createMap()
72
+ event.putDouble("offsetStart", alShadow.blankOffsetAtStart / pixelDensity)
73
+ event.putDouble("offsetEnd", alShadow.blankOffsetAtEnd / pixelDensity)
74
+ val reactContext = context as ReactContext
75
+ reactContext
76
+ .getJSModule(RCTEventEmitter::class.java)
77
+ .receiveEvent(id, "onBlankAreaEvent", event)
78
+ }
79
+ }
@@ -0,0 +1,69 @@
1
+ package com.shopify.reactnative.flash_list
2
+
3
+ import com.facebook.react.module.annotations.ReactModule
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.facebook.react.uimanager.annotations.ReactProp
6
+ import com.facebook.react.views.view.ReactViewGroup
7
+ import com.facebook.react.views.view.ReactViewManager
8
+ import com.facebook.react.common.MapBuilder
9
+ import kotlin.math.roundToInt
10
+
11
+ /** ViewManager for AutoLayoutView - Container for all RecyclerListView children. Automatically removes all gaps and overlaps for GridLayouts with flexible spans.
12
+ * Note: This cannot work for masonry layouts i.e, pinterest like layout */
13
+ @ReactModule(name = AutoLayoutViewManager.REACT_CLASS)
14
+ class AutoLayoutViewManager: ReactViewManager() {
15
+
16
+ companion object {
17
+ const val REACT_CLASS = "AutoLayoutView"
18
+ }
19
+
20
+ override fun getName(): String {
21
+ return REACT_CLASS
22
+ }
23
+
24
+ override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
25
+ return AutoLayoutView(context).also { it.pixelDensity = context.resources.displayMetrics.density.toDouble() }
26
+ }
27
+
28
+ override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
29
+ return MapBuilder.builder<String, Any>().put(
30
+ "onBlankAreaEvent",
31
+ MapBuilder.of(
32
+ "registrationName", "onBlankAreaEvent")
33
+ ).build();
34
+ }
35
+
36
+ @ReactProp(name = "horizontal")
37
+ fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) {
38
+ view.alShadow.horizontal = isHorizontal
39
+ }
40
+
41
+ @ReactProp(name = "disableAutoLayout")
42
+ fun setDisableAutoLayout(view: AutoLayoutView, disableAutoLayout: Boolean) {
43
+ view.disableAutoLayout = disableAutoLayout
44
+ }
45
+
46
+ @ReactProp(name = "scrollOffset")
47
+ fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) {
48
+ view.alShadow.scrollOffset = convertToPixelLayout(scrollOffset, view.pixelDensity)
49
+ }
50
+
51
+ @ReactProp(name = "windowSize")
52
+ fun setWindowSize(view: AutoLayoutView, windowSize: Double) {
53
+ view.alShadow.windowSize = convertToPixelLayout(windowSize, view.pixelDensity)
54
+ }
55
+
56
+ @ReactProp(name = "renderAheadOffset")
57
+ fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) {
58
+ view.alShadow.renderOffset = convertToPixelLayout(renderOffset, view.pixelDensity)
59
+ }
60
+
61
+ @ReactProp(name = "enableInstrumentation")
62
+ fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) {
63
+ view.enableInstrumentation = enableInstrumentation
64
+ }
65
+
66
+ private fun convertToPixelLayout(dp: Double, density: Double): Int {
67
+ return (dp * density).roundToInt()
68
+ }
69
+ }
@@ -0,0 +1,16 @@
1
+ package com.shopify.reactnative.flash_list;
2
+
3
+ public interface CellContainer {
4
+ void setIndex(int value);
5
+ int getIndex();
6
+ void setLeft(int value);
7
+ int getLeft();
8
+ void setTop(int value);
9
+ int getTop();
10
+ void setRight(int value);
11
+ int getRight();
12
+ void setBottom(int value);
13
+ int getBottom();
14
+ int getHeight();
15
+ int getWidth();
16
+ }
@@ -0,0 +1,16 @@
1
+ package com.shopify.reactnative.flash_list
2
+
3
+ import android.content.Context
4
+ import com.facebook.react.views.view.ReactViewGroup
5
+
6
+ class CellContainerImpl(context: Context) : ReactViewGroup(context), CellContainer {
7
+ private var index = -1
8
+ override fun setIndex(value: Int) {
9
+ index = value
10
+ }
11
+
12
+ override fun getIndex(): Int {
13
+ return index
14
+ }
15
+
16
+ }
@@ -0,0 +1,27 @@
1
+ package com.shopify.reactnative.flash_list
2
+
3
+ import com.facebook.react.module.annotations.ReactModule
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.facebook.react.uimanager.annotations.ReactProp
6
+ import com.facebook.react.views.view.ReactViewGroup
7
+ import com.facebook.react.views.view.ReactViewManager
8
+
9
+ @ReactModule(name = AutoLayoutViewManager.REACT_CLASS)
10
+ class CellContainerManager: ReactViewManager() {
11
+ companion object {
12
+ const val REACT_CLASS = "CellContainer"
13
+ }
14
+
15
+ override fun getName(): String {
16
+ return REACT_CLASS
17
+ }
18
+
19
+ override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
20
+ return CellContainerImpl(context)
21
+ }
22
+
23
+ @ReactProp(name = "index")
24
+ fun setIndex(view: CellContainerImpl, index: Int) {
25
+ view.index = index
26
+ }
27
+ }
@@ -0,0 +1,19 @@
1
+ package com.shopify.reactnative.flash_list
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class ReactNativeFlashListPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf()
11
+ }
12
+
13
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
+ return listOf(
15
+ AutoLayoutViewManager(),
16
+ CellContainerManager()
17
+ )
18
+ }
19
+ }