@amazon-devices/react-navigation__react-native-tab-view 2.0.1758683737

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 (116) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +649 -0
  3. package/SystemBundles/c5889b273a6efc2a3c30241851055c37630fbf3381df86d403967369775f0536.bundle +14497 -0
  4. package/SystemBundles/c5889b273a6efc2a3c30241851055c37630fbf3381df86d403967369775f0536.map +1 -0
  5. package/SystemModules/amzn__react-navigation__react-native-tab-view-2.modules.txt +41 -0
  6. package/SystemModules/amzn__react-navigation__react-native-tab-view-2.private.modules.txt +98 -0
  7. package/SystemModules/keplerscript-system-bundle-config.json +7 -0
  8. package/THIRD_PARTY_NOTICES.txt +24 -0
  9. package/kepler-compatibility.json +8 -0
  10. package/kepler-transformed-package-info.json +3 -0
  11. package/lib/commonjs/Pager.android.js +14 -0
  12. package/lib/commonjs/Pager.android.js.map +1 -0
  13. package/lib/commonjs/Pager.ios.js +14 -0
  14. package/lib/commonjs/Pager.ios.js.map +1 -0
  15. package/lib/commonjs/Pager.js +14 -0
  16. package/lib/commonjs/Pager.js.map +1 -0
  17. package/lib/commonjs/PagerViewAdapter.js +132 -0
  18. package/lib/commonjs/PagerViewAdapter.js.map +1 -0
  19. package/lib/commonjs/PanResponderAdapter.js +204 -0
  20. package/lib/commonjs/PanResponderAdapter.js.map +1 -0
  21. package/lib/commonjs/PlatformPressable.js +43 -0
  22. package/lib/commonjs/PlatformPressable.js.map +1 -0
  23. package/lib/commonjs/SceneMap.js +29 -0
  24. package/lib/commonjs/SceneMap.js.map +1 -0
  25. package/lib/commonjs/SceneView.js +78 -0
  26. package/lib/commonjs/SceneView.js.map +1 -0
  27. package/lib/commonjs/TabBar.js +405 -0
  28. package/lib/commonjs/TabBar.js.map +1 -0
  29. package/lib/commonjs/TabBarIndicator.js +110 -0
  30. package/lib/commonjs/TabBarIndicator.js.map +1 -0
  31. package/lib/commonjs/TabBarItem.js +227 -0
  32. package/lib/commonjs/TabBarItem.js.map +1 -0
  33. package/lib/commonjs/TabView.js +122 -0
  34. package/lib/commonjs/TabView.js.map +1 -0
  35. package/lib/commonjs/index.js +42 -0
  36. package/lib/commonjs/index.js.map +1 -0
  37. package/lib/commonjs/types.js +6 -0
  38. package/lib/commonjs/types.js.map +1 -0
  39. package/lib/commonjs/useAnimatedValue.js +18 -0
  40. package/lib/commonjs/useAnimatedValue.js.map +1 -0
  41. package/lib/module/Pager.android.js +2 -0
  42. package/lib/module/Pager.android.js.map +1 -0
  43. package/lib/module/Pager.ios.js +2 -0
  44. package/lib/module/Pager.ios.js.map +1 -0
  45. package/lib/module/Pager.js +2 -0
  46. package/lib/module/Pager.js.map +1 -0
  47. package/lib/module/PagerViewAdapter.js +123 -0
  48. package/lib/module/PagerViewAdapter.js.map +1 -0
  49. package/lib/module/PanResponderAdapter.js +195 -0
  50. package/lib/module/PanResponderAdapter.js.map +1 -0
  51. package/lib/module/PlatformPressable.js +35 -0
  52. package/lib/module/PlatformPressable.js.map +1 -0
  53. package/lib/module/SceneMap.js +21 -0
  54. package/lib/module/SceneMap.js.map +1 -0
  55. package/lib/module/SceneView.js +70 -0
  56. package/lib/module/SceneView.js.map +1 -0
  57. package/lib/module/TabBar.js +396 -0
  58. package/lib/module/TabBar.js.map +1 -0
  59. package/lib/module/TabBarIndicator.js +101 -0
  60. package/lib/module/TabBarIndicator.js.map +1 -0
  61. package/lib/module/TabBarItem.js +218 -0
  62. package/lib/module/TabBarItem.js.map +1 -0
  63. package/lib/module/TabView.js +113 -0
  64. package/lib/module/TabView.js.map +1 -0
  65. package/lib/module/index.js +6 -0
  66. package/lib/module/index.js.map +1 -0
  67. package/lib/module/types.js +2 -0
  68. package/lib/module/types.js.map +1 -0
  69. package/lib/module/useAnimatedValue.js +10 -0
  70. package/lib/module/useAnimatedValue.js.map +1 -0
  71. package/lib/typescript/src/Pager.android.d.ts +2 -0
  72. package/lib/typescript/src/Pager.android.d.ts.map +1 -0
  73. package/lib/typescript/src/Pager.d.ts +2 -0
  74. package/lib/typescript/src/Pager.d.ts.map +1 -0
  75. package/lib/typescript/src/Pager.ios.d.ts +2 -0
  76. package/lib/typescript/src/Pager.ios.d.ts.map +1 -0
  77. package/lib/typescript/src/PagerViewAdapter.d.ts +15 -0
  78. package/lib/typescript/src/PagerViewAdapter.d.ts.map +1 -0
  79. package/lib/typescript/src/PanResponderAdapter.d.ts +16 -0
  80. package/lib/typescript/src/PanResponderAdapter.d.ts.map +1 -0
  81. package/lib/typescript/src/PlatformPressable.d.ts +16 -0
  82. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -0
  83. package/lib/typescript/src/SceneMap.d.ts +10 -0
  84. package/lib/typescript/src/SceneMap.d.ts.map +1 -0
  85. package/lib/typescript/src/SceneView.d.ts +16 -0
  86. package/lib/typescript/src/SceneView.d.ts.map +1 -0
  87. package/lib/typescript/src/TabBar.d.ts +44 -0
  88. package/lib/typescript/src/TabBar.d.ts.map +1 -0
  89. package/lib/typescript/src/TabBarIndicator.d.ts +13 -0
  90. package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -0
  91. package/lib/typescript/src/TabBarItem.d.ts +37 -0
  92. package/lib/typescript/src/TabBarItem.d.ts.map +1 -0
  93. package/lib/typescript/src/TabView.d.ts +27 -0
  94. package/lib/typescript/src/TabView.d.ts.map +1 -0
  95. package/lib/typescript/src/index.d.ts +11 -0
  96. package/lib/typescript/src/index.d.ts.map +1 -0
  97. package/lib/typescript/src/types.d.ts +42 -0
  98. package/lib/typescript/src/types.d.ts.map +1 -0
  99. package/lib/typescript/src/useAnimatedValue.d.ts +3 -0
  100. package/lib/typescript/src/useAnimatedValue.d.ts.map +1 -0
  101. package/package.json +1 -0
  102. package/src/Pager.android.tsx +1 -0
  103. package/src/Pager.ios.tsx +1 -0
  104. package/src/Pager.tsx +1 -0
  105. package/src/PagerViewAdapter.tsx +180 -0
  106. package/src/PanResponderAdapter.tsx +340 -0
  107. package/src/PlatformPressable.tsx +42 -0
  108. package/src/SceneMap.tsx +30 -0
  109. package/src/SceneView.tsx +107 -0
  110. package/src/TabBar.tsx +639 -0
  111. package/src/TabBarIndicator.tsx +153 -0
  112. package/src/TabBarItem.tsx +319 -0
  113. package/src/TabView.tsx +155 -0
  114. package/src/index.tsx +10 -0
  115. package/src/types.tsx +59 -0
  116. package/src/useAnimatedValue.tsx +12 -0
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ import { StyleProp, ViewStyle } from 'react-native';
3
+ import type { Layout, NavigationState, PagerProps, Route, SceneRendererProps } from './types';
4
+ export type Props<T extends Route> = PagerProps & {
5
+ onIndexChange: (index: number) => void;
6
+ navigationState: NavigationState<T>;
7
+ renderScene: (props: SceneRendererProps & {
8
+ route: T;
9
+ }) => React.ReactNode;
10
+ renderLazyPlaceholder?: (props: {
11
+ route: T;
12
+ }) => React.ReactNode;
13
+ renderTabBar?: (props: SceneRendererProps & {
14
+ navigationState: NavigationState<T>;
15
+ }) => React.ReactNode;
16
+ tabBarPosition?: 'top' | 'bottom';
17
+ initialLayout?: Partial<Layout>;
18
+ lazy?: ((props: {
19
+ route: T;
20
+ }) => boolean) | boolean;
21
+ lazyPreloadDistance?: number;
22
+ sceneContainerStyle?: StyleProp<ViewStyle>;
23
+ pagerStyle?: StyleProp<ViewStyle>;
24
+ style?: StyleProp<ViewStyle>;
25
+ };
26
+ export default function TabView<T extends Route>({ onIndexChange, navigationState, renderScene, initialLayout, keyboardDismissMode, lazy, lazyPreloadDistance, onSwipeStart, onSwipeEnd, renderLazyPlaceholder, renderTabBar, sceneContainerStyle, pagerStyle, style, swipeEnabled, tabBarPosition, animationEnabled, overScrollMode, }: Props<T>): JSX.Element;
27
+ //# sourceMappingURL=TabView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabView.d.ts","sourceRoot":"","sources":["../../../src/TabView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAEL,SAAS,EAGT,SAAS,EACV,MAAM,cAAc,CAAC;AAKtB,OAAO,KAAK,EACV,MAAM,EACN,eAAe,EACf,UAAU,EACV,KAAK,EACL,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,IAAI,UAAU,GAAG;IAChD,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IACpC,WAAW,EAAE,CAAC,KAAK,EAAE,kBAAkB,GAAG;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;IAC3E,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;IACjE,YAAY,CAAC,EAAE,CACb,KAAK,EAAE,kBAAkB,GAAG;QAAE,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC,CAAA;KAAE,KAChE,KAAK,CAAC,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,GAAG,OAAO,CAAC;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,CAAC,SAAS,KAAK,EAAE,EAC/C,aAAa,EACb,eAAe,EACf,WAAW,EACX,aAAa,EACb,mBAA4B,EAC5B,IAAY,EACZ,mBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,qBAAkC,EAClC,YAA+C,EAC/C,mBAAmB,EACnB,UAAU,EACV,KAAK,EACL,YAAmB,EACnB,cAAsB,EACtB,gBAAuB,EACvB,cAAc,GACf,EAAE,KAAK,CAAC,CAAC,CAAC,eA2FV"}
@@ -0,0 +1,11 @@
1
+ export { default as SceneMap } from './SceneMap';
2
+ export type { Props as TabBarProps } from './TabBar';
3
+ export { default as TabBar } from './TabBar';
4
+ export type { Props as TabBarIndicatorProps } from './TabBarIndicator';
5
+ export { default as TabBarIndicator } from './TabBarIndicator';
6
+ export type { Props as TabBarItemProps } from './TabBarItem';
7
+ export { default as TabBarItem } from './TabBarItem';
8
+ export type { Props as TabViewProps } from './TabView';
9
+ export { default as TabView } from './TabView';
10
+ export type { NavigationState, Route, SceneRendererProps } from './types';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,KAAK,IAAI,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,KAAK,IAAI,eAAe,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { Animated } from 'react-native';
2
+ import type { PagerViewProps } from 'react-native-pager-view';
3
+ export type Route = {
4
+ key: string;
5
+ icon?: string;
6
+ title?: string;
7
+ accessible?: boolean;
8
+ accessibilityLabel?: string;
9
+ testID?: string;
10
+ };
11
+ export type Event = {
12
+ defaultPrevented: boolean;
13
+ preventDefault(): void;
14
+ };
15
+ export type Scene<T extends Route> = {
16
+ route: T;
17
+ };
18
+ export type NavigationState<T extends Route> = {
19
+ index: number;
20
+ routes: T[];
21
+ };
22
+ export type Layout = {
23
+ width: number;
24
+ height: number;
25
+ };
26
+ export type Listener = (value: number) => void;
27
+ export type SceneRendererProps = {
28
+ layout: Layout;
29
+ position: Animated.AnimatedInterpolation<number>;
30
+ jumpTo: (key: string) => void;
31
+ };
32
+ export type EventEmitterProps = {
33
+ addEnterListener: (listener: Listener) => () => void;
34
+ };
35
+ export type PagerProps = Omit<PagerViewProps, 'initialPage' | 'scrollEnabled' | 'onPageScroll' | 'onPageSelected' | 'onPageScrollStateChanged' | 'keyboardDismissMode' | 'children'> & {
36
+ keyboardDismissMode?: 'none' | 'on-drag' | 'auto';
37
+ swipeEnabled?: boolean;
38
+ animationEnabled?: boolean;
39
+ onSwipeStart?: () => void;
40
+ onSwipeEnd?: () => void;
41
+ };
42
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,IAAI,IAAI,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,IAAI;IACnC,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,KAAK,IAAI;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,EAAE,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,IAAI,CAAC;CACtD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,IAAI,CAC3B,cAAc,EACZ,aAAa,GACb,eAAe,GACf,cAAc,GACd,gBAAgB,GAChB,0BAA0B,GAC1B,qBAAqB,GACrB,UAAU,CACb,GAAG;IACF,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Animated } from 'react-native';
2
+ export default function useAnimatedValue(initialValue: number): Animated.Value;
3
+ //# sourceMappingURL=useAnimatedValue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAnimatedValue.d.ts","sourceRoot":"","sources":["../../../src/useAnimatedValue.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,YAAY,EAAE,MAAM,kBAQ5D"}
package/package.json ADDED
@@ -0,0 +1 @@
1
+ {"name": "@amazon-devices/react-navigation__react-native-tab-view", "description": "Tab view component for React Native", "version": "2.0.1758683737+3.4.4", "keywords": ["react-native-component", "react-component", "react-native", "ios", "android", "tab", "swipe", "scrollable", "coverflow"], "license": "See license in LICENSE", "repository": {"type": "git", "url": "https://github.com/react-navigation/react-navigation.git", "directory": "packages/react-native-tab-view"}, "bugs": {"url": "https://github.com/react-navigation/react-navigation/issues"}, "homepage": "https://github.com/react-navigation/react-navigation/tree/main/packages/react-native-tab-view#readme", "main": "lib/commonjs/index.js", "react-native": "src/index.tsx", "source": "src/index.tsx", "module": "lib/module/index.js", "types": "lib/typescript/src/index.d.ts", "files": ["src", "lib", "kepler-compatibility.json", "!**/__tests__", "SystemModules", "SystemBundles", "THIRD_PARTY_NOTICES.txt", "kepler-transformed-package-info.json"], "sideEffects": false, "kepler": {"temporary-alias-for-package": {"name": "@react-navigation/react-native-tab-view", "version": "3.4.4"}}, "scripts": {"clean": "del lib"}, "dependencies": {"use-latest-callback": "0.1.6"}, "devDependencies": {"del-cli": "5.0.0", "react": "17.0.2", "react-native": "0.72.0", "@types/react": "17.0.2", "react-native-builder-bob": "0.20.4", "react-native-pager-view": "6.1.2", "typescript": "4.9.4"}, "peerDependencies": {"react": "*", "react-native": "*", "react-native-pager-view": "*"}, "react-native-builder-bob": {"source": "src", "output": "lib", "targets": ["commonjs", "module", ["typescript", {"project": "tsconfig.build.json"}]]}}
@@ -0,0 +1 @@
1
+ export { default } from './PagerViewAdapter';
@@ -0,0 +1 @@
1
+ export { default } from './PagerViewAdapter';
package/src/Pager.tsx ADDED
@@ -0,0 +1 @@
1
+ export { default } from './PanResponderAdapter';
@@ -0,0 +1,180 @@
1
+ import * as React from 'react';
2
+ import { Animated, Keyboard, StyleSheet } from 'react-native';
3
+ import ViewPager, {
4
+ PageScrollStateChangedNativeEvent,
5
+ } from 'react-native-pager-view';
6
+
7
+ import type {
8
+ EventEmitterProps,
9
+ Listener,
10
+ NavigationState,
11
+ PagerProps,
12
+ Route,
13
+ } from './types';
14
+ import useAnimatedValue from './useAnimatedValue';
15
+
16
+ const AnimatedViewPager = Animated.createAnimatedComponent(ViewPager);
17
+
18
+ type Props<T extends Route> = PagerProps & {
19
+ onIndexChange: (index: number) => void;
20
+ navigationState: NavigationState<T>;
21
+ children: (
22
+ props: EventEmitterProps & {
23
+ // Animated value which represents the state of current index
24
+ // It can include fractional digits as it represents the intermediate value
25
+ position: Animated.AnimatedInterpolation<number>;
26
+ // Function to actually render the content of the pager
27
+ // The parent component takes care of rendering
28
+ render: (children: React.ReactNode) => React.ReactNode;
29
+ // Callback to call when switching the tab
30
+ // The tab switch animation is performed even if the index in state is unchanged
31
+ jumpTo: (key: string) => void;
32
+ }
33
+ ) => React.ReactElement;
34
+ };
35
+
36
+ export default function PagerViewAdapter<T extends Route>({
37
+ keyboardDismissMode = 'auto',
38
+ swipeEnabled = true,
39
+ navigationState,
40
+ onIndexChange,
41
+ onSwipeStart,
42
+ onSwipeEnd,
43
+ children,
44
+ style,
45
+ animationEnabled,
46
+ ...rest
47
+ }: Props<T>) {
48
+ const { index } = navigationState;
49
+
50
+ const listenersRef = React.useRef<Listener[]>([]);
51
+
52
+ const pagerRef = React.useRef<ViewPager>(null);
53
+ const indexRef = React.useRef<number>(index);
54
+ const navigationStateRef = React.useRef(navigationState);
55
+
56
+ const position = useAnimatedValue(index);
57
+ const offset = useAnimatedValue(0);
58
+
59
+ React.useEffect(() => {
60
+ navigationStateRef.current = navigationState;
61
+ });
62
+
63
+ const jumpTo = React.useCallback(
64
+ (key: string) => {
65
+ const index = navigationStateRef.current.routes.findIndex(
66
+ (route: { key: string }) => route.key === key
67
+ );
68
+
69
+ if (animationEnabled) {
70
+ pagerRef.current?.setPage(index);
71
+ } else {
72
+ pagerRef.current?.setPageWithoutAnimation(index);
73
+ position.setValue(index);
74
+ }
75
+ },
76
+ [animationEnabled, position]
77
+ );
78
+
79
+ React.useEffect(() => {
80
+ if (keyboardDismissMode === 'auto') {
81
+ Keyboard.dismiss();
82
+ }
83
+
84
+ if (indexRef.current !== index) {
85
+ if (animationEnabled) {
86
+ pagerRef.current?.setPage(index);
87
+ } else {
88
+ pagerRef.current?.setPageWithoutAnimation(index);
89
+ position.setValue(index);
90
+ }
91
+ }
92
+ }, [keyboardDismissMode, index, animationEnabled, position]);
93
+
94
+ const onPageScrollStateChanged = (
95
+ state: PageScrollStateChangedNativeEvent
96
+ ) => {
97
+ const { pageScrollState } = state.nativeEvent;
98
+
99
+ switch (pageScrollState) {
100
+ case 'idle':
101
+ onSwipeEnd?.();
102
+ return;
103
+ case 'dragging': {
104
+ const subscription = offset.addListener(({ value }) => {
105
+ const next =
106
+ index + (value > 0 ? Math.ceil(value) : Math.floor(value));
107
+
108
+ if (next !== index) {
109
+ listenersRef.current.forEach((listener) => listener(next));
110
+ }
111
+
112
+ offset.removeListener(subscription);
113
+ });
114
+
115
+ onSwipeStart?.();
116
+ return;
117
+ }
118
+ }
119
+ };
120
+
121
+ const addEnterListener = React.useCallback((listener: Listener) => {
122
+ listenersRef.current.push(listener);
123
+
124
+ return () => {
125
+ const index = listenersRef.current.indexOf(listener);
126
+
127
+ if (index > -1) {
128
+ listenersRef.current.splice(index, 1);
129
+ }
130
+ };
131
+ }, []);
132
+
133
+ const memoizedPosition = React.useMemo(
134
+ () => Animated.add(position, offset),
135
+ [offset, position]
136
+ );
137
+
138
+ return children({
139
+ position: memoizedPosition,
140
+ addEnterListener,
141
+ jumpTo,
142
+ render: (children) => (
143
+ <AnimatedViewPager
144
+ {...rest}
145
+ ref={pagerRef}
146
+ style={[styles.container, style]}
147
+ initialPage={index}
148
+ keyboardDismissMode={
149
+ keyboardDismissMode === 'auto' ? 'on-drag' : keyboardDismissMode
150
+ }
151
+ onPageScroll={Animated.event(
152
+ [
153
+ {
154
+ nativeEvent: {
155
+ position: position,
156
+ offset: offset,
157
+ },
158
+ },
159
+ ],
160
+ { useNativeDriver: true }
161
+ )}
162
+ onPageSelected={(e) => {
163
+ const index = e.nativeEvent.position;
164
+ indexRef.current = index;
165
+ onIndexChange(index);
166
+ }}
167
+ onPageScrollStateChanged={onPageScrollStateChanged}
168
+ scrollEnabled={swipeEnabled}
169
+ >
170
+ {children}
171
+ </AnimatedViewPager>
172
+ ),
173
+ });
174
+ }
175
+
176
+ const styles = StyleSheet.create({
177
+ container: {
178
+ flex: 1,
179
+ },
180
+ });
@@ -0,0 +1,340 @@
1
+ import * as React from 'react';
2
+ import {
3
+ Animated,
4
+ GestureResponderEvent,
5
+ I18nManager,
6
+ Keyboard,
7
+ PanResponder,
8
+ PanResponderGestureState,
9
+ StyleSheet,
10
+ View,
11
+ } from 'react-native';
12
+
13
+ import type {
14
+ EventEmitterProps,
15
+ Layout,
16
+ Listener,
17
+ NavigationState,
18
+ PagerProps,
19
+ Route,
20
+ } from './types';
21
+ import useAnimatedValue from './useAnimatedValue';
22
+
23
+ type Props<T extends Route> = PagerProps & {
24
+ layout: Layout;
25
+ onIndexChange: (index: number) => void;
26
+ navigationState: NavigationState<T>;
27
+ children: (
28
+ props: EventEmitterProps & {
29
+ // Animated value which represents the state of current index
30
+ // It can include fractional digits as it represents the intermediate value
31
+ position: Animated.AnimatedInterpolation<number>;
32
+ // Function to actually render the content of the pager
33
+ // The parent component takes care of rendering
34
+ render: (children: React.ReactNode) => React.ReactNode;
35
+ // Callback to call when switching the tab
36
+ // The tab switch animation is performed even if the index in state is unchanged
37
+ jumpTo: (key: string) => void;
38
+ }
39
+ ) => React.ReactElement;
40
+ };
41
+
42
+ const DEAD_ZONE = 12;
43
+
44
+ const DefaultTransitionSpec = {
45
+ timing: Animated.spring,
46
+ stiffness: 1000,
47
+ damping: 500,
48
+ mass: 3,
49
+ overshootClamping: true,
50
+ };
51
+
52
+ export default function PanResponderAdapter<T extends Route>({
53
+ layout,
54
+ keyboardDismissMode = 'auto',
55
+ swipeEnabled = true,
56
+ navigationState,
57
+ onIndexChange,
58
+ onSwipeStart,
59
+ onSwipeEnd,
60
+ children,
61
+ style,
62
+ animationEnabled = false,
63
+ }: Props<T>) {
64
+ const { routes, index } = navigationState;
65
+
66
+ const panX = useAnimatedValue(0);
67
+
68
+ const listenersRef = React.useRef<Listener[]>([]);
69
+
70
+ const navigationStateRef = React.useRef(navigationState);
71
+ const layoutRef = React.useRef(layout);
72
+ const onIndexChangeRef = React.useRef(onIndexChange);
73
+
74
+ const currentIndexRef = React.useRef(index);
75
+ const pendingIndexRef = React.useRef<number>();
76
+
77
+ const swipeVelocityThreshold = 0.15;
78
+ const swipeDistanceThreshold = layout.width / 1.75;
79
+
80
+ const jumpToIndex = React.useCallback(
81
+ (index: number, animate = animationEnabled) => {
82
+ const offset = -index * layoutRef.current.width;
83
+
84
+ const { timing, ...transitionConfig } = DefaultTransitionSpec;
85
+
86
+ if (animate) {
87
+ Animated.parallel([
88
+ timing(panX, {
89
+ ...transitionConfig,
90
+ toValue: offset,
91
+ useNativeDriver: false,
92
+ }),
93
+ ]).start(({ finished }) => {
94
+ if (finished) {
95
+ onIndexChangeRef.current(index);
96
+ pendingIndexRef.current = undefined;
97
+ }
98
+ });
99
+ pendingIndexRef.current = index;
100
+ } else {
101
+ panX.setValue(offset);
102
+ onIndexChangeRef.current(index);
103
+ pendingIndexRef.current = undefined;
104
+ }
105
+ },
106
+ [animationEnabled, panX]
107
+ );
108
+
109
+ React.useEffect(() => {
110
+ navigationStateRef.current = navigationState;
111
+ layoutRef.current = layout;
112
+ onIndexChangeRef.current = onIndexChange;
113
+ });
114
+
115
+ React.useEffect(() => {
116
+ const offset = -navigationStateRef.current.index * layout.width;
117
+
118
+ panX.setValue(offset);
119
+ }, [layout.width, panX]);
120
+
121
+ React.useEffect(() => {
122
+ if (keyboardDismissMode === 'auto') {
123
+ Keyboard.dismiss();
124
+ }
125
+
126
+ if (layout.width && currentIndexRef.current !== index) {
127
+ currentIndexRef.current = index;
128
+ jumpToIndex(index);
129
+ }
130
+ }, [jumpToIndex, keyboardDismissMode, layout.width, index]);
131
+
132
+ const isMovingHorizontally = (
133
+ _: GestureResponderEvent,
134
+ gestureState: PanResponderGestureState
135
+ ) => {
136
+ return (
137
+ Math.abs(gestureState.dx) > Math.abs(gestureState.dy * 2) &&
138
+ Math.abs(gestureState.vx) > Math.abs(gestureState.vy * 2)
139
+ );
140
+ };
141
+
142
+ const canMoveScreen = (
143
+ event: GestureResponderEvent,
144
+ gestureState: PanResponderGestureState
145
+ ) => {
146
+ if (swipeEnabled === false) {
147
+ return false;
148
+ }
149
+
150
+ const diffX = I18nManager.isRTL ? -gestureState.dx : gestureState.dx;
151
+
152
+ return (
153
+ isMovingHorizontally(event, gestureState) &&
154
+ ((diffX >= DEAD_ZONE && currentIndexRef.current > 0) ||
155
+ (diffX <= -DEAD_ZONE && currentIndexRef.current < routes.length - 1))
156
+ );
157
+ };
158
+
159
+ const startGesture = () => {
160
+ onSwipeStart?.();
161
+
162
+ if (keyboardDismissMode === 'on-drag') {
163
+ Keyboard.dismiss();
164
+ }
165
+
166
+ panX.stopAnimation();
167
+ // @ts-expect-error: _value is private, but docs use it as well
168
+ panX.setOffset(panX._value);
169
+ };
170
+
171
+ const respondToGesture = (
172
+ _: GestureResponderEvent,
173
+ gestureState: PanResponderGestureState
174
+ ) => {
175
+ const diffX = I18nManager.isRTL ? -gestureState.dx : gestureState.dx;
176
+
177
+ if (
178
+ // swiping left
179
+ (diffX > 0 && index <= 0) ||
180
+ // swiping right
181
+ (diffX < 0 && index >= routes.length - 1)
182
+ ) {
183
+ return;
184
+ }
185
+
186
+ if (layout.width) {
187
+ // @ts-expect-error: _offset is private, but docs use it as well
188
+ const position = (panX._offset + diffX) / -layout.width;
189
+ const next =
190
+ position > index ? Math.ceil(position) : Math.floor(position);
191
+
192
+ if (next !== index) {
193
+ listenersRef.current.forEach((listener) => listener(next));
194
+ }
195
+ }
196
+
197
+ panX.setValue(diffX);
198
+ };
199
+
200
+ const finishGesture = (
201
+ _: GestureResponderEvent,
202
+ gestureState: PanResponderGestureState
203
+ ) => {
204
+ panX.flattenOffset();
205
+
206
+ onSwipeEnd?.();
207
+
208
+ const currentIndex =
209
+ typeof pendingIndexRef.current === 'number'
210
+ ? pendingIndexRef.current
211
+ : currentIndexRef.current;
212
+
213
+ let nextIndex = currentIndex;
214
+
215
+ if (
216
+ Math.abs(gestureState.dx) > Math.abs(gestureState.dy) &&
217
+ Math.abs(gestureState.vx) > Math.abs(gestureState.vy) &&
218
+ (Math.abs(gestureState.dx) > swipeDistanceThreshold ||
219
+ Math.abs(gestureState.vx) > swipeVelocityThreshold)
220
+ ) {
221
+ nextIndex = Math.round(
222
+ Math.min(
223
+ Math.max(
224
+ 0,
225
+ I18nManager.isRTL
226
+ ? currentIndex + gestureState.dx / Math.abs(gestureState.dx)
227
+ : currentIndex - gestureState.dx / Math.abs(gestureState.dx)
228
+ ),
229
+ routes.length - 1
230
+ )
231
+ );
232
+
233
+ currentIndexRef.current = nextIndex;
234
+ }
235
+
236
+ if (!isFinite(nextIndex)) {
237
+ nextIndex = currentIndex;
238
+ }
239
+
240
+ jumpToIndex(nextIndex, true);
241
+ };
242
+
243
+ // TODO: use the listeners
244
+ const addEnterListener = React.useCallback((listener: Listener) => {
245
+ listenersRef.current.push(listener);
246
+
247
+ return () => {
248
+ const index = listenersRef.current.indexOf(listener);
249
+
250
+ if (index > -1) {
251
+ listenersRef.current.splice(index, 1);
252
+ }
253
+ };
254
+ }, []);
255
+
256
+ const jumpTo = React.useCallback(
257
+ (key: string) => {
258
+ const index = navigationStateRef.current.routes.findIndex(
259
+ (route: { key: string }) => route.key === key
260
+ );
261
+
262
+ jumpToIndex(index);
263
+ },
264
+ [jumpToIndex]
265
+ );
266
+
267
+ const panResponder = PanResponder.create({
268
+ onMoveShouldSetPanResponder: canMoveScreen,
269
+ onMoveShouldSetPanResponderCapture: canMoveScreen,
270
+ onPanResponderGrant: startGesture,
271
+ onPanResponderMove: respondToGesture,
272
+ onPanResponderTerminate: finishGesture,
273
+ onPanResponderRelease: finishGesture,
274
+ onPanResponderTerminationRequest: () => true,
275
+ });
276
+
277
+ const maxTranslate = layout.width * (routes.length - 1);
278
+ const translateX = Animated.multiply(
279
+ panX.interpolate({
280
+ inputRange: [-maxTranslate, 0],
281
+ outputRange: [-maxTranslate, 0],
282
+ extrapolate: 'clamp',
283
+ }),
284
+ I18nManager.isRTL ? -1 : 1
285
+ );
286
+
287
+ const position = React.useMemo(
288
+ () => (layout.width ? Animated.divide(panX, -layout.width) : null),
289
+ [layout.width, panX]
290
+ );
291
+
292
+ return children({
293
+ position: position ?? new Animated.Value(index),
294
+ addEnterListener,
295
+ jumpTo,
296
+ render: (children) => (
297
+ <Animated.View
298
+ style={[
299
+ styles.sheet,
300
+ layout.width
301
+ ? {
302
+ width: routes.length * layout.width,
303
+ transform: [{ translateX }],
304
+ }
305
+ : null,
306
+ style,
307
+ ]}
308
+ {...panResponder.panHandlers}
309
+ >
310
+ {React.Children.map(children, (child, i) => {
311
+ const route = routes[i];
312
+ const focused = i === index;
313
+
314
+ return (
315
+ <View
316
+ key={route.key}
317
+ style={
318
+ layout.width
319
+ ? { width: layout.width }
320
+ : focused
321
+ ? StyleSheet.absoluteFill
322
+ : null
323
+ }
324
+ >
325
+ {focused || layout.width ? child : null}
326
+ </View>
327
+ );
328
+ })}
329
+ </Animated.View>
330
+ ),
331
+ });
332
+ }
333
+
334
+ const styles = StyleSheet.create({
335
+ sheet: {
336
+ flex: 1,
337
+ flexDirection: 'row',
338
+ alignItems: 'stretch',
339
+ },
340
+ });
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+ import { Platform, Pressable, PressableProps } from 'react-native';
3
+
4
+ export type Props = PressableProps & {
5
+ pressColor?: string;
6
+ pressOpacity?: number;
7
+ children: React.ReactNode;
8
+ };
9
+
10
+ const ANDROID_VERSION_LOLLIPOP = 21;
11
+ const ANDROID_SUPPORTS_RIPPLE =
12
+ Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP;
13
+
14
+ /**
15
+ * PlatformPressable provides an abstraction on top of TouchableNativeFeedback and
16
+ * TouchableOpacity to handle platform differences.
17
+ *
18
+ * On Android, you can pass the props of TouchableNativeFeedback.
19
+ * On other platforms, you can pass the props of TouchableOpacity.
20
+ */
21
+ export default function PlatformPressable({
22
+ android_ripple,
23
+ pressColor = 'rgba(0, 0, 0, .32)',
24
+ pressOpacity,
25
+ style,
26
+ ...rest
27
+ }: Props) {
28
+ return (
29
+ <Pressable
30
+ android_ripple={
31
+ ANDROID_SUPPORTS_RIPPLE
32
+ ? { color: pressColor, ...android_ripple }
33
+ : undefined
34
+ }
35
+ style={({ pressed }) => [
36
+ { opacity: pressed && !ANDROID_SUPPORTS_RIPPLE ? pressOpacity : 1 },
37
+ typeof style === 'function' ? style({ pressed }) : style,
38
+ ]}
39
+ {...rest}
40
+ />
41
+ );
42
+ }