@onekeyfe/react-native-pager-view 3.0.118 → 3.0.120

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.
@@ -6,6 +6,7 @@ import CollapsiblePagerViewNativeComponent, {
6
6
  Commands as CollapsiblePagerViewNativeCommands,
7
7
  type NativeProps,
8
8
  type OnCollapsibleStateChangedEventData,
9
+ type OnNativeTabPressEventData,
9
10
  } from "./CollapsiblePagerViewNativeComponent";
10
11
  import type {
11
12
  OnPageScrollEventData,
@@ -22,6 +23,63 @@ export type CollapsiblePagerMountState = Readonly<{
22
23
  pageKeys: readonly string[];
23
24
  }>;
24
25
 
26
+ export type CollapsiblePagerNativeTabBarItem = Readonly<{
27
+ key: string;
28
+ title: string;
29
+ accessibilityLabel?: string;
30
+ testID?: string;
31
+ }>;
32
+
33
+ export type CollapsiblePagerNativeTabBarStyle = Readonly<{
34
+ height?: number;
35
+ contentPaddingHorizontal?: number;
36
+ itemSpacing?: number;
37
+ fontSize?: number;
38
+ fontFamily?: string;
39
+ backgroundColor?: ReactNative.ColorValue;
40
+ activeTextColor?: ReactNative.ColorValue;
41
+ inactiveTextColor?: ReactNative.ColorValue;
42
+ indicatorColor?: ReactNative.ColorValue;
43
+ indicatorHeight?: number;
44
+ indicatorBottom?: number;
45
+ }>;
46
+
47
+ export type CollapsiblePagerNativeTabBarConfig = Readonly<{
48
+ items: readonly CollapsiblePagerNativeTabBarItem[];
49
+ style?: CollapsiblePagerNativeTabBarStyle;
50
+ }>;
51
+
52
+ export type CollapsiblePagerNativeSubHeaderColumns = Readonly<{
53
+ leading: string;
54
+ middle: string;
55
+ trailing: string;
56
+ }>;
57
+
58
+ export type CollapsiblePagerNativeSubHeaderStyle = Readonly<{
59
+ height?: number;
60
+ tabsHeight?: number;
61
+ contentPaddingHorizontal?: number;
62
+ itemSpacing?: number;
63
+ fontSize?: number;
64
+ columnFontSize?: number;
65
+ trailingColumnWidth?: number;
66
+ columnGap?: number;
67
+ selectedBackgroundColor?: ReactNative.ColorValue;
68
+ }>;
69
+
70
+ export type CollapsiblePagerNativeSubHeaderConfig = Readonly<{
71
+ items: readonly CollapsiblePagerNativeTabBarItem[];
72
+ selectedKey: string;
73
+ columns: CollapsiblePagerNativeSubHeaderColumns;
74
+ style?: CollapsiblePagerNativeSubHeaderStyle;
75
+ }>;
76
+
77
+ export type CollapsiblePagerViewOnNativeTabPressEvent =
78
+ ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>;
79
+
80
+ export type CollapsiblePagerViewOnNativeSubHeaderPressEvent =
81
+ ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>;
82
+
25
83
  export interface CollapsiblePagerViewProps
26
84
  extends Omit<
27
85
  NativeProps,
@@ -30,6 +88,22 @@ export interface CollapsiblePagerViewProps
30
88
  | "stickyHeaderHeight"
31
89
  | "pageKeys"
32
90
  | "retainedPages"
91
+ | "nativeTabBarItems"
92
+ | "nativeTabBarHeight"
93
+ | "nativeTabBarContentPaddingHorizontal"
94
+ | "nativeTabBarItemSpacing"
95
+ | "nativeTabBarFontSize"
96
+ | "nativeTabBarFontFamily"
97
+ | "nativeTabBarBackgroundColor"
98
+ | "nativeTabBarActiveTextColor"
99
+ | "nativeTabBarInactiveTextColor"
100
+ | "nativeTabBarIndicatorColor"
101
+ | "nativeTabBarIndicatorHeight"
102
+ | "nativeTabBarIndicatorBottom"
103
+ | "nativeSubHeaderConfig"
104
+ | "nativeSubHeaderSelectedBackgroundColor"
105
+ | "onNativeTabPress"
106
+ | "onNativeSubHeaderPress"
33
107
  | "layoutDirection"
34
108
  > {
35
109
  /** Collapsible content rendered above the sticky bar. */
@@ -40,6 +114,11 @@ export interface CollapsiblePagerViewProps
40
114
  headerHeight: number;
41
115
  /** Measured sticky bar height. */
42
116
  stickyHeaderHeight: number;
117
+ /**
118
+ * Lets the active native list own vertical gestures that begin on pager headers.
119
+ * Defaults to false and has no effect on Web.
120
+ */
121
+ nativeSmoothHeaderScrollEnabled?: boolean;
43
122
  /**
44
123
  * Number of pages retained on either side of the selected page. Page slots
45
124
  * remain stable, while distant React/native list subtrees are really
@@ -47,12 +126,21 @@ export interface CollapsiblePagerViewProps
47
126
  */
48
127
  pageRetentionDistance?: number;
49
128
  layoutDirection?: "ltr" | "rtl" | "locale";
129
+ /** Optional native tab bar for iOS and Android. */
130
+ nativeTabBar?: CollapsiblePagerNativeTabBarConfig;
131
+ onNativeTabPress?: (event: CollapsiblePagerViewOnNativeTabPressEvent) => void;
132
+ /** Optional native secondary sticky header for iOS and Android. */
133
+ nativeSubHeader?: CollapsiblePagerNativeSubHeaderConfig;
134
+ onNativeSubHeaderPress?: (
135
+ event: CollapsiblePagerViewOnNativeSubHeaderPressEvent
136
+ ) => void;
50
137
  children?: React.ReactNode;
51
138
  onMountedPagesChanged?: (state: CollapsiblePagerMountState) => void;
52
139
  }
53
140
 
54
141
  type State = {
55
142
  selectedPage: number;
143
+ transientRetainedPages: readonly number[];
56
144
  };
57
145
 
58
146
  const pageIndex = (value: number) => {
@@ -61,10 +149,7 @@ const pageIndex = (value: number) => {
61
149
  };
62
150
 
63
151
  const clampedPageIndex = (value: number, pageCount: number) =>
64
- Math.min(
65
- Math.max(0, pageIndex(value) ?? 0),
66
- Math.max(0, pageCount - 1)
67
- );
152
+ Math.min(Math.max(0, pageIndex(value) ?? 0), Math.max(0, pageCount - 1));
68
153
 
69
154
  const pageKey = (child: React.ReactNode, index: number) => {
70
155
  if (React.isValidElement(child) && child.key != null) {
@@ -87,6 +172,7 @@ export class CollapsiblePagerView extends React.PureComponent<
87
172
  this.props.initialPage ?? 0,
88
173
  React.Children.toArray(this.props.children).length
89
174
  ),
175
+ transientRetainedPages: [],
90
176
  };
91
177
 
92
178
  private nativeRef: React.ElementRef<
@@ -95,6 +181,10 @@ export class CollapsiblePagerView extends React.PureComponent<
95
181
 
96
182
  private lastMountSignature: string | null = null;
97
183
 
184
+ private nativeTabCommandId = 0;
185
+
186
+ private latestNativeTabTarget: number | null = null;
187
+
98
188
  componentDidMount() {
99
189
  this.notifyMountedPagesChanged();
100
190
  }
@@ -117,7 +207,11 @@ export class CollapsiblePagerView extends React.PureComponent<
117
207
  position >= 0 &&
118
208
  position < this.pages().length
119
209
  ) {
120
- CollapsiblePagerViewNativeCommands.setPage(this.nativeRef, position);
210
+ if (this.nativeTabBarEnabled()) {
211
+ this.dispatchNativeTabPageCommand(position, true);
212
+ } else {
213
+ CollapsiblePagerViewNativeCommands.setPage(this.nativeRef, position);
214
+ }
121
215
  }
122
216
  };
123
217
 
@@ -129,10 +223,14 @@ export class CollapsiblePagerView extends React.PureComponent<
129
223
  position >= 0 &&
130
224
  position < this.pages().length
131
225
  ) {
132
- CollapsiblePagerViewNativeCommands.setPageWithoutAnimation(
133
- this.nativeRef,
134
- position
135
- );
226
+ if (this.nativeTabBarEnabled()) {
227
+ this.dispatchNativeTabPageCommand(position, false);
228
+ } else {
229
+ CollapsiblePagerViewNativeCommands.setPageWithoutAnimation(
230
+ this.nativeRef,
231
+ position
232
+ );
233
+ }
136
234
  }
137
235
  };
138
236
 
@@ -149,6 +247,43 @@ export class CollapsiblePagerView extends React.PureComponent<
149
247
  return React.Children.toArray(this.props.children);
150
248
  }
151
249
 
250
+ private nativeTabBarEnabled() {
251
+ return (
252
+ (Platform.OS === "ios" || Platform.OS === "android") &&
253
+ !!this.props.nativeTabBar?.items.length
254
+ );
255
+ }
256
+
257
+ private dispatchNativeTabPageCommand(position: number, animated: boolean) {
258
+ const commandId = ++this.nativeTabCommandId;
259
+ this.latestNativeTabTarget = position;
260
+ this.setState(
261
+ (state) => ({
262
+ transientRetainedPages: state.transientRetainedPages.includes(position)
263
+ ? state.transientRetainedPages
264
+ : [...state.transientRetainedPages, position],
265
+ }),
266
+ () => {
267
+ requestAnimationFrame(() => {
268
+ if (commandId !== this.nativeTabCommandId || !this.nativeRef) {
269
+ return;
270
+ }
271
+ if (animated) {
272
+ CollapsiblePagerViewNativeCommands.setPage(
273
+ this.nativeRef,
274
+ position
275
+ );
276
+ } else {
277
+ CollapsiblePagerViewNativeCommands.setPageWithoutAnimation(
278
+ this.nativeRef,
279
+ position
280
+ );
281
+ }
282
+ });
283
+ }
284
+ );
285
+ }
286
+
152
287
  private retentionDistance() {
153
288
  return Math.max(0, Math.floor(this.props.pageRetentionDistance ?? 1));
154
289
  }
@@ -161,7 +296,10 @@ export class CollapsiblePagerView extends React.PureComponent<
161
296
  );
162
297
  const mounted: number[] = [];
163
298
  for (let index = 0; index < pageCount; index += 1) {
164
- if (Math.abs(index - selected) <= distance) {
299
+ if (
300
+ Math.abs(index - selected) <= distance ||
301
+ this.state.transientRetainedPages.includes(index)
302
+ ) {
165
303
  mounted.push(index);
166
304
  }
167
305
  }
@@ -193,12 +331,41 @@ export class CollapsiblePagerView extends React.PureComponent<
193
331
  event.nativeEvent.position,
194
332
  this.pages().length
195
333
  );
196
- if (position !== this.state.selectedPage) {
197
- this.setState({ selectedPage: position }, this.notifyMountedPagesChanged);
334
+ if (
335
+ position !== this.state.selectedPage ||
336
+ this.state.transientRetainedPages.length > 0
337
+ ) {
338
+ const pendingTarget = this.latestNativeTabTarget;
339
+ const transitionComplete = pendingTarget === position;
340
+ if (transitionComplete) {
341
+ this.latestNativeTabTarget = null;
342
+ }
343
+ this.setState(
344
+ {
345
+ selectedPage: position,
346
+ transientRetainedPages:
347
+ !transitionComplete && pendingTarget !== null
348
+ ? [pendingTarget]
349
+ : [],
350
+ },
351
+ this.notifyMountedPagesChanged
352
+ );
198
353
  }
199
354
  this.props.onPageSelected?.(event);
200
355
  };
201
356
 
357
+ private onNativeTabPress = (
358
+ event: ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>
359
+ ) => {
360
+ this.props.onNativeTabPress?.(event);
361
+ };
362
+
363
+ private onNativeSubHeaderPress = (
364
+ event: ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>
365
+ ) => {
366
+ this.props.onNativeSubHeaderPress?.(event);
367
+ };
368
+
202
369
  render() {
203
370
  const {
204
371
  children: _children,
@@ -206,9 +373,14 @@ export class CollapsiblePagerView extends React.PureComponent<
206
373
  stickyHeader,
207
374
  headerHeight,
208
375
  stickyHeaderHeight,
376
+ nativeSmoothHeaderScrollEnabled,
209
377
  pageRetentionDistance: _pageRetentionDistance,
210
378
  onMountedPagesChanged: _onMountedPagesChanged,
211
379
  onPageSelected: _onPageSelected,
380
+ nativeTabBar,
381
+ onNativeTabPress: _onNativeTabPress,
382
+ nativeSubHeader,
383
+ onNativeSubHeaderPress: _onNativeSubHeaderPress,
212
384
  layoutDirection,
213
385
  initialPage: _initialPage,
214
386
  offscreenPageLimit,
@@ -223,6 +395,23 @@ export class CollapsiblePagerView extends React.PureComponent<
223
395
  ? "rtl"
224
396
  : "ltr"
225
397
  : layoutDirection;
398
+ const nativeTabBarEnabled =
399
+ (Platform.OS === "ios" || Platform.OS === "android") &&
400
+ !!nativeTabBar?.items.length;
401
+ const nativeTabBarStyle = nativeTabBar?.style;
402
+ const nativeSubHeaderEnabled =
403
+ (Platform.OS === "ios" || Platform.OS === "android") &&
404
+ !!nativeSubHeader?.items.length;
405
+ const nativeSubHeaderStyle = nativeSubHeader?.style;
406
+ const nativeSubHeaderConfig = nativeSubHeaderEnabled
407
+ ? JSON.stringify({
408
+ ...nativeSubHeader,
409
+ style: {
410
+ ...nativeSubHeaderStyle,
411
+ selectedBackgroundColor: undefined,
412
+ },
413
+ })
414
+ : undefined;
226
415
 
227
416
  return (
228
417
  <CollapsiblePagerViewNativeComponent
@@ -238,15 +427,76 @@ export class CollapsiblePagerView extends React.PureComponent<
238
427
  offscreenPageLimit={Math.max(1, offscreenPageLimit ?? 1)}
239
428
  headerHeight={Math.max(0, Math.round(headerHeight))}
240
429
  stickyHeaderHeight={Math.max(0, Math.round(stickyHeaderHeight))}
430
+ nativeSmoothHeaderScrollEnabled={nativeSmoothHeaderScrollEnabled}
241
431
  pageKeys={JSON.stringify(pageKeys)}
242
432
  retainedPages={JSON.stringify([...retained])}
243
433
  onPageSelected={this.onPageSelected}
434
+ nativeTabBarItems={
435
+ nativeTabBarEnabled ? JSON.stringify(nativeTabBar.items) : undefined
436
+ }
437
+ nativeTabBarHeight={
438
+ nativeTabBarEnabled ? nativeTabBarStyle?.height ?? 44 : undefined
439
+ }
440
+ nativeTabBarContentPaddingHorizontal={
441
+ nativeTabBarEnabled
442
+ ? nativeTabBarStyle?.contentPaddingHorizontal ?? 20
443
+ : undefined
444
+ }
445
+ nativeTabBarItemSpacing={
446
+ nativeTabBarEnabled ? nativeTabBarStyle?.itemSpacing ?? 8 : undefined
447
+ }
448
+ nativeTabBarFontSize={
449
+ nativeTabBarEnabled ? nativeTabBarStyle?.fontSize ?? 16 : undefined
450
+ }
451
+ nativeTabBarFontFamily={
452
+ nativeTabBarEnabled ? nativeTabBarStyle?.fontFamily : undefined
453
+ }
454
+ nativeTabBarBackgroundColor={
455
+ nativeTabBarEnabled ? nativeTabBarStyle?.backgroundColor : undefined
456
+ }
457
+ nativeTabBarActiveTextColor={
458
+ nativeTabBarEnabled ? nativeTabBarStyle?.activeTextColor : undefined
459
+ }
460
+ nativeTabBarInactiveTextColor={
461
+ nativeTabBarEnabled ? nativeTabBarStyle?.inactiveTextColor : undefined
462
+ }
463
+ nativeTabBarIndicatorColor={
464
+ nativeTabBarEnabled ? nativeTabBarStyle?.indicatorColor : undefined
465
+ }
466
+ nativeTabBarIndicatorHeight={
467
+ nativeTabBarEnabled
468
+ ? nativeTabBarStyle?.indicatorHeight ?? 2
469
+ : undefined
470
+ }
471
+ nativeTabBarIndicatorBottom={
472
+ nativeTabBarEnabled
473
+ ? nativeTabBarStyle?.indicatorBottom ?? 0
474
+ : undefined
475
+ }
476
+ nativeSubHeaderConfig={nativeSubHeaderConfig}
477
+ nativeSubHeaderSelectedBackgroundColor={
478
+ nativeSubHeaderEnabled
479
+ ? nativeSubHeaderStyle?.selectedBackgroundColor
480
+ : undefined
481
+ }
482
+ onNativeTabPress={
483
+ nativeTabBarEnabled ? this.onNativeTabPress : undefined
484
+ }
485
+ onNativeSubHeaderPress={
486
+ nativeSubHeaderEnabled ? this.onNativeSubHeaderPress : undefined
487
+ }
244
488
  >
245
489
  <View
246
490
  key="collapsible-header"
247
491
  collapsable={false}
248
492
  pointerEvents="box-none"
249
- style={[styles.slot, { bottom: undefined, height: Math.max(0, Math.round(headerHeight)) }]}
493
+ style={[
494
+ styles.slot,
495
+ {
496
+ bottom: undefined,
497
+ height: Math.max(0, Math.round(headerHeight)),
498
+ },
499
+ ]}
250
500
  >
251
501
  {header}
252
502
  </View>
@@ -2,6 +2,7 @@ import type * as React from "react";
2
2
  import {
3
3
  codegenNativeCommands,
4
4
  codegenNativeComponent,
5
+ type ColorValue,
5
6
  type HostComponent,
6
7
  type ViewProps,
7
8
  } from "react-native";
@@ -36,6 +37,11 @@ export type OnCollapsibleStateChangedEventData = Readonly<{
36
37
  reason: string;
37
38
  }>;
38
39
 
40
+ export type OnNativeTabPressEventData = Readonly<{
41
+ position: Int32;
42
+ key: string;
43
+ }>;
44
+
39
45
  /**
40
46
  * Internal native props. Consumers should use CollapsiblePagerViewProps from
41
47
  * CollapsiblePagerView instead of mounting this host component directly.
@@ -47,14 +53,31 @@ export interface NativeProps extends ViewProps {
47
53
  offscreenPageLimit?: Int32;
48
54
  // OneKey patch: Preserve nested pager gesture coordination for collapsible pages.
49
55
  nestedScrollEnabled?: WithDefault<boolean, false>;
56
+ nativeSmoothHeaderScrollEnabled?: WithDefault<boolean, false>;
50
57
  headerHeight?: Int32;
51
58
  stickyHeaderHeight?: Int32;
52
59
  pageKeys?: string;
53
60
  retainedPages?: string;
61
+ nativeTabBarItems?: string;
62
+ nativeTabBarHeight?: Double;
63
+ nativeTabBarContentPaddingHorizontal?: Double;
64
+ nativeTabBarItemSpacing?: Double;
65
+ nativeTabBarFontSize?: Double;
66
+ nativeTabBarFontFamily?: string;
67
+ nativeTabBarBackgroundColor?: ColorValue;
68
+ nativeTabBarActiveTextColor?: ColorValue;
69
+ nativeTabBarInactiveTextColor?: ColorValue;
70
+ nativeTabBarIndicatorColor?: ColorValue;
71
+ nativeTabBarIndicatorHeight?: Double;
72
+ nativeTabBarIndicatorBottom?: Double;
73
+ nativeSubHeaderConfig?: string;
74
+ nativeSubHeaderSelectedBackgroundColor?: ColorValue;
54
75
  onPageScroll?: DirectEventHandler<OnPageScrollEventData>;
55
76
  onPageSelected?: DirectEventHandler<OnPageSelectedEventData>;
56
77
  onPageScrollStateChanged?: DirectEventHandler<OnPageScrollStateChangedEventData>;
57
78
  onCollapsibleStateChanged?: DirectEventHandler<OnCollapsibleStateChangedEventData>;
79
+ onNativeTabPress?: DirectEventHandler<OnNativeTabPressEventData>;
80
+ onNativeSubHeaderPress?: DirectEventHandler<OnNativeTabPressEventData>;
58
81
  }
59
82
 
60
83
  type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;