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

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 iOS list own vertical gestures that begin on pager headers.
119
+ * Defaults to false and currently has no effect on Android or 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. It is currently rendered on iOS only. */
130
+ nativeTabBar?: CollapsiblePagerNativeTabBarConfig;
131
+ onNativeTabPress?: (event: CollapsiblePagerViewOnNativeTabPressEvent) => void;
132
+ /** Optional native secondary sticky header. It is currently rendered on iOS only. */
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,40 @@ export class CollapsiblePagerView extends React.PureComponent<
149
247
  return React.Children.toArray(this.props.children);
150
248
  }
151
249
 
250
+ private nativeTabBarEnabled() {
251
+ return Platform.OS === "ios" && !!this.props.nativeTabBar?.items.length;
252
+ }
253
+
254
+ private dispatchNativeTabPageCommand(position: number, animated: boolean) {
255
+ const commandId = ++this.nativeTabCommandId;
256
+ this.latestNativeTabTarget = position;
257
+ this.setState(
258
+ (state) => ({
259
+ transientRetainedPages: state.transientRetainedPages.includes(position)
260
+ ? state.transientRetainedPages
261
+ : [...state.transientRetainedPages, position],
262
+ }),
263
+ () => {
264
+ requestAnimationFrame(() => {
265
+ if (commandId !== this.nativeTabCommandId || !this.nativeRef) {
266
+ return;
267
+ }
268
+ if (animated) {
269
+ CollapsiblePagerViewNativeCommands.setPage(
270
+ this.nativeRef,
271
+ position
272
+ );
273
+ } else {
274
+ CollapsiblePagerViewNativeCommands.setPageWithoutAnimation(
275
+ this.nativeRef,
276
+ position
277
+ );
278
+ }
279
+ });
280
+ }
281
+ );
282
+ }
283
+
152
284
  private retentionDistance() {
153
285
  return Math.max(0, Math.floor(this.props.pageRetentionDistance ?? 1));
154
286
  }
@@ -161,7 +293,10 @@ export class CollapsiblePagerView extends React.PureComponent<
161
293
  );
162
294
  const mounted: number[] = [];
163
295
  for (let index = 0; index < pageCount; index += 1) {
164
- if (Math.abs(index - selected) <= distance) {
296
+ if (
297
+ Math.abs(index - selected) <= distance ||
298
+ this.state.transientRetainedPages.includes(index)
299
+ ) {
165
300
  mounted.push(index);
166
301
  }
167
302
  }
@@ -193,12 +328,41 @@ export class CollapsiblePagerView extends React.PureComponent<
193
328
  event.nativeEvent.position,
194
329
  this.pages().length
195
330
  );
196
- if (position !== this.state.selectedPage) {
197
- this.setState({ selectedPage: position }, this.notifyMountedPagesChanged);
331
+ if (
332
+ position !== this.state.selectedPage ||
333
+ this.state.transientRetainedPages.length > 0
334
+ ) {
335
+ const pendingTarget = this.latestNativeTabTarget;
336
+ const transitionComplete = pendingTarget === position;
337
+ if (transitionComplete) {
338
+ this.latestNativeTabTarget = null;
339
+ }
340
+ this.setState(
341
+ {
342
+ selectedPage: position,
343
+ transientRetainedPages:
344
+ !transitionComplete && pendingTarget !== null
345
+ ? [pendingTarget]
346
+ : [],
347
+ },
348
+ this.notifyMountedPagesChanged
349
+ );
198
350
  }
199
351
  this.props.onPageSelected?.(event);
200
352
  };
201
353
 
354
+ private onNativeTabPress = (
355
+ event: ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>
356
+ ) => {
357
+ this.props.onNativeTabPress?.(event);
358
+ };
359
+
360
+ private onNativeSubHeaderPress = (
361
+ event: ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>
362
+ ) => {
363
+ this.props.onNativeSubHeaderPress?.(event);
364
+ };
365
+
202
366
  render() {
203
367
  const {
204
368
  children: _children,
@@ -206,9 +370,14 @@ export class CollapsiblePagerView extends React.PureComponent<
206
370
  stickyHeader,
207
371
  headerHeight,
208
372
  stickyHeaderHeight,
373
+ nativeSmoothHeaderScrollEnabled,
209
374
  pageRetentionDistance: _pageRetentionDistance,
210
375
  onMountedPagesChanged: _onMountedPagesChanged,
211
376
  onPageSelected: _onPageSelected,
377
+ nativeTabBar,
378
+ onNativeTabPress: _onNativeTabPress,
379
+ nativeSubHeader,
380
+ onNativeSubHeaderPress: _onNativeSubHeaderPress,
212
381
  layoutDirection,
213
382
  initialPage: _initialPage,
214
383
  offscreenPageLimit,
@@ -223,6 +392,21 @@ export class CollapsiblePagerView extends React.PureComponent<
223
392
  ? "rtl"
224
393
  : "ltr"
225
394
  : layoutDirection;
395
+ const nativeTabBarEnabled =
396
+ Platform.OS === "ios" && !!nativeTabBar?.items.length;
397
+ const nativeTabBarStyle = nativeTabBar?.style;
398
+ const nativeSubHeaderEnabled =
399
+ Platform.OS === "ios" && !!nativeSubHeader?.items.length;
400
+ const nativeSubHeaderStyle = nativeSubHeader?.style;
401
+ const nativeSubHeaderConfig = nativeSubHeaderEnabled
402
+ ? JSON.stringify({
403
+ ...nativeSubHeader,
404
+ style: {
405
+ ...nativeSubHeaderStyle,
406
+ selectedBackgroundColor: undefined,
407
+ },
408
+ })
409
+ : undefined;
226
410
 
227
411
  return (
228
412
  <CollapsiblePagerViewNativeComponent
@@ -238,15 +422,76 @@ export class CollapsiblePagerView extends React.PureComponent<
238
422
  offscreenPageLimit={Math.max(1, offscreenPageLimit ?? 1)}
239
423
  headerHeight={Math.max(0, Math.round(headerHeight))}
240
424
  stickyHeaderHeight={Math.max(0, Math.round(stickyHeaderHeight))}
425
+ nativeSmoothHeaderScrollEnabled={nativeSmoothHeaderScrollEnabled}
241
426
  pageKeys={JSON.stringify(pageKeys)}
242
427
  retainedPages={JSON.stringify([...retained])}
243
428
  onPageSelected={this.onPageSelected}
429
+ nativeTabBarItems={
430
+ nativeTabBarEnabled ? JSON.stringify(nativeTabBar.items) : undefined
431
+ }
432
+ nativeTabBarHeight={
433
+ nativeTabBarEnabled ? nativeTabBarStyle?.height ?? 44 : undefined
434
+ }
435
+ nativeTabBarContentPaddingHorizontal={
436
+ nativeTabBarEnabled
437
+ ? nativeTabBarStyle?.contentPaddingHorizontal ?? 20
438
+ : undefined
439
+ }
440
+ nativeTabBarItemSpacing={
441
+ nativeTabBarEnabled ? nativeTabBarStyle?.itemSpacing ?? 8 : undefined
442
+ }
443
+ nativeTabBarFontSize={
444
+ nativeTabBarEnabled ? nativeTabBarStyle?.fontSize ?? 16 : undefined
445
+ }
446
+ nativeTabBarFontFamily={
447
+ nativeTabBarEnabled ? nativeTabBarStyle?.fontFamily : undefined
448
+ }
449
+ nativeTabBarBackgroundColor={
450
+ nativeTabBarEnabled ? nativeTabBarStyle?.backgroundColor : undefined
451
+ }
452
+ nativeTabBarActiveTextColor={
453
+ nativeTabBarEnabled ? nativeTabBarStyle?.activeTextColor : undefined
454
+ }
455
+ nativeTabBarInactiveTextColor={
456
+ nativeTabBarEnabled ? nativeTabBarStyle?.inactiveTextColor : undefined
457
+ }
458
+ nativeTabBarIndicatorColor={
459
+ nativeTabBarEnabled ? nativeTabBarStyle?.indicatorColor : undefined
460
+ }
461
+ nativeTabBarIndicatorHeight={
462
+ nativeTabBarEnabled
463
+ ? nativeTabBarStyle?.indicatorHeight ?? 2
464
+ : undefined
465
+ }
466
+ nativeTabBarIndicatorBottom={
467
+ nativeTabBarEnabled
468
+ ? nativeTabBarStyle?.indicatorBottom ?? 0
469
+ : undefined
470
+ }
471
+ nativeSubHeaderConfig={nativeSubHeaderConfig}
472
+ nativeSubHeaderSelectedBackgroundColor={
473
+ nativeSubHeaderEnabled
474
+ ? nativeSubHeaderStyle?.selectedBackgroundColor
475
+ : undefined
476
+ }
477
+ onNativeTabPress={
478
+ nativeTabBarEnabled ? this.onNativeTabPress : undefined
479
+ }
480
+ onNativeSubHeaderPress={
481
+ nativeSubHeaderEnabled ? this.onNativeSubHeaderPress : undefined
482
+ }
244
483
  >
245
484
  <View
246
485
  key="collapsible-header"
247
486
  collapsable={false}
248
487
  pointerEvents="box-none"
249
- style={[styles.slot, { bottom: undefined, height: Math.max(0, Math.round(headerHeight)) }]}
488
+ style={[
489
+ styles.slot,
490
+ {
491
+ bottom: undefined,
492
+ height: Math.max(0, Math.round(headerHeight)),
493
+ },
494
+ ]}
250
495
  >
251
496
  {header}
252
497
  </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>;