@onekeyfe/react-native-native-list 3.0.105

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 (181) hide show
  1. package/README.md +342 -0
  2. package/android/CMakeLists.txt +20 -0
  3. package/android/build.gradle +105 -0
  4. package/android/gradle.properties +4 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  7. package/android/src/main/java/com/onekey/nativelist/HybridNativeList.kt +125 -0
  8. package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +165 -0
  9. package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +147 -0
  10. package/android/src/main/java/com/onekey/nativelist/NativeListPackage.kt +25 -0
  11. package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +2939 -0
  12. package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +1794 -0
  13. package/common/fonts/Roobert-Bold.ttf +0 -0
  14. package/common/fonts/Roobert-Medium.ttf +0 -0
  15. package/common/fonts/Roobert-Regular.ttf +0 -0
  16. package/common/fonts/Roobert-SemiBold.ttf +0 -0
  17. package/docs/DESIGN.md +129 -0
  18. package/ios/HybridNativeList.swift +131 -0
  19. package/ios/NativeListCell.swift +2858 -0
  20. package/ios/NativeListDesignAssets.swift +110 -0
  21. package/ios/NativeListModels.swift +215 -0
  22. package/ios/RNCNativeListView.swift +1737 -0
  23. package/ios/Resources/NativeListIcons.xcassets/Contents.json +1 -0
  24. package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/Contents.json +1 -0
  25. package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/icon.svg +1 -0
  26. package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/Contents.json +1 -0
  27. package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/icon.svg +1 -0
  28. package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/Contents.json +1 -0
  29. package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/icon.svg +1 -0
  30. package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/Contents.json +1 -0
  31. package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/icon.svg +1 -0
  32. package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/Contents.json +1 -0
  33. package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/icon.svg +1 -0
  34. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/Contents.json +1 -0
  35. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/icon.svg +1 -0
  36. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/Contents.json +1 -0
  37. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/icon.svg +1 -0
  38. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/Contents.json +1 -0
  39. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/icon.svg +1 -0
  40. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/Contents.json +1 -0
  41. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/icon.svg +1 -0
  42. package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/Contents.json +1 -0
  43. package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/icon.svg +1 -0
  44. package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/Contents.json +1 -0
  45. package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/icon.svg +1 -0
  46. package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/Contents.json +1 -0
  47. package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/icon.svg +1 -0
  48. package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/Contents.json +1 -0
  49. package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/icon.svg +1 -0
  50. package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/Contents.json +1 -0
  51. package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/icon.svg +1 -0
  52. package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/Contents.json +1 -0
  53. package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/icon.svg +1 -0
  54. package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/Contents.json +1 -0
  55. package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/icon.svg +1 -0
  56. package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/Contents.json +1 -0
  57. package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/icon.svg +1 -0
  58. package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/Contents.json +1 -0
  59. package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/icon.svg +1 -0
  60. package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/Contents.json +1 -0
  61. package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/icon.svg +1 -0
  62. package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/Contents.json +1 -0
  63. package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/icon.svg +1 -0
  64. package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/Contents.json +1 -0
  65. package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/icon.svg +1 -0
  66. package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/Contents.json +1 -0
  67. package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/icon.svg +1 -0
  68. package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/Contents.json +1 -0
  69. package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/icon.svg +1 -0
  70. package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/Contents.json +1 -0
  71. package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/icon.svg +1 -0
  72. package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/Contents.json +1 -0
  73. package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/icon.svg +1 -0
  74. package/lib/module/NativeList.js +183 -0
  75. package/lib/module/NativeList.nitro.js +4 -0
  76. package/lib/module/NativeList.types.js +4 -0
  77. package/lib/module/NativeList.web.js +167 -0
  78. package/lib/module/index.js +6 -0
  79. package/lib/module/models.js +2 -0
  80. package/lib/module/package.json +1 -0
  81. package/lib/module/scrolling.js +121 -0
  82. package/lib/module/selection.js +85 -0
  83. package/lib/module/validation.js +562 -0
  84. package/lib/module/web/NativeListWebEngine.js +2238 -0
  85. package/lib/nitrogen/generated/.gitattributes +1 -0
  86. package/lib/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  87. package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +201 -0
  88. package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +84 -0
  89. package/lib/nitrogen/generated/android/c++/JNativeListScrollAlignment.hpp +64 -0
  90. package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +104 -0
  91. package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.hpp +54 -0
  92. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/Func_void_std__string.kt +78 -0
  93. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +177 -0
  94. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/NativeListScrollAlignment.kt +25 -0
  95. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/nativelistOnLoad.kt +35 -0
  96. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListManager.kt +96 -0
  97. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListStateUpdater.kt +23 -0
  98. package/lib/nitrogen/generated/android/nativelist+autolinking.cmake +83 -0
  99. package/lib/nitrogen/generated/android/nativelist+autolinking.gradle +27 -0
  100. package/lib/nitrogen/generated/android/nativelistOnLoad.cpp +58 -0
  101. package/lib/nitrogen/generated/android/nativelistOnLoad.hpp +34 -0
  102. package/lib/nitrogen/generated/ios/NativeListModule+autolinking.rb +62 -0
  103. package/lib/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.cpp +41 -0
  104. package/lib/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.hpp +91 -0
  105. package/lib/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Umbrella.hpp +49 -0
  106. package/lib/nitrogen/generated/ios/NativeListModuleAutolinking.mm +33 -0
  107. package/lib/nitrogen/generated/ios/NativeListModuleAutolinking.swift +26 -0
  108. package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.cpp +11 -0
  109. package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +180 -0
  110. package/lib/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +180 -0
  111. package/lib/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  112. package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +69 -0
  113. package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +447 -0
  114. package/lib/nitrogen/generated/ios/swift/NativeListScrollAlignment.swift +48 -0
  115. package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +43 -0
  116. package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +87 -0
  117. package/lib/nitrogen/generated/shared/c++/NativeListScrollAlignment.hpp +84 -0
  118. package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +46 -0
  119. package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +103 -0
  120. package/lib/nitrogen/generated/shared/json/NativeListConfig.json +16 -0
  121. package/lib/typescript/package.json +1 -0
  122. package/lib/typescript/src/NativeList.d.ts +18 -0
  123. package/lib/typescript/src/NativeList.nitro.d.ts +29 -0
  124. package/lib/typescript/src/NativeList.types.d.ts +87 -0
  125. package/lib/typescript/src/NativeList.web.d.ts +19 -0
  126. package/lib/typescript/src/index.d.ts +7 -0
  127. package/lib/typescript/src/models.d.ts +456 -0
  128. package/lib/typescript/src/scrolling.d.ts +32 -0
  129. package/lib/typescript/src/selection.d.ts +27 -0
  130. package/lib/typescript/src/validation.d.ts +7 -0
  131. package/lib/typescript/src/web/NativeListWebEngine.d.ts +203 -0
  132. package/nitro.json +23 -0
  133. package/nitrogen/generated/.gitattributes +1 -0
  134. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  135. package/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +201 -0
  136. package/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +84 -0
  137. package/nitrogen/generated/android/c++/JNativeListScrollAlignment.hpp +64 -0
  138. package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +104 -0
  139. package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.hpp +54 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/Func_void_std__string.kt +78 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +177 -0
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/NativeListScrollAlignment.kt +25 -0
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/nativelistOnLoad.kt +35 -0
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListManager.kt +96 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListStateUpdater.kt +23 -0
  146. package/nitrogen/generated/android/nativelist+autolinking.cmake +83 -0
  147. package/nitrogen/generated/android/nativelist+autolinking.gradle +27 -0
  148. package/nitrogen/generated/android/nativelistOnLoad.cpp +58 -0
  149. package/nitrogen/generated/android/nativelistOnLoad.hpp +34 -0
  150. package/nitrogen/generated/ios/NativeListModule+autolinking.rb +62 -0
  151. package/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.cpp +41 -0
  152. package/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.hpp +91 -0
  153. package/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Umbrella.hpp +49 -0
  154. package/nitrogen/generated/ios/NativeListModuleAutolinking.mm +33 -0
  155. package/nitrogen/generated/ios/NativeListModuleAutolinking.swift +26 -0
  156. package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.cpp +11 -0
  157. package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +180 -0
  158. package/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +180 -0
  159. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  160. package/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +69 -0
  161. package/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +447 -0
  162. package/nitrogen/generated/ios/swift/NativeListScrollAlignment.swift +48 -0
  163. package/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +43 -0
  164. package/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +87 -0
  165. package/nitrogen/generated/shared/c++/NativeListScrollAlignment.hpp +84 -0
  166. package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +46 -0
  167. package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +103 -0
  168. package/nitrogen/generated/shared/json/NativeListConfig.json +16 -0
  169. package/package.json +126 -0
  170. package/react-native-native-list.podspec +38 -0
  171. package/react-native.config.js +19 -0
  172. package/src/NativeList.nitro.ts +48 -0
  173. package/src/NativeList.tsx +298 -0
  174. package/src/NativeList.types.ts +125 -0
  175. package/src/NativeList.web.tsx +258 -0
  176. package/src/index.ts +35 -0
  177. package/src/models.ts +604 -0
  178. package/src/scrolling.ts +189 -0
  179. package/src/selection.ts +159 -0
  180. package/src/validation.ts +877 -0
  181. package/src/web/NativeListWebEngine.ts +3767 -0
@@ -0,0 +1,298 @@
1
+ import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react';
2
+ import { callback, getHostComponent } from 'react-native-nitro-modules';
3
+ import type {
4
+ NativeListMethods,
5
+ NativeListNativeProps,
6
+ } from './NativeList.nitro';
7
+ import type {
8
+ ActionAnchorInvalidatedEvent,
9
+ RowActionEvent,
10
+ SelectionDeltaEvent,
11
+ ReorderEvent,
12
+ EndReachedEvent,
13
+ VisibleRangeChangedEvent,
14
+ } from './models';
15
+ import type { NativeListProps, NativeListRef } from './NativeList.types';
16
+ import {
17
+ normalizeIndexScroll,
18
+ normalizeKeyScroll,
19
+ normalizePositionScroll,
20
+ resolveLocationIndex,
21
+ scrollFailure,
22
+ validateOffset,
23
+ type NormalizedPositionScroll,
24
+ } from './scrolling';
25
+ import { serializePatches, serializeSnapshot } from './validation';
26
+
27
+ export type { NativeListProps, NativeListRef } from './NativeList.types';
28
+ export type {
29
+ ActionAnchorState,
30
+ ScrollAlignment,
31
+ ScrollPositionOptions,
32
+ ScrollToEndParams,
33
+ ScrollToIndexFailedInfo,
34
+ ScrollToIndexParams,
35
+ ScrollToItemParams,
36
+ ScrollToKeyParams,
37
+ ScrollToLocationParams,
38
+ ScrollToOffsetParams,
39
+ } from './NativeList.types';
40
+
41
+ const NativeListConfig = require('../nitrogen/generated/shared/json/NativeListConfig.json');
42
+
43
+ const NativeListHost = getHostComponent<
44
+ NativeListNativeProps,
45
+ NativeListMethods
46
+ >('NativeList', () => NativeListConfig);
47
+
48
+ function parsePayload<T>(payloadJson: string): T {
49
+ return JSON.parse(payloadJson) as T;
50
+ }
51
+
52
+ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
53
+ function NativeList(
54
+ {
55
+ snapshot,
56
+ webVirtualizationEnabled: _webVirtualizationEnabled,
57
+ onRowAction,
58
+ onActionAnchorInvalidated,
59
+ onSelectionDelta,
60
+ onReorder,
61
+ onEndReached,
62
+ onVisibleRangeChanged,
63
+ onRefresh,
64
+ onScrollToIndexFailed,
65
+ initialScrollIndex,
66
+ initialScrollKey,
67
+ initialScrollViewPosition,
68
+ initialScrollViewOffset,
69
+ ...viewProps
70
+ },
71
+ forwardedRef
72
+ ) {
73
+ const nativeRef = useRef<NativeListMethods | null>(null);
74
+ const callbacksRef = useRef({
75
+ onRowAction,
76
+ onActionAnchorInvalidated,
77
+ onSelectionDelta,
78
+ onReorder,
79
+ onEndReached,
80
+ onVisibleRangeChanged,
81
+ onRefresh,
82
+ onScrollToIndexFailed,
83
+ });
84
+ callbacksRef.current = {
85
+ onRowAction,
86
+ onActionAnchorInvalidated,
87
+ onSelectionDelta,
88
+ onReorder,
89
+ onEndReached,
90
+ onVisibleRangeChanged,
91
+ onRefresh,
92
+ onScrollToIndexFailed,
93
+ };
94
+ const snapshotRef = useRef(snapshot);
95
+ snapshotRef.current = snapshot;
96
+ const initialScrollRef = useRef<
97
+ | Readonly<{
98
+ index?: number;
99
+ key?: string;
100
+ scroll: NormalizedPositionScroll;
101
+ }>
102
+ | undefined
103
+ >(
104
+ initialScrollIndex === undefined && initialScrollKey === undefined
105
+ ? undefined
106
+ : initialScrollIndex !== undefined
107
+ ? normalizeIndexScroll({
108
+ index: initialScrollIndex,
109
+ animated: false,
110
+ viewPosition: initialScrollViewPosition,
111
+ viewOffset: initialScrollViewOffset,
112
+ })
113
+ : {
114
+ key: initialScrollKey,
115
+ scroll: normalizePositionScroll(
116
+ {
117
+ animated: false,
118
+ viewPosition: initialScrollViewPosition,
119
+ viewOffset: initialScrollViewOffset,
120
+ },
121
+ 'start'
122
+ ),
123
+ }
124
+ );
125
+ const didApplyInitialScroll = useRef(false);
126
+ const snapshotJson = useMemo(() => serializeSnapshot(snapshot), [snapshot]);
127
+
128
+ const emitIndexFailure = (
129
+ index: number,
130
+ reason: Parameters<typeof scrollFailure>[2]
131
+ ) => {
132
+ callbacksRef.current.onScrollToIndexFailed?.(
133
+ scrollFailure(snapshotRef.current.rows, index, reason)
134
+ );
135
+ };
136
+
137
+ const dispatchIndexScroll = (
138
+ index: number,
139
+ scroll: NormalizedPositionScroll
140
+ ) => {
141
+ if (index >= snapshotRef.current.rows.length) {
142
+ emitIndexFailure(index, 'index-out-of-range');
143
+ return;
144
+ }
145
+ nativeRef.current?.scrollToIndex(
146
+ index,
147
+ scroll.animated,
148
+ scroll.alignment,
149
+ scroll.viewPosition,
150
+ scroll.viewOffset
151
+ );
152
+ };
153
+
154
+ const dispatchKeyScroll = (
155
+ key: string,
156
+ scroll: NormalizedPositionScroll
157
+ ) => {
158
+ nativeRef.current?.scrollToKey(
159
+ key,
160
+ scroll.animated,
161
+ scroll.alignment,
162
+ scroll.viewPosition,
163
+ scroll.viewOffset
164
+ );
165
+ };
166
+
167
+ const applyInitialScroll = () => {
168
+ if (didApplyInitialScroll.current || !nativeRef.current) return;
169
+ didApplyInitialScroll.current = true;
170
+ const initial = initialScrollRef.current;
171
+ if (!initial) return;
172
+ if (initial.index !== undefined)
173
+ dispatchIndexScroll(initial.index, initial.scroll);
174
+ else if (initial.key !== undefined)
175
+ dispatchKeyScroll(initial.key, initial.scroll);
176
+ };
177
+ const applyInitialScrollCallbackRef = useRef(applyInitialScroll);
178
+ applyInitialScrollCallbackRef.current = applyInitialScroll;
179
+
180
+ useImperativeHandle(forwardedRef, () => ({
181
+ applySnapshot(nextSnapshot) {
182
+ snapshotRef.current = nextSnapshot;
183
+ nativeRef.current?.applySnapshot(serializeSnapshot(nextSnapshot));
184
+ },
185
+ applyPatches(patches) {
186
+ if (patches.length > 0)
187
+ nativeRef.current?.applyPatches(serializePatches(patches));
188
+ },
189
+ reconcileSelection(selectedKeys) {
190
+ nativeRef.current?.reconcileSelection(JSON.stringify(selectedKeys));
191
+ },
192
+ scrollToKey(paramsOrKey, animated, alignment) {
193
+ const { key, scroll } = normalizeKeyScroll(
194
+ paramsOrKey,
195
+ animated,
196
+ alignment
197
+ );
198
+ dispatchKeyScroll(key, scroll);
199
+ },
200
+ scrollToIndex(paramsOrIndex, animated, alignment) {
201
+ const { index, scroll } = normalizeIndexScroll(
202
+ paramsOrIndex,
203
+ animated,
204
+ alignment
205
+ );
206
+ dispatchIndexScroll(index, scroll);
207
+ },
208
+ scrollToItem(params) {
209
+ const index = snapshotRef.current.rows.findIndex(
210
+ (row) => row.key === params.item.key
211
+ );
212
+ if (index < 0) {
213
+ emitIndexFailure(-1, 'item-not-found');
214
+ return;
215
+ }
216
+ dispatchIndexScroll(index, normalizePositionScroll(params, 'start'));
217
+ },
218
+ scrollToOffset({ offset, animated = true }) {
219
+ validateOffset(offset);
220
+ nativeRef.current?.scrollToOffset(offset, animated);
221
+ },
222
+ scrollToEnd({ animated = true } = {}) {
223
+ nativeRef.current?.scrollToEnd(animated);
224
+ },
225
+ scrollToLocation(params) {
226
+ const index = resolveLocationIndex(snapshotRef.current.rows, params);
227
+ if (index === undefined) {
228
+ const sectionCount = snapshotRef.current.rows.filter(
229
+ (row) => row.type === 'sectionHeader' && row.variant !== 'summary'
230
+ ).length;
231
+ emitIndexFailure(
232
+ params.itemIndex,
233
+ params.sectionIndex >= sectionCount
234
+ ? 'section-out-of-range'
235
+ : 'item-out-of-range'
236
+ );
237
+ return;
238
+ }
239
+ dispatchIndexScroll(index, normalizePositionScroll(params, 'start'));
240
+ },
241
+ setActionAnchorState(state) {
242
+ nativeRef.current?.setActionAnchorState(JSON.stringify(state));
243
+ },
244
+ setRefreshing(refreshing) {
245
+ nativeRef.current?.setRefreshing(refreshing);
246
+ },
247
+ }));
248
+
249
+ const nativeCallbacks = useMemo(
250
+ () => ({
251
+ hybridRef: callback((ref: NativeListMethods) => {
252
+ nativeRef.current = ref;
253
+ applyInitialScrollCallbackRef.current();
254
+ }),
255
+ onRowAction: callback((payloadJson: string) => {
256
+ const payload = parsePayload<RowActionEvent>(payloadJson);
257
+ if (payload.actionKey === 'nativeList.refresh')
258
+ callbacksRef.current.onRefresh?.();
259
+ callbacksRef.current.onRowAction?.(payload);
260
+ }),
261
+ onActionAnchorInvalidated: callback((payloadJson: string) => {
262
+ callbacksRef.current.onActionAnchorInvalidated?.(
263
+ parsePayload<ActionAnchorInvalidatedEvent>(payloadJson)
264
+ );
265
+ }),
266
+ onSelectionDelta: callback((payloadJson: string) => {
267
+ callbacksRef.current.onSelectionDelta?.(
268
+ parsePayload<SelectionDeltaEvent>(payloadJson)
269
+ );
270
+ }),
271
+ onReorder: callback((payloadJson: string) => {
272
+ callbacksRef.current.onReorder?.(
273
+ parsePayload<ReorderEvent>(payloadJson)
274
+ );
275
+ }),
276
+ onEndReached: callback((payloadJson: string) => {
277
+ callbacksRef.current.onEndReached?.(
278
+ parsePayload<EndReachedEvent>(payloadJson)
279
+ );
280
+ }),
281
+ onVisibleRangeChanged: callback((payloadJson: string) => {
282
+ callbacksRef.current.onVisibleRangeChanged?.(
283
+ parsePayload<VisibleRangeChangedEvent>(payloadJson)
284
+ );
285
+ }),
286
+ }),
287
+ []
288
+ );
289
+
290
+ return (
291
+ <NativeListHost
292
+ {...viewProps}
293
+ {...nativeCallbacks}
294
+ snapshotJson={snapshotJson}
295
+ />
296
+ );
297
+ }
298
+ );
@@ -0,0 +1,125 @@
1
+ import type { ViewProps } from 'react-native';
2
+ import type {
3
+ ActionAnchorInvalidatedEvent,
4
+ EndReachedEvent,
5
+ NativeListSnapshot,
6
+ ReorderEvent,
7
+ RowActionEvent,
8
+ RowModel,
9
+ RowPatch,
10
+ SelectionDeltaEvent,
11
+ VisibleRangeChangedEvent,
12
+ } from './models';
13
+
14
+ export type ActionAnchorState = Readonly<{
15
+ token: string;
16
+ open: boolean;
17
+ restoreFocus?: boolean;
18
+ }>;
19
+
20
+ export type ScrollAlignment = 'start' | 'center' | 'end' | 'nearest';
21
+
22
+ export type ScrollPositionOptions = Readonly<{
23
+ animated?: boolean;
24
+ alignment?: ScrollAlignment;
25
+ viewPosition?: number;
26
+ viewOffset?: number;
27
+ }>;
28
+
29
+ export type ScrollToIndexParams = ScrollPositionOptions &
30
+ Readonly<{ index: number }>;
31
+
32
+ export type ScrollToKeyParams = ScrollPositionOptions &
33
+ Readonly<{ key: string }>;
34
+
35
+ export type ScrollToItemParams = ScrollPositionOptions &
36
+ Readonly<{ item: RowModel }>;
37
+
38
+ export type ScrollToOffsetParams = Readonly<{
39
+ offset: number;
40
+ animated?: boolean;
41
+ }>;
42
+
43
+ export type ScrollToEndParams = Readonly<{
44
+ animated?: boolean;
45
+ }>;
46
+
47
+ export type ScrollToLocationParams = ScrollPositionOptions &
48
+ Readonly<{
49
+ sectionIndex: number;
50
+ itemIndex: number;
51
+ }>;
52
+
53
+ export type ScrollToIndexFailedInfo = Readonly<{
54
+ index: number;
55
+ itemCount: number;
56
+ highestMeasuredFrameIndex: number;
57
+ averageItemLength: number;
58
+ reason:
59
+ | 'index-out-of-range'
60
+ | 'section-out-of-range'
61
+ | 'item-out-of-range'
62
+ | 'item-not-found';
63
+ }>;
64
+
65
+ type InitialScrollProps =
66
+ | Readonly<{
67
+ initialScrollIndex?: never;
68
+ initialScrollKey?: never;
69
+ initialScrollViewPosition?: never;
70
+ initialScrollViewOffset?: never;
71
+ }>
72
+ | Readonly<{
73
+ initialScrollIndex: number;
74
+ initialScrollKey?: never;
75
+ initialScrollViewPosition?: number;
76
+ initialScrollViewOffset?: number;
77
+ }>
78
+ | Readonly<{
79
+ initialScrollIndex?: never;
80
+ initialScrollKey: string;
81
+ initialScrollViewPosition?: number;
82
+ initialScrollViewOffset?: number;
83
+ }>;
84
+
85
+ type ScrollToIndex = (
86
+ paramsOrIndex: ScrollToIndexParams | number,
87
+ animated?: boolean,
88
+ alignment?: ScrollAlignment
89
+ ) => void;
90
+
91
+ type ScrollToKey = (
92
+ paramsOrKey: ScrollToKeyParams | string,
93
+ animated?: boolean,
94
+ alignment?: ScrollAlignment
95
+ ) => void;
96
+
97
+ export type NativeListRef = Readonly<{
98
+ applySnapshot(snapshot: NativeListSnapshot): void;
99
+ applyPatches(patches: readonly RowPatch[]): void;
100
+ reconcileSelection(selectedKeys: readonly string[]): void;
101
+ scrollToKey: ScrollToKey;
102
+ scrollToIndex: ScrollToIndex;
103
+ scrollToItem(params: ScrollToItemParams): void;
104
+ scrollToOffset(params: ScrollToOffsetParams): void;
105
+ scrollToEnd(params?: ScrollToEndParams): void;
106
+ scrollToLocation(params: ScrollToLocationParams): void;
107
+ setActionAnchorState(state: ActionAnchorState): void;
108
+ setRefreshing(refreshing: boolean): void;
109
+ }>;
110
+
111
+ export type NativeListProps = Omit<ViewProps, 'children'> &
112
+ Readonly<{
113
+ snapshot: NativeListSnapshot;
114
+ /** Web only. Defaults to true and is ignored by the native host. */
115
+ webVirtualizationEnabled?: boolean;
116
+ onRowAction?: (event: RowActionEvent) => void;
117
+ onActionAnchorInvalidated?: (event: ActionAnchorInvalidatedEvent) => void;
118
+ onSelectionDelta?: (event: SelectionDeltaEvent) => void;
119
+ onReorder?: (event: ReorderEvent) => void;
120
+ onEndReached?: (event: EndReachedEvent) => void;
121
+ onVisibleRangeChanged?: (event: VisibleRangeChangedEvent) => void;
122
+ onRefresh?: () => void;
123
+ onScrollToIndexFailed?: (info: ScrollToIndexFailedInfo) => void;
124
+ }> &
125
+ InitialScrollProps;
@@ -0,0 +1,258 @@
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import { View } from 'react-native';
11
+ import type { NativeListProps, NativeListRef } from './NativeList.types';
12
+ import type { NativeListSnapshot } from './models';
13
+ import {
14
+ normalizeIndexScroll,
15
+ normalizeKeyScroll,
16
+ normalizePositionScroll,
17
+ resolveLocationIndex,
18
+ scrollFailure,
19
+ validateOffset,
20
+ type NormalizedPositionScroll,
21
+ } from './scrolling';
22
+ import { serializePatches, validateSnapshot } from './validation';
23
+ import {
24
+ NativeListWebEngine,
25
+ type NativeListWebCallbacks,
26
+ } from './web/NativeListWebEngine';
27
+
28
+ export type { NativeListProps, NativeListRef } from './NativeList.types';
29
+ export type {
30
+ ActionAnchorState,
31
+ ScrollAlignment,
32
+ ScrollPositionOptions,
33
+ ScrollToEndParams,
34
+ ScrollToIndexFailedInfo,
35
+ ScrollToIndexParams,
36
+ ScrollToItemParams,
37
+ ScrollToKeyParams,
38
+ ScrollToLocationParams,
39
+ ScrollToOffsetParams,
40
+ } from './NativeList.types';
41
+
42
+ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
43
+ function NativeList(
44
+ {
45
+ snapshot,
46
+ webVirtualizationEnabled = true,
47
+ onRowAction,
48
+ onActionAnchorInvalidated,
49
+ onSelectionDelta,
50
+ onReorder,
51
+ onEndReached,
52
+ onVisibleRangeChanged,
53
+ onRefresh,
54
+ onScrollToIndexFailed,
55
+ initialScrollIndex,
56
+ initialScrollKey,
57
+ initialScrollViewPosition,
58
+ initialScrollViewOffset,
59
+ ...viewProps
60
+ },
61
+ forwardedRef
62
+ ) {
63
+ const [host, setHost] = useState<HTMLElement | null>(null);
64
+ const engineRef = useRef<NativeListWebEngine | undefined>(undefined);
65
+ const webVirtualizationEnabledRef = useRef(webVirtualizationEnabled);
66
+ webVirtualizationEnabledRef.current = webVirtualizationEnabled;
67
+ const validatedSnapshot = useMemo(
68
+ () => validateSnapshot(snapshot),
69
+ [snapshot]
70
+ );
71
+ const snapshotRef = useRef(validatedSnapshot);
72
+ snapshotRef.current = validatedSnapshot;
73
+ const appliedSnapshotRef = useRef<NativeListSnapshot | undefined>(
74
+ undefined
75
+ );
76
+ const callbacksRef = useRef<NativeListWebCallbacks>({});
77
+ callbacksRef.current = {
78
+ onRowAction,
79
+ onActionAnchorInvalidated,
80
+ onSelectionDelta,
81
+ onReorder,
82
+ onEndReached,
83
+ onVisibleRangeChanged,
84
+ onRefresh,
85
+ onScrollToIndexFailed,
86
+ };
87
+ const initialScrollRef = useRef<
88
+ | Readonly<{
89
+ index?: number;
90
+ key?: string;
91
+ scroll: NormalizedPositionScroll;
92
+ }>
93
+ | undefined
94
+ >(
95
+ initialScrollIndex === undefined && initialScrollKey === undefined
96
+ ? undefined
97
+ : initialScrollIndex !== undefined
98
+ ? normalizeIndexScroll({
99
+ index: initialScrollIndex,
100
+ animated: false,
101
+ viewPosition: initialScrollViewPosition,
102
+ viewOffset: initialScrollViewOffset,
103
+ })
104
+ : {
105
+ key: initialScrollKey,
106
+ scroll: normalizePositionScroll(
107
+ {
108
+ animated: false,
109
+ viewPosition: initialScrollViewPosition,
110
+ viewOffset: initialScrollViewOffset,
111
+ },
112
+ 'start'
113
+ ),
114
+ }
115
+ );
116
+ const didApplyInitialScroll = useRef(false);
117
+
118
+ useEffect(() => {
119
+ if (!host) return;
120
+ const engine = new NativeListWebEngine(
121
+ host,
122
+ snapshotRef.current,
123
+ callbacksRef.current,
124
+ webVirtualizationEnabledRef.current
125
+ );
126
+ engineRef.current = engine;
127
+ appliedSnapshotRef.current = snapshotRef.current;
128
+ const initial = initialScrollRef.current;
129
+ if (initial && !didApplyInitialScroll.current) {
130
+ didApplyInitialScroll.current = true;
131
+ if (initial.index !== undefined)
132
+ engine.scrollToIndex(initial.index, initial.scroll);
133
+ else if (initial.key !== undefined)
134
+ engine.scrollToKey(initial.key, initial.scroll);
135
+ }
136
+ return () => {
137
+ engine.destroy();
138
+ if (engineRef.current === engine) engineRef.current = undefined;
139
+ };
140
+ }, [host]);
141
+
142
+ useEffect(() => {
143
+ engineRef.current?.setVirtualizationEnabled(webVirtualizationEnabled);
144
+ }, [webVirtualizationEnabled]);
145
+
146
+ useEffect(() => {
147
+ engineRef.current?.updateCallbacks(callbacksRef.current);
148
+ });
149
+
150
+ useEffect(() => {
151
+ const engine = engineRef.current;
152
+ if (!engine || appliedSnapshotRef.current === validatedSnapshot) return;
153
+ engine.applySnapshot(validatedSnapshot);
154
+ appliedSnapshotRef.current = validatedSnapshot;
155
+ }, [validatedSnapshot]);
156
+
157
+ const emitIndexFailure = (
158
+ index: number,
159
+ reason: Parameters<typeof scrollFailure>[2]
160
+ ) => {
161
+ callbacksRef.current.onScrollToIndexFailed?.(
162
+ scrollFailure(snapshotRef.current.rows, index, reason)
163
+ );
164
+ };
165
+
166
+ useImperativeHandle(forwardedRef, () => ({
167
+ applySnapshot(nextSnapshot) {
168
+ const next = validateSnapshot(nextSnapshot);
169
+ snapshotRef.current = next;
170
+ appliedSnapshotRef.current = next;
171
+ engineRef.current?.applySnapshot(next);
172
+ },
173
+ applyPatches(patches) {
174
+ if (patches.length > 0) {
175
+ serializePatches(patches);
176
+ engineRef.current?.applyPatches(patches);
177
+ }
178
+ },
179
+ reconcileSelection(selectedKeys) {
180
+ engineRef.current?.reconcileSelection(selectedKeys);
181
+ },
182
+ scrollToKey(paramsOrKey, animated, alignment) {
183
+ const { key, scroll } = normalizeKeyScroll(
184
+ paramsOrKey,
185
+ animated,
186
+ alignment
187
+ );
188
+ engineRef.current?.scrollToKey(key, scroll);
189
+ },
190
+ scrollToIndex(paramsOrIndex, animated, alignment) {
191
+ const { index, scroll } = normalizeIndexScroll(
192
+ paramsOrIndex,
193
+ animated,
194
+ alignment
195
+ );
196
+ if (index >= snapshotRef.current.rows.length) {
197
+ emitIndexFailure(index, 'index-out-of-range');
198
+ return;
199
+ }
200
+ engineRef.current?.scrollToIndex(index, scroll);
201
+ },
202
+ scrollToItem(params) {
203
+ const index = snapshotRef.current.rows.findIndex(
204
+ (row) => row.key === params.item.key
205
+ );
206
+ if (index < 0) {
207
+ emitIndexFailure(-1, 'item-not-found');
208
+ return;
209
+ }
210
+ engineRef.current?.scrollToIndex(
211
+ index,
212
+ normalizePositionScroll(params, 'start')
213
+ );
214
+ },
215
+ scrollToOffset({ offset, animated = true }) {
216
+ validateOffset(offset);
217
+ engineRef.current?.scrollToOffset(offset, animated);
218
+ },
219
+ scrollToEnd({ animated = true } = {}) {
220
+ engineRef.current?.scrollToEnd(animated);
221
+ },
222
+ scrollToLocation(params) {
223
+ const index = resolveLocationIndex(snapshotRef.current.rows, params);
224
+ if (index === undefined) {
225
+ const sectionCount = snapshotRef.current.rows.filter(
226
+ (row) => row.type === 'sectionHeader' && row.variant !== 'summary'
227
+ ).length;
228
+ emitIndexFailure(
229
+ params.itemIndex,
230
+ params.sectionIndex >= sectionCount
231
+ ? 'section-out-of-range'
232
+ : 'item-out-of-range'
233
+ );
234
+ return;
235
+ }
236
+ engineRef.current?.scrollToIndex(
237
+ index,
238
+ normalizePositionScroll(params, 'start')
239
+ );
240
+ },
241
+ setActionAnchorState(state) {
242
+ engineRef.current?.setActionAnchorState(state);
243
+ },
244
+ setRefreshing(refreshing) {
245
+ engineRef.current?.setRefreshing(refreshing);
246
+ },
247
+ }));
248
+
249
+ const setHostRef = useCallback(
250
+ (value: React.ComponentRef<typeof View> | null) => {
251
+ setHost(value as unknown as HTMLElement | null);
252
+ },
253
+ []
254
+ );
255
+
256
+ return <View {...viewProps} ref={setHostRef} />;
257
+ }
258
+ );
package/src/index.ts ADDED
@@ -0,0 +1,35 @@
1
+ export { NativeList } from './NativeList';
2
+ export type {
3
+ ActionAnchorState,
4
+ NativeListProps,
5
+ NativeListRef,
6
+ ScrollAlignment,
7
+ ScrollPositionOptions,
8
+ ScrollToEndParams,
9
+ ScrollToIndexFailedInfo,
10
+ ScrollToIndexParams,
11
+ ScrollToItemParams,
12
+ ScrollToKeyParams,
13
+ ScrollToLocationParams,
14
+ ScrollToOffsetParams,
15
+ } from './NativeList';
16
+ export type * from './models';
17
+ export {
18
+ applyRowPatches,
19
+ serializePatches,
20
+ serializeSnapshot,
21
+ validatePatches,
22
+ validateSnapshot,
23
+ } from './validation';
24
+ export {
25
+ checkboxStateForKeys,
26
+ checkboxStateForSection,
27
+ isSelectableRow,
28
+ reduceSelection,
29
+ selectionStateFromSnapshot,
30
+ } from './selection';
31
+ export type {
32
+ SelectionAction,
33
+ SelectionResult,
34
+ SelectionState,
35
+ } from './selection';