@hero-design/rn 8.63.2 → 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/CHANGELOG.md +9 -0
- package/es/index.js +35 -19
- package/eslint.config.js +42 -0
- package/lib/index.js +35 -19
- package/package.json +9 -5
- 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/src/components/Toast/Toast.tsx +1 -0
- package/src/components/Toast/__tests__/__snapshots__/Toast.spec.tsx.snap +7 -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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @hero-design/rn
|
|
2
2
|
|
|
3
|
+
## 8.63.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#3129](https://github.com/Thinkei/hero-design/pull/3129) [`116f04b1a`](https://github.com/Thinkei/hero-design/commit/116f04b1ae7a63a4f3563a935989a318200ce044) Thanks [@vinhphan-eh](https://github.com/vinhphan-eh)! - Fix mobile visual tests workflow
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`de331a19e`](https://github.com/Thinkei/hero-design/commit/de331a19ea56e55ff18b060a21b31831df60c794), [`264d28d3c`](https://github.com/Thinkei/hero-design/commit/264d28d3cb2f68b96bc94f4b72a9681cd4c4abb0)]:
|
|
10
|
+
- @hero-design/react-native-month-year-picker@8.42.10
|
|
11
|
+
|
|
3
12
|
## 8.63.2
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/es/index.js
CHANGED
|
@@ -15040,6 +15040,7 @@ var Toast$1 = function Toast(_ref2) {
|
|
|
15040
15040
|
outputRange: toastConfig.position === 'top' ? [-20, distance] : [20, -distance]
|
|
15041
15041
|
});
|
|
15042
15042
|
return /*#__PURE__*/React__default.createElement(Container, {
|
|
15043
|
+
testID: "toast-container",
|
|
15043
15044
|
themeVariant: variant,
|
|
15044
15045
|
themeIntent: intent,
|
|
15045
15046
|
style: [style, {
|
|
@@ -17826,6 +17827,20 @@ var ScrollableTabHeader = function ScrollableTabHeader(_ref2) {
|
|
|
17826
17827
|
}));
|
|
17827
17828
|
};
|
|
17828
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
|
+
|
|
17829
17844
|
var TabContext = /*#__PURE__*/React__default.createContext(null);
|
|
17830
17845
|
var ScreenContext = /*#__PURE__*/React__default.createContext(null);
|
|
17831
17846
|
var useIsFocused = function useIsFocused() {
|
|
@@ -17853,14 +17868,15 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17853
17868
|
componentTestID = _ref.testID,
|
|
17854
17869
|
_ref$variant = _ref.variant,
|
|
17855
17870
|
variant = _ref$variant === void 0 ? 'highlighted' : _ref$variant;
|
|
17856
|
-
var pagerViewRef =
|
|
17871
|
+
var pagerViewRef = useRef(null);
|
|
17857
17872
|
var insets = useSafeAreaInsets();
|
|
17858
17873
|
var selectedTabIndex = tabs.findIndex(function (item) {
|
|
17859
17874
|
return item.key === selectedTabKey;
|
|
17860
17875
|
});
|
|
17861
|
-
|
|
17862
|
-
|
|
17863
|
-
|
|
17876
|
+
var _useHandlePageScroll = useHandlePageScroll(),
|
|
17877
|
+
hasScrolled = _useHandlePageScroll.hasScrolled,
|
|
17878
|
+
onPageScrollStateChanged = _useHandlePageScroll.onPageScrollStateChanged;
|
|
17879
|
+
useEffect(function () {
|
|
17864
17880
|
var timeoutHandle;
|
|
17865
17881
|
if (selectedTabIndex !== -1) {
|
|
17866
17882
|
// If the selected tab is changed too quickly, the setPage is crashed and not work anymore
|
|
@@ -17877,7 +17893,7 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17877
17893
|
}
|
|
17878
17894
|
};
|
|
17879
17895
|
}, [selectedTabIndex, pagerViewRef]);
|
|
17880
|
-
var tabContextProviderValue =
|
|
17896
|
+
var tabContextProviderValue = useMemo(function () {
|
|
17881
17897
|
return {
|
|
17882
17898
|
selectedTabKey: selectedTabKey
|
|
17883
17899
|
};
|
|
@@ -17899,11 +17915,7 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17899
17915
|
useNext: true,
|
|
17900
17916
|
initialPage: selectedTabIndex,
|
|
17901
17917
|
ref: pagerViewRef,
|
|
17902
|
-
onPageScrollStateChanged:
|
|
17903
|
-
if (!hasScrolled.current && e.nativeEvent.pageScrollState === 'dragging') {
|
|
17904
|
-
hasScrolled.current = true;
|
|
17905
|
-
}
|
|
17906
|
-
},
|
|
17918
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
17907
17919
|
onPageSelected: function onPageSelected(e) {
|
|
17908
17920
|
var index = e.nativeEvent.position;
|
|
17909
17921
|
var selectedItem = tabs[index];
|
|
@@ -17967,23 +17979,26 @@ var Tabs = function Tabs(_ref2) {
|
|
|
17967
17979
|
componentTestID = _ref2.testID;
|
|
17968
17980
|
var theme = useTheme$1();
|
|
17969
17981
|
var insets = useSafeAreaInsets();
|
|
17970
|
-
var pagerViewRef =
|
|
17982
|
+
var pagerViewRef = useRef(null);
|
|
17971
17983
|
var selectedTabIndex = tabs.findIndex(function (item) {
|
|
17972
17984
|
return item.key === selectedTabKey;
|
|
17973
17985
|
});
|
|
17974
|
-
var scrollOffsetAnimatedValue =
|
|
17975
|
-
var positionAnimatedValue =
|
|
17976
|
-
var
|
|
17977
|
-
|
|
17978
|
-
tabsWidth =
|
|
17979
|
-
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;
|
|
17980
17995
|
useEffect(function () {
|
|
17981
17996
|
if (selectedTabIndex !== -1) {
|
|
17982
17997
|
var _pagerViewRef$current;
|
|
17983
17998
|
(_pagerViewRef$current = pagerViewRef.current) === null || _pagerViewRef$current === void 0 || _pagerViewRef$current.setPage(selectedTabIndex);
|
|
17984
17999
|
}
|
|
17985
18000
|
}, [selectedTabIndex]);
|
|
17986
|
-
var tabContextProviderValue =
|
|
18001
|
+
var tabContextProviderValue = useMemo(function () {
|
|
17987
18002
|
return {
|
|
17988
18003
|
selectedTabKey: selectedTabKey
|
|
17989
18004
|
};
|
|
@@ -18039,10 +18054,11 @@ var Tabs = function Tabs(_ref2) {
|
|
|
18039
18054
|
onPageSelected: function onPageSelected(e) {
|
|
18040
18055
|
var index = e.nativeEvent.position;
|
|
18041
18056
|
var selectedItem = tabs[index];
|
|
18042
|
-
if (selectedItem) {
|
|
18057
|
+
if (hasScrolled.current && selectedItem) {
|
|
18043
18058
|
onTabPress(selectedItem.key);
|
|
18044
18059
|
}
|
|
18045
18060
|
},
|
|
18061
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
18046
18062
|
onPageScroll: Animated.event([{
|
|
18047
18063
|
nativeEvent: {
|
|
18048
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
|
@@ -15070,6 +15070,7 @@ var Toast$1 = function Toast(_ref2) {
|
|
|
15070
15070
|
outputRange: toastConfig.position === 'top' ? [-20, distance] : [20, -distance]
|
|
15071
15071
|
});
|
|
15072
15072
|
return /*#__PURE__*/React__default["default"].createElement(Container, {
|
|
15073
|
+
testID: "toast-container",
|
|
15073
15074
|
themeVariant: variant,
|
|
15074
15075
|
themeIntent: intent,
|
|
15075
15076
|
style: [style, {
|
|
@@ -17856,6 +17857,20 @@ var ScrollableTabHeader = function ScrollableTabHeader(_ref2) {
|
|
|
17856
17857
|
}));
|
|
17857
17858
|
};
|
|
17858
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
|
+
|
|
17859
17874
|
var TabContext = /*#__PURE__*/React__default["default"].createContext(null);
|
|
17860
17875
|
var ScreenContext = /*#__PURE__*/React__default["default"].createContext(null);
|
|
17861
17876
|
var useIsFocused = function useIsFocused() {
|
|
@@ -17883,14 +17898,15 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17883
17898
|
componentTestID = _ref.testID,
|
|
17884
17899
|
_ref$variant = _ref.variant,
|
|
17885
17900
|
variant = _ref$variant === void 0 ? 'highlighted' : _ref$variant;
|
|
17886
|
-
var pagerViewRef =
|
|
17901
|
+
var pagerViewRef = React.useRef(null);
|
|
17887
17902
|
var insets = reactNativeSafeAreaContext.useSafeAreaInsets();
|
|
17888
17903
|
var selectedTabIndex = tabs.findIndex(function (item) {
|
|
17889
17904
|
return item.key === selectedTabKey;
|
|
17890
17905
|
});
|
|
17891
|
-
|
|
17892
|
-
|
|
17893
|
-
|
|
17906
|
+
var _useHandlePageScroll = useHandlePageScroll(),
|
|
17907
|
+
hasScrolled = _useHandlePageScroll.hasScrolled,
|
|
17908
|
+
onPageScrollStateChanged = _useHandlePageScroll.onPageScrollStateChanged;
|
|
17909
|
+
React.useEffect(function () {
|
|
17894
17910
|
var timeoutHandle;
|
|
17895
17911
|
if (selectedTabIndex !== -1) {
|
|
17896
17912
|
// If the selected tab is changed too quickly, the setPage is crashed and not work anymore
|
|
@@ -17907,7 +17923,7 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17907
17923
|
}
|
|
17908
17924
|
};
|
|
17909
17925
|
}, [selectedTabIndex, pagerViewRef]);
|
|
17910
|
-
var tabContextProviderValue =
|
|
17926
|
+
var tabContextProviderValue = React.useMemo(function () {
|
|
17911
17927
|
return {
|
|
17912
17928
|
selectedTabKey: selectedTabKey
|
|
17913
17929
|
};
|
|
@@ -17929,11 +17945,7 @@ var ScrollableTab = function ScrollableTab(_ref) {
|
|
|
17929
17945
|
useNext: true,
|
|
17930
17946
|
initialPage: selectedTabIndex,
|
|
17931
17947
|
ref: pagerViewRef,
|
|
17932
|
-
onPageScrollStateChanged:
|
|
17933
|
-
if (!hasScrolled.current && e.nativeEvent.pageScrollState === 'dragging') {
|
|
17934
|
-
hasScrolled.current = true;
|
|
17935
|
-
}
|
|
17936
|
-
},
|
|
17948
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
17937
17949
|
onPageSelected: function onPageSelected(e) {
|
|
17938
17950
|
var index = e.nativeEvent.position;
|
|
17939
17951
|
var selectedItem = tabs[index];
|
|
@@ -17997,23 +18009,26 @@ var Tabs = function Tabs(_ref2) {
|
|
|
17997
18009
|
componentTestID = _ref2.testID;
|
|
17998
18010
|
var theme = useTheme$1();
|
|
17999
18011
|
var insets = reactNativeSafeAreaContext.useSafeAreaInsets();
|
|
18000
|
-
var pagerViewRef =
|
|
18012
|
+
var pagerViewRef = React.useRef(null);
|
|
18001
18013
|
var selectedTabIndex = tabs.findIndex(function (item) {
|
|
18002
18014
|
return item.key === selectedTabKey;
|
|
18003
18015
|
});
|
|
18004
|
-
var scrollOffsetAnimatedValue =
|
|
18005
|
-
var positionAnimatedValue =
|
|
18006
|
-
var
|
|
18007
|
-
|
|
18008
|
-
tabsWidth =
|
|
18009
|
-
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;
|
|
18010
18025
|
React.useEffect(function () {
|
|
18011
18026
|
if (selectedTabIndex !== -1) {
|
|
18012
18027
|
var _pagerViewRef$current;
|
|
18013
18028
|
(_pagerViewRef$current = pagerViewRef.current) === null || _pagerViewRef$current === void 0 || _pagerViewRef$current.setPage(selectedTabIndex);
|
|
18014
18029
|
}
|
|
18015
18030
|
}, [selectedTabIndex]);
|
|
18016
|
-
var tabContextProviderValue =
|
|
18031
|
+
var tabContextProviderValue = React.useMemo(function () {
|
|
18017
18032
|
return {
|
|
18018
18033
|
selectedTabKey: selectedTabKey
|
|
18019
18034
|
};
|
|
@@ -18069,10 +18084,11 @@ var Tabs = function Tabs(_ref2) {
|
|
|
18069
18084
|
onPageSelected: function onPageSelected(e) {
|
|
18070
18085
|
var index = e.nativeEvent.position;
|
|
18071
18086
|
var selectedItem = tabs[index];
|
|
18072
|
-
if (selectedItem) {
|
|
18087
|
+
if (hasScrolled.current && selectedItem) {
|
|
18073
18088
|
onTabPress(selectedItem.key);
|
|
18074
18089
|
}
|
|
18075
18090
|
},
|
|
18091
|
+
onPageScrollStateChanged: onPageScrollStateChanged,
|
|
18076
18092
|
onPageScroll: reactNative.Animated.event([{
|
|
18077
18093
|
nativeEvent: {
|
|
18078
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",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"nanoid": "^4.0.2"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@hero-design/react-native-month-year-picker": "^8.42.
|
|
31
|
+
"@hero-design/react-native-month-year-picker": "^8.42.10",
|
|
32
32
|
"@react-native-community/datetimepicker": "^3.5.2 || ^7.6.1",
|
|
33
33
|
"@react-native-community/slider": "^4.5.1",
|
|
34
34
|
"react": "18.2.0",
|
|
@@ -47,8 +47,11 @@
|
|
|
47
47
|
"@babel/preset-typescript": "^7.20.0",
|
|
48
48
|
"@babel/runtime": "^7.20.0",
|
|
49
49
|
"@emotion/jest": "^11.11.0",
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
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",
|
|
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",
|
|
54
57
|
"@rollup/plugin-babel": "^5.3.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;
|