@onekeyfe/react-native-pager-view 3.0.114 → 3.0.115

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 (29) hide show
  1. package/README.md +40 -0
  2. package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +630 -0
  3. package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +222 -0
  4. package/android/src/main/java/com/reactnativepagerview/NestedScrollableHost.kt +3 -1
  5. package/android/src/main/java/com/reactnativepagerview/PagerViewViewPackage.kt +1 -1
  6. package/android/src/main/java/com/reactnativepagerview/event/CollapsibleStateChangedEvent.kt +39 -0
  7. package/ios/RNCCollapsiblePagerViewComponentView.h +9 -0
  8. package/ios/RNCCollapsiblePagerViewComponentView.mm +991 -0
  9. package/ios/RNCPagerViewComponentView.mm +4 -2
  10. package/lib/module/CollapsiblePagerView.js +167 -0
  11. package/lib/module/CollapsiblePagerView.web.js +478 -0
  12. package/lib/module/CollapsiblePagerViewNativeComponent.ts +87 -0
  13. package/lib/module/PagerView.web.js +177 -0
  14. package/lib/module/index.js +2 -1
  15. package/lib/module/index.web.js +6 -0
  16. package/lib/module/usePagerView.js +1 -1
  17. package/lib/typescript/src/CollapsiblePagerView.d.ts +58 -0
  18. package/lib/typescript/src/CollapsiblePagerView.web.d.ts +105 -0
  19. package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +51 -0
  20. package/lib/typescript/src/PagerView.web.d.ts +55 -0
  21. package/lib/typescript/src/index.d.ts +1 -0
  22. package/lib/typescript/src/index.web.d.ts +5 -0
  23. package/package.json +5 -2
  24. package/src/CollapsiblePagerView.tsx +293 -0
  25. package/src/CollapsiblePagerView.web.tsx +710 -0
  26. package/src/CollapsiblePagerViewNativeComponent.ts +87 -0
  27. package/src/PagerView.web.tsx +280 -0
  28. package/src/index.tsx +1 -0
  29. package/src/index.web.tsx +12 -0
@@ -0,0 +1,293 @@
1
+ import React from "react";
2
+ import { I18nManager, Platform, StyleSheet, View } from "react-native";
3
+ import type * as ReactNative from "react-native";
4
+
5
+ import CollapsiblePagerViewNativeComponent, {
6
+ Commands as CollapsiblePagerViewNativeCommands,
7
+ type NativeProps,
8
+ type OnCollapsibleStateChangedEventData,
9
+ } from "./CollapsiblePagerViewNativeComponent";
10
+ import type {
11
+ OnPageScrollEventData,
12
+ OnPageScrollStateChangedEventData,
13
+ OnPageSelectedEventData,
14
+ } from "./PagerViewNativeComponent";
15
+
16
+ export type CollapsiblePagerDiagnostics =
17
+ ReactNative.NativeSyntheticEvent<OnCollapsibleStateChangedEventData>;
18
+
19
+ export type CollapsiblePagerMountState = Readonly<{
20
+ position: number;
21
+ mountedPages: readonly number[];
22
+ pageKeys: readonly string[];
23
+ }>;
24
+
25
+ export interface CollapsiblePagerViewProps
26
+ extends Omit<
27
+ NativeProps,
28
+ | "children"
29
+ | "headerHeight"
30
+ | "stickyHeaderHeight"
31
+ | "pageKeys"
32
+ | "retainedPages"
33
+ | "layoutDirection"
34
+ > {
35
+ /** Collapsible content rendered above the sticky bar. */
36
+ header: React.ReactNode;
37
+ /** Bar which remains pinned after the header has collapsed. */
38
+ stickyHeader: React.ReactNode;
39
+ /** Measured header height. It may be updated after an onLayout measurement. */
40
+ headerHeight: number;
41
+ /** Measured sticky bar height. */
42
+ stickyHeaderHeight: number;
43
+ /**
44
+ * Number of pages retained on either side of the selected page. Page slots
45
+ * remain stable, while distant React/native list subtrees are really
46
+ * unmounted. Defaults to one adjacent page.
47
+ */
48
+ pageRetentionDistance?: number;
49
+ layoutDirection?: "ltr" | "rtl" | "locale";
50
+ children?: React.ReactNode;
51
+ onMountedPagesChanged?: (state: CollapsiblePagerMountState) => void;
52
+ }
53
+
54
+ type State = {
55
+ selectedPage: number;
56
+ };
57
+
58
+ const pageIndex = (value: number) => {
59
+ const index = Math.trunc(value);
60
+ return Number.isFinite(index) ? index : null;
61
+ };
62
+
63
+ const clampedPageIndex = (value: number, pageCount: number) =>
64
+ Math.min(
65
+ Math.max(0, pageIndex(value) ?? 0),
66
+ Math.max(0, pageCount - 1)
67
+ );
68
+
69
+ const pageKey = (child: React.ReactNode, index: number) => {
70
+ if (React.isValidElement(child) && child.key != null) {
71
+ return String(child.key);
72
+ }
73
+ return `page-${index}`;
74
+ };
75
+
76
+ /**
77
+ * Pager variant whose vertical header coordination is performed by the native
78
+ * scrolling containers. React participates only at settled page boundaries to
79
+ * retain the selected/adjacent heavy page subtrees.
80
+ */
81
+ export class CollapsiblePagerView extends React.PureComponent<
82
+ CollapsiblePagerViewProps,
83
+ State
84
+ > {
85
+ state: State = {
86
+ selectedPage: clampedPageIndex(
87
+ this.props.initialPage ?? 0,
88
+ React.Children.toArray(this.props.children).length
89
+ ),
90
+ };
91
+
92
+ private nativeRef: React.ElementRef<
93
+ typeof CollapsiblePagerViewNativeComponent
94
+ > | null = null;
95
+
96
+ private lastMountSignature: string | null = null;
97
+
98
+ componentDidMount() {
99
+ this.notifyMountedPagesChanged();
100
+ }
101
+
102
+ componentDidUpdate() {
103
+ const pageCount = this.pages().length;
104
+ const selectedPage = clampedPageIndex(this.state.selectedPage, pageCount);
105
+ if (selectedPage !== this.state.selectedPage) {
106
+ this.setState({ selectedPage });
107
+ return;
108
+ }
109
+ this.notifyMountedPagesChanged();
110
+ }
111
+
112
+ public setPage = (selectedPage: number) => {
113
+ const position = pageIndex(selectedPage);
114
+ if (
115
+ this.nativeRef &&
116
+ position !== null &&
117
+ position >= 0 &&
118
+ position < this.pages().length
119
+ ) {
120
+ CollapsiblePagerViewNativeCommands.setPage(this.nativeRef, position);
121
+ }
122
+ };
123
+
124
+ public setPageWithoutAnimation = (selectedPage: number) => {
125
+ const position = pageIndex(selectedPage);
126
+ if (
127
+ this.nativeRef &&
128
+ position !== null &&
129
+ position >= 0 &&
130
+ position < this.pages().length
131
+ ) {
132
+ CollapsiblePagerViewNativeCommands.setPageWithoutAnimation(
133
+ this.nativeRef,
134
+ position
135
+ );
136
+ }
137
+ };
138
+
139
+ public setScrollEnabled = (scrollEnabled: boolean) => {
140
+ if (this.nativeRef) {
141
+ CollapsiblePagerViewNativeCommands.setScrollEnabledImperatively(
142
+ this.nativeRef,
143
+ scrollEnabled
144
+ );
145
+ }
146
+ };
147
+
148
+ private pages() {
149
+ return React.Children.toArray(this.props.children);
150
+ }
151
+
152
+ private retentionDistance() {
153
+ return Math.max(0, Math.floor(this.props.pageRetentionDistance ?? 1));
154
+ }
155
+
156
+ private mountedPages(pageCount = this.pages().length) {
157
+ const distance = this.retentionDistance();
158
+ const selected = Math.min(
159
+ Math.max(0, this.state.selectedPage),
160
+ Math.max(0, pageCount - 1)
161
+ );
162
+ const mounted: number[] = [];
163
+ for (let index = 0; index < pageCount; index += 1) {
164
+ if (Math.abs(index - selected) <= distance) {
165
+ mounted.push(index);
166
+ }
167
+ }
168
+ return mounted;
169
+ }
170
+
171
+ private notifyMountedPagesChanged = () => {
172
+ if (!this.props.onMountedPagesChanged) {
173
+ return;
174
+ }
175
+ const pages = this.pages();
176
+ const state = {
177
+ position: this.state.selectedPage,
178
+ mountedPages: this.mountedPages(pages.length),
179
+ pageKeys: pages.map(pageKey),
180
+ };
181
+ const signature = JSON.stringify(state);
182
+ if (signature === this.lastMountSignature) {
183
+ return;
184
+ }
185
+ this.lastMountSignature = signature;
186
+ this.props.onMountedPagesChanged(state);
187
+ };
188
+
189
+ private onPageSelected = (
190
+ event: ReactNative.NativeSyntheticEvent<OnPageSelectedEventData>
191
+ ) => {
192
+ const position = clampedPageIndex(
193
+ event.nativeEvent.position,
194
+ this.pages().length
195
+ );
196
+ if (position !== this.state.selectedPage) {
197
+ this.setState({ selectedPage: position }, this.notifyMountedPagesChanged);
198
+ }
199
+ this.props.onPageSelected?.(event);
200
+ };
201
+
202
+ render() {
203
+ const {
204
+ children: _children,
205
+ header,
206
+ stickyHeader,
207
+ headerHeight,
208
+ stickyHeaderHeight,
209
+ pageRetentionDistance: _pageRetentionDistance,
210
+ onMountedPagesChanged: _onMountedPagesChanged,
211
+ onPageSelected: _onPageSelected,
212
+ layoutDirection,
213
+ offscreenPageLimit,
214
+ ...nativeProps
215
+ } = this.props;
216
+ const pages = this.pages();
217
+ const pageKeys = pages.map(pageKey);
218
+ const retained = new Set(this.mountedPages(pages.length));
219
+ const deducedLayoutDirection =
220
+ !layoutDirection || layoutDirection === "locale"
221
+ ? I18nManager.isRTL
222
+ ? "rtl"
223
+ : "ltr"
224
+ : layoutDirection;
225
+
226
+ return (
227
+ <CollapsiblePagerViewNativeComponent
228
+ {...nativeProps}
229
+ ref={(ref) => {
230
+ this.nativeRef = ref;
231
+ }}
232
+ layoutDirection={deducedLayoutDirection}
233
+ offscreenPageLimit={Math.max(1, offscreenPageLimit ?? 1)}
234
+ headerHeight={Math.max(0, Math.round(headerHeight))}
235
+ stickyHeaderHeight={Math.max(0, Math.round(stickyHeaderHeight))}
236
+ pageKeys={JSON.stringify(pageKeys)}
237
+ retainedPages={JSON.stringify([...retained])}
238
+ onPageSelected={this.onPageSelected}
239
+ >
240
+ <View
241
+ key="collapsible-header"
242
+ collapsable={false}
243
+ pointerEvents="box-none"
244
+ style={[styles.slot, { bottom: undefined, height: Math.max(0, Math.round(headerHeight)) }]}
245
+ >
246
+ {header}
247
+ </View>
248
+ <View
249
+ key="sticky-header"
250
+ collapsable={false}
251
+ pointerEvents="box-none"
252
+ style={[
253
+ styles.slot,
254
+ {
255
+ top: Math.max(0, Math.round(headerHeight)),
256
+ bottom: undefined,
257
+ height: Math.max(0, Math.round(stickyHeaderHeight)),
258
+ },
259
+ ]}
260
+ >
261
+ {stickyHeader}
262
+ </View>
263
+ {pages.map((page, index) => (
264
+ <View
265
+ key={pageKeys[index]}
266
+ collapsable={false}
267
+ style={[
268
+ styles.slot,
269
+ // Android translates the expanded pager while consuming header scroll.
270
+ // Keep Yoga's page bounds aligned with that native viewport.
271
+ Platform.OS === "android"
272
+ ? { bottom: -Math.max(0, Math.round(headerHeight)) }
273
+ : null,
274
+ ]}
275
+ >
276
+ {retained.has(index) ? page : null}
277
+ </View>
278
+ ))}
279
+ </CollapsiblePagerViewNativeComponent>
280
+ );
281
+ }
282
+ }
283
+
284
+ const styles = {
285
+ slot: StyleSheet.absoluteFill,
286
+ };
287
+
288
+ export type CollapsiblePagerViewOnPageScrollEvent =
289
+ ReactNative.NativeSyntheticEvent<OnPageScrollEventData>;
290
+ export type CollapsiblePagerViewOnPageSelectedEvent =
291
+ ReactNative.NativeSyntheticEvent<OnPageSelectedEventData>;
292
+ export type CollapsiblePagerViewOnPageScrollStateChangedEvent =
293
+ ReactNative.NativeSyntheticEvent<OnPageScrollStateChangedEventData>;