react-native-tab-view 3.2.1 → 3.3.2

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 (66) hide show
  1. package/{LICENSE.md → LICENSE} +0 -0
  2. package/README.md +5 -36
  3. package/lib/commonjs/PagerViewAdapter.js +2 -1
  4. package/lib/commonjs/PagerViewAdapter.js.map +1 -1
  5. package/lib/commonjs/PanResponderAdapter.js +2 -1
  6. package/lib/commonjs/PanResponderAdapter.js.map +1 -1
  7. package/lib/commonjs/SceneMap.js +9 -12
  8. package/lib/commonjs/SceneMap.js.map +1 -1
  9. package/lib/commonjs/SceneView.js +54 -101
  10. package/lib/commonjs/SceneView.js.map +1 -1
  11. package/lib/commonjs/TabBar.js +358 -325
  12. package/lib/commonjs/TabBar.js.map +1 -1
  13. package/lib/commonjs/TabBarIndicator.js +81 -99
  14. package/lib/commonjs/TabBarIndicator.js.map +1 -1
  15. package/lib/commonjs/TabBarItem.js +184 -161
  16. package/lib/commonjs/TabBarItem.js.map +1 -1
  17. package/lib/commonjs/TabView.js +2 -2
  18. package/lib/commonjs/TabView.js.map +1 -1
  19. package/lib/commonjs/index.js +3 -3
  20. package/lib/commonjs/index.js.map +1 -1
  21. package/lib/module/PagerViewAdapter.js +2 -1
  22. package/lib/module/PagerViewAdapter.js.map +1 -1
  23. package/lib/module/PanResponderAdapter.js +3 -2
  24. package/lib/module/PanResponderAdapter.js.map +1 -1
  25. package/lib/module/SceneMap.js +9 -14
  26. package/lib/module/SceneMap.js.map +1 -1
  27. package/lib/module/SceneView.js +54 -99
  28. package/lib/module/SceneView.js.map +1 -1
  29. package/lib/module/TabBar.js +355 -324
  30. package/lib/module/TabBar.js.map +1 -1
  31. package/lib/module/TabBarIndicator.js +75 -93
  32. package/lib/module/TabBarIndicator.js.map +1 -1
  33. package/lib/module/TabBarItem.js +178 -154
  34. package/lib/module/TabBarItem.js.map +1 -1
  35. package/lib/module/TabView.js +2 -2
  36. package/lib/module/TabView.js.map +1 -1
  37. package/lib/module/index.js +2 -2
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/typescript/{Pager.android.d.ts → src/Pager.android.d.ts} +0 -0
  40. package/lib/typescript/{Pager.d.ts → src/Pager.d.ts} +0 -0
  41. package/lib/typescript/{Pager.ios.d.ts → src/Pager.ios.d.ts} +0 -0
  42. package/lib/typescript/{PagerViewAdapter.d.ts → src/PagerViewAdapter.d.ts} +1 -1
  43. package/lib/typescript/{PanResponderAdapter.d.ts → src/PanResponderAdapter.d.ts} +1 -1
  44. package/lib/typescript/{PlatformPressable.d.ts → src/PlatformPressable.d.ts} +0 -0
  45. package/lib/typescript/{SceneMap.d.ts → src/SceneMap.d.ts} +5 -3
  46. package/lib/typescript/src/SceneView.d.ts +15 -0
  47. package/lib/typescript/src/TabBar.d.ts +42 -0
  48. package/lib/typescript/src/TabBarIndicator.d.ts +12 -0
  49. package/lib/typescript/{TabBarItem.d.ts → src/TabBarItem.d.ts} +5 -7
  50. package/lib/typescript/{TabView.d.ts → src/TabView.d.ts} +1 -1
  51. package/lib/typescript/{index.d.ts → src/index.d.ts} +7 -7
  52. package/lib/typescript/{types.d.ts → src/types.d.ts} +0 -0
  53. package/lib/typescript/{useAnimatedValue.d.ts → src/useAnimatedValue.d.ts} +0 -0
  54. package/package.json +28 -58
  55. package/src/PagerViewAdapter.tsx +11 -5
  56. package/src/PanResponderAdapter.tsx +16 -12
  57. package/src/SceneMap.tsx +12 -7
  58. package/src/SceneView.tsx +73 -108
  59. package/src/TabBar.tsx +506 -401
  60. package/src/TabBarIndicator.tsx +114 -117
  61. package/src/TabBarItem.tsx +230 -200
  62. package/src/TabView.tsx +6 -5
  63. package/src/index.tsx +7 -12
  64. package/lib/typescript/SceneView.d.ts +0 -32
  65. package/lib/typescript/TabBar.d.ts +0 -72
  66. package/lib/typescript/TabBarIndicator.d.ts +0 -20
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import { Animated, StyleProp, LayoutChangeEvent, TextStyle, ViewStyle } from 'react-native';
3
- import type { Scene, Route, NavigationState } from './types';
2
+ import { Animated, LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from 'react-native';
3
+ import type { NavigationState, Route, Scene } from './types';
4
4
  export declare type Props<T extends Route> = {
5
5
  position: Animated.AnimatedInterpolation;
6
6
  route: T;
@@ -27,11 +27,9 @@ export declare type Props<T extends Route> = {
27
27
  onLayout?: (event: LayoutChangeEvent) => void;
28
28
  onPress: () => void;
29
29
  onLongPress: () => void;
30
+ defaultTabWidth?: number;
30
31
  labelStyle?: StyleProp<TextStyle>;
31
32
  style: StyleProp<ViewStyle>;
32
33
  };
33
- export default class TabBarItem<T extends Route> extends React.Component<Props<T>> {
34
- private getActiveOpacity;
35
- private getInactiveOpacity;
36
- render(): JSX.Element;
37
- }
34
+ declare function TabBarItem<T extends Route>(props: Props<T>): JSX.Element;
35
+ export default TabBarItem;
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { StyleProp, ViewStyle } from 'react-native';
3
- import type { Layout, NavigationState, Route, SceneRendererProps, PagerProps } from './types';
3
+ import type { Layout, NavigationState, PagerProps, Route, SceneRendererProps } from './types';
4
4
  export declare type Props<T extends Route> = PagerProps & {
5
5
  onIndexChange: (index: number) => void;
6
6
  navigationState: NavigationState<T>;
@@ -1,10 +1,10 @@
1
- export { default as TabBar } from './TabBar';
1
+ export { default as SceneMap } from './SceneMap';
2
2
  export type { Props as TabBarProps } from './TabBar';
3
- export { default as TabView } from './TabView';
4
- export type { Props as TabViewProps } from './TabView';
5
- export { default as TabBarIndicator } from './TabBarIndicator';
3
+ export { default as TabBar } from './TabBar';
6
4
  export type { Props as TabBarIndicatorProps } from './TabBarIndicator';
7
- export { default as TabBarItem } from './TabBarItem';
5
+ export { default as TabBarIndicator } from './TabBarIndicator';
8
6
  export type { Props as TabBarItemProps } from './TabBarItem';
9
- export { default as SceneMap } from './SceneMap';
10
- export type { Route, NavigationState, SceneRendererProps } from './types';
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';
File without changes
package/package.json CHANGED
@@ -1,27 +1,7 @@
1
1
  {
2
2
  "name": "react-native-tab-view",
3
- "version": "3.2.1",
4
3
  "description": "Tab view component for React Native",
5
- "main": "lib/commonjs/index.js",
6
- "react-native": "src/index.tsx",
7
- "module": "lib/module/index.js",
8
- "types": "lib/typescript/index.d.ts",
9
- "files": [
10
- "src",
11
- "lib"
12
- ],
13
- "scripts": {
14
- "test": "jest",
15
- "typescript": "tsc --noEmit",
16
- "lint": "eslint --ext .js,.ts,.tsx .",
17
- "release": "release-it",
18
- "example": "yarn --cwd example",
19
- "bootstrap": "yarn example && yarn",
20
- "prepare": "bob build"
21
- },
22
- "publishConfig": {
23
- "registry": "https://registry.npmjs.org/"
24
- },
4
+ "version": "3.3.2",
25
5
  "keywords": [
26
6
  "react-native-component",
27
7
  "react-component",
@@ -33,57 +13,46 @@
33
13
  "scrollable",
34
14
  "coverflow"
35
15
  ],
16
+ "license": "MIT",
36
17
  "repository": {
37
18
  "type": "git",
38
- "url": "git+https://github.com/satya164/react-native-tab-view.git"
19
+ "url": "https://github.com/react-navigation/react-navigation.git",
20
+ "directory": "packages/react-native-tab-view"
39
21
  },
40
- "author": "Satyajit Sahoo <satyajit.happy@gmail.com> (https://github.com/satya164/)",
41
- "license": "MIT",
42
22
  "bugs": {
43
- "url": "https://github.com/satya164/react-native-tab-view/issues"
23
+ "url": "https://github.com/react-navigation/react-navigation/issues"
24
+ },
25
+ "homepage": "https://github.com/react-navigation/react-navigation/tree/main/packages/react-native-tab-view#readme",
26
+ "main": "lib/commonjs/index.js",
27
+ "react-native": "src/index.tsx",
28
+ "source": "src/index.tsx",
29
+ "module": "lib/module/index.js",
30
+ "types": "lib/typescript/src/index.d.ts",
31
+ "files": [
32
+ "src",
33
+ "lib",
34
+ "!**/__tests__"
35
+ ],
36
+ "sideEffects": false,
37
+ "scripts": {
38
+ "prepack": "bob build",
39
+ "clean": "del lib"
40
+ },
41
+ "dependencies": {
42
+ "use-latest-callback": "^0.1.5"
44
43
  },
45
- "homepage": "https://github.com/satya164/react-native-tab-view#readme",
46
44
  "devDependencies": {
47
- "@commitlint/config-conventional": "^17.1.0",
48
- "@expo/vector-icons": "^13.0.0",
49
- "@release-it/conventional-changelog": "^5.1.0",
50
- "@types/react": "^18.0.14",
51
- "@types/react-native": "0.69.5",
52
- "babel-jest": "^29.0.1",
53
- "babel-loader": "^8.2.5",
54
- "babel-preset-react-native": "^4.0.0",
55
- "commitlint": "^17.1.2",
56
- "eslint": "^8.23.0",
57
- "eslint-config-satya164": "^3.1.11",
58
- "eslint-plugin-react-native-globals": "^0.1.2",
59
- "husky": "^4.2.5",
60
- "jest": "^29.0.1",
61
- "prettier": "^2.7.1",
62
45
  "react": "18.0.0",
63
46
  "react-native": "0.69.5",
64
- "react-native-builder-bob": "^0.18.3",
47
+ "react-native-builder-bob": "^0.18.1",
65
48
  "react-native-pager-view": "5.4.24",
66
- "release-it": "^15.4.0",
67
- "typescript": "^4.8.2"
49
+ "typescript": "^4.7.4"
68
50
  },
69
51
  "peerDependencies": {
70
52
  "react": "*",
71
53
  "react-native": "*",
72
54
  "react-native-pager-view": "*"
73
55
  },
74
- "husky": {
75
- "hooks": {
76
- "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
77
- "pre-commit": "yarn lint && yarn typescript"
78
- }
79
- },
80
- "jest": {
81
- "preset": "react-native",
82
- "modulePathIgnorePatterns": [
83
- "<rootDir>/example/node_modules",
84
- "<rootDir>/lib/"
85
- ]
86
- },
87
56
  "react-native-builder-bob": {
88
57
  "source": "src",
89
58
  "output": "lib",
@@ -97,5 +66,6 @@
97
66
  }
98
67
  ]
99
68
  ]
100
- }
69
+ },
70
+ "gitHead": "4e29403ec63d9304561e296667f6cf15806c316b"
101
71
  }
@@ -3,14 +3,15 @@ import { Animated, Keyboard, StyleSheet } from 'react-native';
3
3
  import ViewPager, {
4
4
  PageScrollStateChangedNativeEvent,
5
5
  } from 'react-native-pager-view';
6
- import useAnimatedValue from './useAnimatedValue';
6
+
7
7
  import type {
8
- NavigationState,
9
- Route,
10
- Listener,
11
8
  EventEmitterProps,
9
+ Listener,
10
+ NavigationState,
12
11
  PagerProps,
12
+ Route,
13
13
  } from './types';
14
+ import useAnimatedValue from './useAnimatedValue';
14
15
 
15
16
  const AnimatedViewPager = Animated.createAnimatedComponent(ViewPager);
16
17
 
@@ -129,8 +130,13 @@ export default function PagerViewAdapter<T extends Route>({
129
130
  };
130
131
  }, []);
131
132
 
133
+ const memoizedPosition = React.useMemo(
134
+ () => Animated.add(position, offset),
135
+ [offset, position]
136
+ );
137
+
132
138
  return children({
133
- position: Animated.add(position, offset),
139
+ position: memoizedPosition,
134
140
  addEnterListener,
135
141
  jumpTo,
136
142
  render: (children) => (
@@ -1,23 +1,24 @@
1
1
  import * as React from 'react';
2
2
  import {
3
3
  Animated,
4
- PanResponder,
5
- Keyboard,
6
- StyleSheet,
7
4
  GestureResponderEvent,
8
- PanResponderGestureState,
9
5
  I18nManager,
6
+ Keyboard,
7
+ PanResponder,
8
+ PanResponderGestureState,
9
+ StyleSheet,
10
10
  View,
11
11
  } from 'react-native';
12
- import useAnimatedValue from './useAnimatedValue';
12
+
13
13
  import type {
14
- NavigationState,
15
- Route,
16
- Layout,
17
14
  EventEmitterProps,
18
- PagerProps,
15
+ Layout,
19
16
  Listener,
17
+ NavigationState,
18
+ PagerProps,
19
+ Route,
20
20
  } from './types';
21
+ import useAnimatedValue from './useAnimatedValue';
21
22
 
22
23
  type Props<T extends Route> = PagerProps & {
23
24
  layout: Layout;
@@ -283,10 +284,13 @@ export default function PanResponderAdapter<T extends Route>({
283
284
  I18nManager.isRTL ? -1 : 1
284
285
  );
285
286
 
287
+ const position = React.useMemo(
288
+ () => (layout.width ? Animated.divide(panX, -layout.width) : null),
289
+ [layout.width, panX]
290
+ );
291
+
286
292
  return children({
287
- position: layout.width
288
- ? Animated.divide(panX, -layout.width)
289
- : new Animated.Value(index),
293
+ position: position ?? new Animated.Value(index),
290
294
  addEnterListener,
291
295
  jumpTo,
292
296
  render: (children) => (
package/src/SceneMap.tsx CHANGED
@@ -1,19 +1,24 @@
1
1
  import * as React from 'react';
2
+
2
3
  import type { SceneRendererProps } from './types';
3
4
 
4
- class SceneComponent<
5
- T extends { component: React.ComponentType<any> }
6
- > extends React.PureComponent<T> {
7
- render() {
8
- const { component, ...rest } = this.props;
5
+ type SceneProps = {
6
+ route: any;
7
+ } & Omit<SceneRendererProps, 'layout'>;
8
+
9
+ const SceneComponent = React.memo(
10
+ <T extends { component: React.ComponentType<any> } & SceneProps>({
11
+ component,
12
+ ...rest
13
+ }: T) => {
9
14
  return React.createElement(component, rest);
10
15
  }
11
- }
16
+ );
12
17
 
13
18
  export default function SceneMap<T extends any>(scenes: {
14
19
  [key: string]: React.ComponentType<T>;
15
20
  }) {
16
- return ({ route, jumpTo, position }: SceneRendererProps & { route: any }) => (
21
+ return ({ route, jumpTo, position }: SceneProps) => (
17
22
  <SceneComponent
18
23
  key={route.key}
19
24
  component={scenes[route.key]}
package/src/SceneView.tsx CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as React from 'react';
2
- import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
2
+ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
3
+
3
4
  import type {
4
- SceneRendererProps,
5
5
  EventEmitterProps,
6
6
  NavigationState,
7
7
  Route,
8
+ SceneRendererProps,
8
9
  } from './types';
9
10
 
10
11
  type Props<T extends Route> = SceneRendererProps &
@@ -17,121 +18,85 @@ type Props<T extends Route> = SceneRendererProps &
17
18
  style?: StyleProp<ViewStyle>;
18
19
  };
19
20
 
20
- type State = {
21
- loading: boolean;
22
- };
23
-
24
- export default class SceneView<T extends Route> extends React.Component<
25
- Props<T>,
26
- State
27
- > {
28
- static getDerivedStateFromProps(props: Props<Route>, state: State) {
29
- if (
30
- state.loading &&
31
- Math.abs(props.navigationState.index - props.index) <=
32
- props.lazyPreloadDistance
33
- ) {
34
- // Always render the route when it becomes focused
35
- return { loading: false };
36
- }
37
-
38
- return null;
21
+ export default function SceneView<T extends Route>({
22
+ children,
23
+ navigationState,
24
+ lazy,
25
+ layout,
26
+ index,
27
+ lazyPreloadDistance,
28
+ addEnterListener,
29
+ style,
30
+ }: Props<T>) {
31
+ const [isLoading, setIsLoading] = React.useState(
32
+ Math.abs(navigationState.index - index) > lazyPreloadDistance
33
+ );
34
+
35
+ if (
36
+ isLoading &&
37
+ Math.abs(navigationState.index - index) <= lazyPreloadDistance
38
+ ) {
39
+ // Always render the route when it becomes focused
40
+ setIsLoading(false);
39
41
  }
40
42
 
41
- state = {
42
- loading:
43
- Math.abs(this.props.navigationState.index - this.props.index) >
44
- this.props.lazyPreloadDistance,
45
- };
43
+ React.useEffect(() => {
44
+ const handleEnter = (value: number) => {
45
+ // If we're entering the current route, we need to load it
46
+ if (value === index) {
47
+ setIsLoading((prevState) => {
48
+ if (prevState) {
49
+ return false;
50
+ }
51
+ return prevState;
52
+ });
53
+ }
54
+ };
55
+
56
+ let unsubscribe: (() => void) | undefined;
57
+ let timer: NodeJS.Timeout;
46
58
 
47
- componentDidMount() {
48
- if (this.props.lazy) {
59
+ if (lazy && isLoading) {
49
60
  // If lazy mode is enabled, listen to when we enter screens
50
- this.unsubscribe = this.props.addEnterListener(this.handleEnter);
51
- } else if (this.state.loading) {
61
+ unsubscribe = addEnterListener(handleEnter);
62
+ } else if (isLoading) {
52
63
  // If lazy mode is not enabled, render the scene with a delay if not loaded already
53
64
  // This improves the initial startup time as the scene is no longer blocking
54
- this.timerHandler = setTimeout(
55
- () => this.setState({ loading: false }),
56
- 0
57
- );
65
+ timer = setTimeout(() => setIsLoading(false), 0);
58
66
  }
59
- }
60
67
 
61
- componentDidUpdate(prevProps: Props<T>, prevState: State) {
62
- if (
63
- this.props.lazy !== prevProps.lazy ||
64
- this.state.loading !== prevState.loading
65
- ) {
66
- // We only need the listener if the tab hasn't loaded yet and lazy is enabled
67
- if (this.props.lazy && this.state.loading) {
68
- this.unsubscribe?.();
69
- this.unsubscribe = this.props.addEnterListener(this.handleEnter);
70
- } else {
71
- this.unsubscribe?.();
68
+ return () => {
69
+ unsubscribe?.();
70
+ clearTimeout(timer);
71
+ };
72
+ }, [addEnterListener, index, isLoading, lazy]);
73
+
74
+ const focused = navigationState.index === index;
75
+
76
+ return (
77
+ <View
78
+ accessibilityElementsHidden={!focused}
79
+ importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
80
+ style={[
81
+ styles.route,
82
+ // If we don't have the layout yet, make the focused screen fill the container
83
+ // This avoids delay before we are able to render pages side by side
84
+ layout.width
85
+ ? { width: layout.width }
86
+ : focused
87
+ ? StyleSheet.absoluteFill
88
+ : null,
89
+ style,
90
+ ]}
91
+ >
92
+ {
93
+ // Only render the route only if it's either focused or layout is available
94
+ // When layout is not available, we must not render unfocused routes
95
+ // so that the focused route can fill the screen
96
+ focused || layout.width ? children({ loading: isLoading }) : null
72
97
  }
73
- }
74
- }
75
-
76
- componentWillUnmount() {
77
- this.unsubscribe?.();
78
-
79
- if (this.timerHandler) {
80
- clearTimeout(this.timerHandler);
81
- this.timerHandler = undefined;
82
- }
83
- }
84
-
85
- private timerHandler: NodeJS.Timeout | undefined;
86
-
87
- private unsubscribe: (() => void) | null = null;
88
-
89
- private handleEnter = (value: number) => {
90
- const { index } = this.props;
91
-
92
- // If we're entering the current route, we need to load it
93
- if (value === index) {
94
- this.setState((prevState) => {
95
- if (prevState.loading) {
96
- return { loading: false };
97
- }
98
-
99
- return null;
100
- });
101
- }
102
- };
103
-
104
- render() {
105
- const { navigationState, index, layout, style } = this.props;
106
- const { loading } = this.state;
107
-
108
- const focused = navigationState.index === index;
109
-
110
- return (
111
- <View
112
- accessibilityElementsHidden={!focused}
113
- importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
114
- style={[
115
- styles.route,
116
- // If we don't have the layout yet, make the focused screen fill the container
117
- // This avoids delay before we are able to render pages side by side
118
- layout.width
119
- ? { width: layout.width }
120
- : focused
121
- ? StyleSheet.absoluteFill
122
- : null,
123
- style,
124
- ]}
125
- >
126
- {
127
- // Only render the route only if it's either focused or layout is available
128
- // When layout is not available, we must not render unfocused routes
129
- // so that the focused route can fill the screen
130
- focused || layout.width ? this.props.children({ loading }) : null
131
- }
132
- </View>
133
- );
134
- }
98
+ </View>
99
+ );
135
100
  }
136
101
 
137
102
  const styles = StyleSheet.create({