@hero-design/rn 8.63.3 → 8.63.4-alpha.0
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.
- package/es/index.js +34 -19
- package/eslint.config.js +42 -0
- package/lib/index.js +34 -19
- package/package.json +7 -3
- package/rollup.config.js +13 -0
- package/sonar-project.properties +1 -1
- package/src/components/Tabs/ScrollableTabs.tsx +10 -16
- package/src/components/Tabs/__tests__/ScrollableTabs.spec.tsx +32 -0
- package/src/components/Tabs/__tests__/index.spec.tsx +32 -0
- package/src/components/Tabs/index.tsx +12 -8
- package/src/components/Tabs/useHandlePageScroll.tsx +32 -0
- package/stats/8.63.3/rn-stats.html +4844 -0
- package/types/components/Tabs/index.d.ts +1 -1
- package/types/components/Tabs/useHandlePageScroll.d.ts +8 -0
- package/types/testHelpers/utils.d.ts +1 -0
- package/.eslintrc.js +0 -13
- package/.turbo/turbo-build.log +0 -5
- package/src/theme/components/.eslintrc.json +0 -10
package/es/index.js
CHANGED
|
@@ -17827,6 +17827,20 @@ var ScrollableTabHeader = function ScrollableTabHeader(_ref2) {
|
|
|
17827
17827
|
}));
|
|
17828
17828
|
};
|
|
17829
17829
|
|
|
17830
|
+
var useHandlePageScroll = function useHandlePageScroll() {
|
|
17831
|
+
// Used as a flag to prevent calling onTabPress on initial render
|
|
17832
|
+
var hasScrolled = useRef(false);
|
|
17833
|
+
var onPageScrollStateChanged = useCallback(function (e) {
|
|
17834
|
+
if (!hasScrolled.current && e.nativeEvent.pageScrollState === 'dragging') {
|
|
17835
|
+
hasScrolled.current = true;
|
|
17836
|
+
}
|
|
17837
|
+
}, []);
|
|
17838
|
+
return {
|
|
17839
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
17840
|
+
hasScrolled: hasScrolled
|
|
17841
|
+
};
|
|
17842
|
+
};
|
|
17843
|
+
|
|
17830
17844
|
var TabContext = /*#__PURE__*/React__default.createContext(null);
|
|
17831
17845
|
var ScreenContext = /*#__PURE__*/React__default.createContext(null);
|
|
17832
17846
|
var useIsFocused = function useIsFocused() {
|
|
@@ -17854,14 +17868,15 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17854
17868
|
componentTestID = _ref.testID,
|
|
17855
17869
|
_ref$variant = _ref.variant,
|
|
17856
17870
|
variant = _ref$variant === void 0 ? 'highlighted' : _ref$variant;
|
|
17857
|
-
var pagerViewRef =
|
|
17871
|
+
var pagerViewRef = useRef(null);
|
|
17858
17872
|
var insets = useSafeAreaInsets();
|
|
17859
17873
|
var selectedTabIndex = tabs.findIndex(function (item) {
|
|
17860
17874
|
return item.key === selectedTabKey;
|
|
17861
17875
|
});
|
|
17862
|
-
|
|
17863
|
-
|
|
17864
|
-
|
|
17876
|
+
var _useHandlePageScroll = useHandlePageScroll(),
|
|
17877
|
+
hasScrolled = _useHandlePageScroll.hasScrolled,
|
|
17878
|
+
onPageScrollStateChanged = _useHandlePageScroll.onPageScrollStateChanged;
|
|
17879
|
+
useEffect(function () {
|
|
17865
17880
|
var timeoutHandle;
|
|
17866
17881
|
if (selectedTabIndex !== -1) {
|
|
17867
17882
|
// If the selected tab is changed too quickly, the setPage is crashed and not work anymore
|
|
@@ -17878,7 +17893,7 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17878
17893
|
}
|
|
17879
17894
|
};
|
|
17880
17895
|
}, [selectedTabIndex, pagerViewRef]);
|
|
17881
|
-
var tabContextProviderValue =
|
|
17896
|
+
var tabContextProviderValue = useMemo(function () {
|
|
17882
17897
|
return {
|
|
17883
17898
|
selectedTabKey: selectedTabKey
|
|
17884
17899
|
};
|
|
@@ -17900,11 +17915,7 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17900
17915
|
useNext: true,
|
|
17901
17916
|
initialPage: selectedTabIndex,
|
|
17902
17917
|
ref: pagerViewRef,
|
|
17903
|
-
onPageScrollStateChanged:
|
|
17904
|
-
if (!hasScrolled.current && e.nativeEvent.pageScrollState === 'dragging') {
|
|
17905
|
-
hasScrolled.current = true;
|
|
17906
|
-
}
|
|
17907
|
-
},
|
|
17918
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
17908
17919
|
onPageSelected: function onPageSelected(e) {
|
|
17909
17920
|
var index = e.nativeEvent.position;
|
|
17910
17921
|
var selectedItem = tabs[index];
|
|
@@ -17968,23 +17979,26 @@ var Tabs = function Tabs(_ref2) {
|
|
|
17968
17979
|
componentTestID = _ref2.testID;
|
|
17969
17980
|
var theme = useTheme$1();
|
|
17970
17981
|
var insets = useSafeAreaInsets();
|
|
17971
|
-
var pagerViewRef =
|
|
17982
|
+
var pagerViewRef = useRef(null);
|
|
17972
17983
|
var selectedTabIndex = tabs.findIndex(function (item) {
|
|
17973
17984
|
return item.key === selectedTabKey;
|
|
17974
17985
|
});
|
|
17975
|
-
var scrollOffsetAnimatedValue =
|
|
17976
|
-
var positionAnimatedValue =
|
|
17977
|
-
var
|
|
17978
|
-
|
|
17979
|
-
tabsWidth =
|
|
17980
|
-
setTabsWidth =
|
|
17986
|
+
var scrollOffsetAnimatedValue = useRef(new Animated.Value(0)).current;
|
|
17987
|
+
var positionAnimatedValue = useRef(new Animated.Value(0)).current;
|
|
17988
|
+
var _useState = useState(0),
|
|
17989
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
17990
|
+
tabsWidth = _useState2[0],
|
|
17991
|
+
setTabsWidth = _useState2[1];
|
|
17992
|
+
var _useHandlePageScroll = useHandlePageScroll(),
|
|
17993
|
+
onPageScrollStateChanged = _useHandlePageScroll.onPageScrollStateChanged,
|
|
17994
|
+
hasScrolled = _useHandlePageScroll.hasScrolled;
|
|
17981
17995
|
useEffect(function () {
|
|
17982
17996
|
if (selectedTabIndex !== -1) {
|
|
17983
17997
|
var _pagerViewRef$current;
|
|
17984
17998
|
(_pagerViewRef$current = pagerViewRef.current) === null || _pagerViewRef$current === void 0 || _pagerViewRef$current.setPage(selectedTabIndex);
|
|
17985
17999
|
}
|
|
17986
18000
|
}, [selectedTabIndex]);
|
|
17987
|
-
var tabContextProviderValue =
|
|
18001
|
+
var tabContextProviderValue = useMemo(function () {
|
|
17988
18002
|
return {
|
|
17989
18003
|
selectedTabKey: selectedTabKey
|
|
17990
18004
|
};
|
|
@@ -18040,10 +18054,11 @@ var Tabs = function Tabs(_ref2) {
|
|
|
18040
18054
|
onPageSelected: function onPageSelected(e) {
|
|
18041
18055
|
var index = e.nativeEvent.position;
|
|
18042
18056
|
var selectedItem = tabs[index];
|
|
18043
|
-
if (selectedItem) {
|
|
18057
|
+
if (hasScrolled.current && selectedItem) {
|
|
18044
18058
|
onTabPress(selectedItem.key);
|
|
18045
18059
|
}
|
|
18046
18060
|
},
|
|
18061
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
18047
18062
|
onPageScroll: Animated.event([{
|
|
18048
18063
|
nativeEvent: {
|
|
18049
18064
|
offset: scrollOffsetAnimatedValue,
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const heroDesign = require('@hero-design/eslint-plugin');
|
|
2
|
+
const _import = require('eslint-plugin-import');
|
|
3
|
+
const { FlatCompat } = require('@eslint/eslintrc');
|
|
4
|
+
const { includeIgnoreFile } = require('@eslint/compat');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const compat = new FlatCompat({
|
|
8
|
+
baseDirectory: __dirname,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const gitignorePath = path.resolve(__dirname, '../../.gitignore');
|
|
12
|
+
|
|
13
|
+
module.exports = [
|
|
14
|
+
...compat.extends('hd', 'plugin:@hero-design/recommendedRn'),
|
|
15
|
+
includeIgnoreFile(gitignorePath),
|
|
16
|
+
{
|
|
17
|
+
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
|
|
18
|
+
|
|
19
|
+
plugins: {
|
|
20
|
+
'@hero-design': heroDesign,
|
|
21
|
+
import: _import,
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
languageOptions: {
|
|
25
|
+
parserOptions: {
|
|
26
|
+
tsconfigRootDir: __dirname,
|
|
27
|
+
project: ['./tsconfig.json'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
rules: {
|
|
32
|
+
'no-underscore-dangle': [
|
|
33
|
+
'error',
|
|
34
|
+
{
|
|
35
|
+
allow: ['__hd__'],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
|
|
39
|
+
'import/no-cycle': 'error',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
package/lib/index.js
CHANGED
|
@@ -17857,6 +17857,20 @@ var ScrollableTabHeader = function ScrollableTabHeader(_ref2) {
|
|
|
17857
17857
|
}));
|
|
17858
17858
|
};
|
|
17859
17859
|
|
|
17860
|
+
var useHandlePageScroll = function useHandlePageScroll() {
|
|
17861
|
+
// Used as a flag to prevent calling onTabPress on initial render
|
|
17862
|
+
var hasScrolled = React.useRef(false);
|
|
17863
|
+
var onPageScrollStateChanged = React.useCallback(function (e) {
|
|
17864
|
+
if (!hasScrolled.current && e.nativeEvent.pageScrollState === 'dragging') {
|
|
17865
|
+
hasScrolled.current = true;
|
|
17866
|
+
}
|
|
17867
|
+
}, []);
|
|
17868
|
+
return {
|
|
17869
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
17870
|
+
hasScrolled: hasScrolled
|
|
17871
|
+
};
|
|
17872
|
+
};
|
|
17873
|
+
|
|
17860
17874
|
var TabContext = /*#__PURE__*/React__default["default"].createContext(null);
|
|
17861
17875
|
var ScreenContext = /*#__PURE__*/React__default["default"].createContext(null);
|
|
17862
17876
|
var useIsFocused = function useIsFocused() {
|
|
@@ -17884,14 +17898,15 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17884
17898
|
componentTestID = _ref.testID,
|
|
17885
17899
|
_ref$variant = _ref.variant,
|
|
17886
17900
|
variant = _ref$variant === void 0 ? 'highlighted' : _ref$variant;
|
|
17887
|
-
var pagerViewRef =
|
|
17901
|
+
var pagerViewRef = React.useRef(null);
|
|
17888
17902
|
var insets = reactNativeSafeAreaContext.useSafeAreaInsets();
|
|
17889
17903
|
var selectedTabIndex = tabs.findIndex(function (item) {
|
|
17890
17904
|
return item.key === selectedTabKey;
|
|
17891
17905
|
});
|
|
17892
|
-
|
|
17893
|
-
|
|
17894
|
-
|
|
17906
|
+
var _useHandlePageScroll = useHandlePageScroll(),
|
|
17907
|
+
hasScrolled = _useHandlePageScroll.hasScrolled,
|
|
17908
|
+
onPageScrollStateChanged = _useHandlePageScroll.onPageScrollStateChanged;
|
|
17909
|
+
React.useEffect(function () {
|
|
17895
17910
|
var timeoutHandle;
|
|
17896
17911
|
if (selectedTabIndex !== -1) {
|
|
17897
17912
|
// If the selected tab is changed too quickly, the setPage is crashed and not work anymore
|
|
@@ -17908,7 +17923,7 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17908
17923
|
}
|
|
17909
17924
|
};
|
|
17910
17925
|
}, [selectedTabIndex, pagerViewRef]);
|
|
17911
|
-
var tabContextProviderValue =
|
|
17926
|
+
var tabContextProviderValue = React.useMemo(function () {
|
|
17912
17927
|
return {
|
|
17913
17928
|
selectedTabKey: selectedTabKey
|
|
17914
17929
|
};
|
|
@@ -17930,11 +17945,7 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17930
17945
|
useNext: true,
|
|
17931
17946
|
initialPage: selectedTabIndex,
|
|
17932
17947
|
ref: pagerViewRef,
|
|
17933
|
-
onPageScrollStateChanged:
|
|
17934
|
-
if (!hasScrolled.current && e.nativeEvent.pageScrollState === 'dragging') {
|
|
17935
|
-
hasScrolled.current = true;
|
|
17936
|
-
}
|
|
17937
|
-
},
|
|
17948
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
17938
17949
|
onPageSelected: function onPageSelected(e) {
|
|
17939
17950
|
var index = e.nativeEvent.position;
|
|
17940
17951
|
var selectedItem = tabs[index];
|
|
@@ -17998,23 +18009,26 @@ var Tabs = function Tabs(_ref2) {
|
|
|
17998
18009
|
componentTestID = _ref2.testID;
|
|
17999
18010
|
var theme = useTheme$1();
|
|
18000
18011
|
var insets = reactNativeSafeAreaContext.useSafeAreaInsets();
|
|
18001
|
-
var pagerViewRef =
|
|
18012
|
+
var pagerViewRef = React.useRef(null);
|
|
18002
18013
|
var selectedTabIndex = tabs.findIndex(function (item) {
|
|
18003
18014
|
return item.key === selectedTabKey;
|
|
18004
18015
|
});
|
|
18005
|
-
var scrollOffsetAnimatedValue =
|
|
18006
|
-
var positionAnimatedValue =
|
|
18007
|
-
var
|
|
18008
|
-
|
|
18009
|
-
tabsWidth =
|
|
18010
|
-
setTabsWidth =
|
|
18016
|
+
var scrollOffsetAnimatedValue = React.useRef(new reactNative.Animated.Value(0)).current;
|
|
18017
|
+
var positionAnimatedValue = React.useRef(new reactNative.Animated.Value(0)).current;
|
|
18018
|
+
var _useState = React.useState(0),
|
|
18019
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
18020
|
+
tabsWidth = _useState2[0],
|
|
18021
|
+
setTabsWidth = _useState2[1];
|
|
18022
|
+
var _useHandlePageScroll = useHandlePageScroll(),
|
|
18023
|
+
onPageScrollStateChanged = _useHandlePageScroll.onPageScrollStateChanged,
|
|
18024
|
+
hasScrolled = _useHandlePageScroll.hasScrolled;
|
|
18011
18025
|
React.useEffect(function () {
|
|
18012
18026
|
if (selectedTabIndex !== -1) {
|
|
18013
18027
|
var _pagerViewRef$current;
|
|
18014
18028
|
(_pagerViewRef$current = pagerViewRef.current) === null || _pagerViewRef$current === void 0 || _pagerViewRef$current.setPage(selectedTabIndex);
|
|
18015
18029
|
}
|
|
18016
18030
|
}, [selectedTabIndex]);
|
|
18017
|
-
var tabContextProviderValue =
|
|
18031
|
+
var tabContextProviderValue = React.useMemo(function () {
|
|
18018
18032
|
return {
|
|
18019
18033
|
selectedTabKey: selectedTabKey
|
|
18020
18034
|
};
|
|
@@ -18070,10 +18084,11 @@ var Tabs = function Tabs(_ref2) {
|
|
|
18070
18084
|
onPageSelected: function onPageSelected(e) {
|
|
18071
18085
|
var index = e.nativeEvent.position;
|
|
18072
18086
|
var selectedItem = tabs[index];
|
|
18073
|
-
if (selectedItem) {
|
|
18087
|
+
if (hasScrolled.current && selectedItem) {
|
|
18074
18088
|
onTabPress(selectedItem.key);
|
|
18075
18089
|
}
|
|
18076
18090
|
},
|
|
18091
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
18077
18092
|
onPageScroll: reactNative.Animated.event([{
|
|
18078
18093
|
nativeEvent: {
|
|
18079
18094
|
offset: scrollOffsetAnimatedValue,
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hero-design/rn",
|
|
3
|
-
"version": "8.63.
|
|
3
|
+
"version": "8.63.4-alpha.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.js",
|
|
7
7
|
"types": "types/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"lint": "eslint src
|
|
9
|
+
"lint": "eslint src",
|
|
10
10
|
"type-check": "tsc --noEmit",
|
|
11
11
|
"test": "jest --runInBand",
|
|
12
12
|
"test:watch": "jest --runInBand --watch",
|
|
@@ -47,7 +47,10 @@
|
|
|
47
47
|
"@babel/preset-typescript": "^7.20.0",
|
|
48
48
|
"@babel/runtime": "^7.20.0",
|
|
49
49
|
"@emotion/jest": "^11.11.0",
|
|
50
|
-
"@
|
|
50
|
+
"@eslint/compat": "^1.1.1",
|
|
51
|
+
"@eslint/eslintrc": "^3.1.0",
|
|
52
|
+
"@eslint/js": "^9.8.0",
|
|
53
|
+
"@hero-design/eslint-plugin": "9.0.1",
|
|
51
54
|
"@hero-design/react-native-month-year-picker": "^8.42.10",
|
|
52
55
|
"@react-native-community/datetimepicker": "7.6.1",
|
|
53
56
|
"@react-native-community/slider": "^4.5.1",
|
|
@@ -86,6 +89,7 @@
|
|
|
86
89
|
"rollup": "^2.68.0",
|
|
87
90
|
"rollup-plugin-copy": "^3.4.0",
|
|
88
91
|
"rollup-plugin-flow": "^1.1.1",
|
|
92
|
+
"rollup-plugin-visualizer": "^5.12.0",
|
|
89
93
|
"ts-jest": "^29.1.1",
|
|
90
94
|
"typescript": "4.8.4"
|
|
91
95
|
},
|
package/rollup.config.js
CHANGED
|
@@ -6,11 +6,16 @@ import json from '@rollup/plugin-json';
|
|
|
6
6
|
import replace from '@rollup/plugin-replace';
|
|
7
7
|
import copy from 'rollup-plugin-copy';
|
|
8
8
|
import flow from 'rollup-plugin-flow';
|
|
9
|
+
import { visualizer } from 'rollup-plugin-visualizer';
|
|
9
10
|
|
|
10
11
|
import pkg from './package.json';
|
|
11
12
|
|
|
12
13
|
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
|
|
13
14
|
|
|
15
|
+
const generateBuildStats = process.env.GENERATE_BUILD_STATS === 'true';
|
|
16
|
+
const bundleTemplate = process.env.BUNDLE_TEMPLATE || 'treemap';
|
|
17
|
+
const fileName = process.env.FILE_NAME || `stats/${pkg.version}/rn-stats.html`;
|
|
18
|
+
|
|
14
19
|
export default {
|
|
15
20
|
input: 'src/index.ts',
|
|
16
21
|
output: [
|
|
@@ -54,5 +59,13 @@ export default {
|
|
|
54
59
|
},
|
|
55
60
|
],
|
|
56
61
|
}),
|
|
62
|
+
...(generateBuildStats
|
|
63
|
+
? [
|
|
64
|
+
visualizer({
|
|
65
|
+
filename: fileName,
|
|
66
|
+
template: bundleTemplate,
|
|
67
|
+
}),
|
|
68
|
+
]
|
|
69
|
+
: []),
|
|
57
70
|
],
|
|
58
71
|
};
|
package/sonar-project.properties
CHANGED
|
@@ -6,6 +6,6 @@ sonar.organization=thinkei
|
|
|
6
6
|
|
|
7
7
|
sonar.sources=.
|
|
8
8
|
sonar.inclusions=**/*
|
|
9
|
-
sonar.exclusions=**/__tests__/**,**/public
|
|
9
|
+
sonar.exclusions=**/__tests__/**,**/public/**,**/stats/**,**.config.js
|
|
10
10
|
sonar.java.binaries=**/src/main/java
|
|
11
11
|
sonar.javascript.lcov.reportPaths=./coverage/lcov.info
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import React, { useRef } from 'react';
|
|
2
|
-
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
1
|
+
import React, { useEffect, useMemo, useRef } from 'react';
|
|
3
2
|
import PagerView from 'react-native-pager-view';
|
|
4
|
-
import {
|
|
3
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
4
|
import type { TabsProps } from '.';
|
|
6
5
|
import SceneView from './SceneView';
|
|
7
6
|
import ScrollableTabHeader from './ScrollableTabsHeader/ScrollableTabsHeader';
|
|
7
|
+
import { TabContainer } from './StyledScrollableTabs';
|
|
8
|
+
import useHandlePageScroll from './useHandlePageScroll';
|
|
8
9
|
import { ScreenContext, TabContext } from './useIsFocused';
|
|
9
10
|
|
|
10
11
|
export interface ScrollableTabProps extends TabsProps {
|
|
@@ -23,15 +24,15 @@ const ScrollableTab = ({
|
|
|
23
24
|
testID: componentTestID,
|
|
24
25
|
variant = 'highlighted',
|
|
25
26
|
}: ScrollableTabProps) => {
|
|
26
|
-
const pagerViewRef =
|
|
27
|
+
const pagerViewRef = useRef<PagerView>(null);
|
|
27
28
|
const insets = useSafeAreaInsets();
|
|
28
29
|
const selectedTabIndex = tabs.findIndex(
|
|
29
30
|
(item) => item.key === selectedTabKey
|
|
30
31
|
);
|
|
31
|
-
// Used as a flag to prevent calling onTabPress on initial render
|
|
32
|
-
const hasScrolled = useRef(false);
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
const { hasScrolled, onPageScrollStateChanged } = useHandlePageScroll();
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
35
36
|
let timeoutHandle: ReturnType<typeof setTimeout>;
|
|
36
37
|
if (selectedTabIndex !== -1) {
|
|
37
38
|
// If the selected tab is changed too quickly, the setPage is crashed and not work anymore
|
|
@@ -49,7 +50,7 @@ const ScrollableTab = ({
|
|
|
49
50
|
};
|
|
50
51
|
}, [selectedTabIndex, pagerViewRef]);
|
|
51
52
|
|
|
52
|
-
const tabContextProviderValue =
|
|
53
|
+
const tabContextProviderValue = useMemo(
|
|
53
54
|
() => ({
|
|
54
55
|
selectedTabKey,
|
|
55
56
|
}),
|
|
@@ -72,14 +73,7 @@ const ScrollableTab = ({
|
|
|
72
73
|
useNext
|
|
73
74
|
initialPage={selectedTabIndex}
|
|
74
75
|
ref={pagerViewRef}
|
|
75
|
-
onPageScrollStateChanged={
|
|
76
|
-
if (
|
|
77
|
-
!hasScrolled.current &&
|
|
78
|
-
e.nativeEvent.pageScrollState === 'dragging'
|
|
79
|
-
) {
|
|
80
|
-
hasScrolled.current = true;
|
|
81
|
-
}
|
|
82
|
-
}}
|
|
76
|
+
onPageScrollStateChanged={onPageScrollStateChanged}
|
|
83
77
|
onPageSelected={(e) => {
|
|
84
78
|
const index = e.nativeEvent.position;
|
|
85
79
|
const selectedItem = tabs[index];
|
|
@@ -99,6 +99,38 @@ describe('Tabs.Scroll', () => {
|
|
|
99
99
|
expect(getByText('Calendar Screen unfocused')).toBeDefined();
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
+
it('calls onTabPress correctly', () => {
|
|
103
|
+
const onTabPress = jest.fn();
|
|
104
|
+
const { getByText } = renderWithTheme(
|
|
105
|
+
<SafeAreaProvider
|
|
106
|
+
initialMetrics={{
|
|
107
|
+
frame: { x: 0, y: 0, width: 0, height: 0 },
|
|
108
|
+
insets: { top: 0, left: 0, right: 0, bottom: 0 },
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<ScrollableTabs
|
|
112
|
+
tabs={[
|
|
113
|
+
{
|
|
114
|
+
key: 'work',
|
|
115
|
+
activeItem: 'Work',
|
|
116
|
+
component: <CustomScreen title="Work Screen" />,
|
|
117
|
+
},
|
|
118
|
+
]}
|
|
119
|
+
onTabPress={onTabPress}
|
|
120
|
+
selectedTabKey="work"
|
|
121
|
+
/>
|
|
122
|
+
</SafeAreaProvider>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Not calling the function on first render
|
|
126
|
+
expect(onTabPress).not.toHaveBeenCalled();
|
|
127
|
+
|
|
128
|
+
// Calling the function on tab press
|
|
129
|
+
fireEvent.press(getByText('Work'));
|
|
130
|
+
|
|
131
|
+
expect(onTabPress).toHaveBeenCalledTimes(1);
|
|
132
|
+
});
|
|
133
|
+
|
|
102
134
|
describe('lazy', () => {
|
|
103
135
|
it('render all screens when lazy = false', async () => {
|
|
104
136
|
const { queryByText } = renderWithTheme(
|
|
@@ -110,6 +110,38 @@ describe('Tabs', () => {
|
|
|
110
110
|
fireEvent.press(getByText('Home'));
|
|
111
111
|
expect(getByText('Home Screen focused')).toBeDefined();
|
|
112
112
|
});
|
|
113
|
+
|
|
114
|
+
it('calls onTabPress only when pressed', () => {
|
|
115
|
+
const onTabPress = jest.fn();
|
|
116
|
+
const { getByText } = renderWithTheme(
|
|
117
|
+
<SafeAreaProvider
|
|
118
|
+
initialMetrics={{
|
|
119
|
+
frame: { x: 0, y: 0, width: 0, height: 0 },
|
|
120
|
+
insets: { top: 0, left: 0, right: 0, bottom: 0 },
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
<Tabs
|
|
124
|
+
tabs={[
|
|
125
|
+
{
|
|
126
|
+
key: 'work',
|
|
127
|
+
activeItem: 'Work',
|
|
128
|
+
component: <CustomScreen title="Work Screen" />,
|
|
129
|
+
},
|
|
130
|
+
]}
|
|
131
|
+
onTabPress={onTabPress}
|
|
132
|
+
selectedTabKey="work"
|
|
133
|
+
/>
|
|
134
|
+
</SafeAreaProvider>
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// Not calling the function on first render
|
|
138
|
+
expect(onTabPress).not.toHaveBeenCalled();
|
|
139
|
+
|
|
140
|
+
// Calling the function on tab press
|
|
141
|
+
fireEvent.press(getByText('Work'));
|
|
142
|
+
|
|
143
|
+
expect(onTabPress).toHaveBeenCalledTimes(1);
|
|
144
|
+
});
|
|
113
145
|
});
|
|
114
146
|
|
|
115
147
|
describe('useIsFocused', () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useTheme } from '@emotion/react';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
|
-
import React, { useEffect } from 'react';
|
|
3
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import type { StyleProp, ViewProps, ViewStyle } from 'react-native';
|
|
5
5
|
import { Animated, TouchableWithoutFeedback, View } from 'react-native';
|
|
6
6
|
import type { PagerViewOnPageScrollEventData } from 'react-native-pager-view';
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from './StyledTabs';
|
|
20
20
|
import type { BadgeConfigType } from './TabWithBadge';
|
|
21
21
|
import TabWithBadge from './TabWithBadge';
|
|
22
|
+
import useHandlePageScroll from './useHandlePageScroll';
|
|
22
23
|
import { ScreenContext, TabContext, useIsFocused } from './useIsFocused';
|
|
23
24
|
|
|
24
25
|
export type ItemType =
|
|
@@ -57,7 +58,7 @@ export interface TabsProps extends ViewProps {
|
|
|
57
58
|
*/
|
|
58
59
|
barStyle?: StyleProp<ViewStyle>;
|
|
59
60
|
/**
|
|
60
|
-
* Whether inactive screen should be removed and unmounted in
|
|
61
|
+
* Whether inactive screen should be removed and unmounted in
|
|
61
62
|
* Defaults value is `false`.
|
|
62
63
|
*/
|
|
63
64
|
lazy?: boolean;
|
|
@@ -116,13 +117,15 @@ const Tabs = ({
|
|
|
116
117
|
}: TabsProps): JSX.Element => {
|
|
117
118
|
const theme = useTheme();
|
|
118
119
|
const insets = useSafeAreaInsets();
|
|
119
|
-
const pagerViewRef =
|
|
120
|
+
const pagerViewRef = useRef<PagerView>(null);
|
|
120
121
|
const selectedTabIndex = tabs.findIndex(
|
|
121
122
|
(item) => item.key === selectedTabKey
|
|
122
123
|
);
|
|
123
|
-
const scrollOffsetAnimatedValue =
|
|
124
|
-
const positionAnimatedValue =
|
|
125
|
-
const [tabsWidth, setTabsWidth] =
|
|
124
|
+
const scrollOffsetAnimatedValue = useRef(new Animated.Value(0)).current;
|
|
125
|
+
const positionAnimatedValue = useRef(new Animated.Value(0)).current;
|
|
126
|
+
const [tabsWidth, setTabsWidth] = useState<number>(0);
|
|
127
|
+
|
|
128
|
+
const { onPageScrollStateChanged, hasScrolled } = useHandlePageScroll();
|
|
126
129
|
|
|
127
130
|
useEffect(() => {
|
|
128
131
|
if (selectedTabIndex !== -1) {
|
|
@@ -130,7 +133,7 @@ const Tabs = ({
|
|
|
130
133
|
}
|
|
131
134
|
}, [selectedTabIndex]);
|
|
132
135
|
|
|
133
|
-
const tabContextProviderValue =
|
|
136
|
+
const tabContextProviderValue = useMemo(
|
|
134
137
|
() => ({
|
|
135
138
|
selectedTabKey,
|
|
136
139
|
}),
|
|
@@ -201,10 +204,11 @@ const Tabs = ({
|
|
|
201
204
|
onPageSelected={(e) => {
|
|
202
205
|
const index = e.nativeEvent.position;
|
|
203
206
|
const selectedItem = tabs[index];
|
|
204
|
-
if (selectedItem) {
|
|
207
|
+
if (hasScrolled.current && selectedItem) {
|
|
205
208
|
onTabPress(selectedItem.key);
|
|
206
209
|
}
|
|
207
210
|
}}
|
|
211
|
+
onPageScrollStateChanged={onPageScrollStateChanged}
|
|
208
212
|
onPageScroll={Animated.event<PagerViewOnPageScrollEventData>(
|
|
209
213
|
[
|
|
210
214
|
{
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
import { NativeSyntheticEvent } from 'react-native';
|
|
3
|
+
|
|
4
|
+
const useHandlePageScroll = () => {
|
|
5
|
+
// Used as a flag to prevent calling onTabPress on initial render
|
|
6
|
+
const hasScrolled = useRef(false);
|
|
7
|
+
|
|
8
|
+
const onPageScrollStateChanged = useCallback(
|
|
9
|
+
(
|
|
10
|
+
e: NativeSyntheticEvent<
|
|
11
|
+
Readonly<{
|
|
12
|
+
pageScrollState: 'idle' | 'dragging' | 'settling';
|
|
13
|
+
}>
|
|
14
|
+
>
|
|
15
|
+
) => {
|
|
16
|
+
if (
|
|
17
|
+
!hasScrolled.current &&
|
|
18
|
+
e.nativeEvent.pageScrollState === 'dragging'
|
|
19
|
+
) {
|
|
20
|
+
hasScrolled.current = true;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
[]
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
onPageScrollStateChanged,
|
|
28
|
+
hasScrolled,
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default useHandlePageScroll;
|