@momo-kits/tab-view 0.89.5 → 0.89.6-rc.1
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/index.tsx +57 -22
- package/package.json +2 -2
- package/publish.sh +21 -23
- package/styles.ts +21 -6
- package/tabBar/CardTabBar.tsx +42 -6
- package/tabBar/SrollableTabBar.tsx +21 -7
- package/tabBar/TabBar.tsx +15 -3
- package/tabItem/CardTabItem.tsx +47 -9
- package/tabItem/TabItem.tsx +118 -33
- package/types.ts +38 -5
package/index.tsx
CHANGED
|
@@ -1,30 +1,51 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
ForwardRefRenderFunction,
|
|
4
|
+
useCallback,
|
|
5
|
+
useContext,
|
|
6
|
+
useImperativeHandle,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import {Animated, LayoutChangeEvent, View} from 'react-native';
|
|
11
|
+
import {Tab, TabViewProps, TabViewRef} from './types';
|
|
4
12
|
import TabBar from './tabBar/TabBar';
|
|
5
13
|
import styles from './styles';
|
|
6
14
|
import CardTabBar from './tabBar/CardTabBar';
|
|
7
15
|
import ScrollableTabBar from './tabBar/SrollableTabBar';
|
|
8
16
|
import PagerView from 'react-native-pager-view';
|
|
17
|
+
import {ApplicationContext} from '@momo-kits/foundation';
|
|
9
18
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
const TabComponent: ForwardRefRenderFunction<TabViewRef, TabViewProps> = (
|
|
20
|
+
{
|
|
21
|
+
scrollable = false,
|
|
22
|
+
tabs = [],
|
|
23
|
+
onPressTabItem,
|
|
24
|
+
type = 'default',
|
|
25
|
+
initialPage = 0,
|
|
26
|
+
pagerProps,
|
|
27
|
+
direction,
|
|
28
|
+
selectedColor,
|
|
29
|
+
unselectedColor,
|
|
30
|
+
},
|
|
31
|
+
ref
|
|
32
|
+
) => {
|
|
18
33
|
const startPage =
|
|
19
34
|
initialPage > tabs.length - 1 ? tabs.length - 1 : initialPage;
|
|
20
35
|
const [selectedIndex, setSelectedIndex] = useState(startPage);
|
|
21
36
|
const [tabBarWidth, setTabBarWidth] = useState(0);
|
|
22
37
|
const isCardTab = type === 'card';
|
|
23
38
|
const pagerRef = useRef<PagerView>(null);
|
|
24
|
-
const
|
|
39
|
+
const scrollX = useRef(new Animated.Value(startPage));
|
|
40
|
+
const lazy = useRef([selectedIndex]).current;
|
|
41
|
+
const {theme} = useContext(ApplicationContext);
|
|
25
42
|
const _onPressTabItem = (index: number) => {
|
|
26
|
-
pagerRef.current?.setPage(index);
|
|
27
43
|
onPressTabItem?.(index);
|
|
44
|
+
pagerRef.current?.setPage(index);
|
|
45
|
+
|
|
46
|
+
if (!lazy.includes(index)) {
|
|
47
|
+
lazy.push(index);
|
|
48
|
+
}
|
|
28
49
|
setSelectedIndex(index);
|
|
29
50
|
};
|
|
30
51
|
|
|
@@ -32,10 +53,12 @@ const TabView: FC<TabViewProps> = ({
|
|
|
32
53
|
setTabBarWidth(e.nativeEvent.layout.width);
|
|
33
54
|
};
|
|
34
55
|
|
|
35
|
-
const onPageSelected = (e: {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
const onPageSelected = (e: {nativeEvent: {position: number}}) => {
|
|
57
|
+
const {position} = e.nativeEvent;
|
|
58
|
+
if (!lazy.includes(position)) {
|
|
59
|
+
lazy.push(position);
|
|
60
|
+
}
|
|
61
|
+
setSelectedIndex(position);
|
|
39
62
|
};
|
|
40
63
|
|
|
41
64
|
let TabBarComponent = scrollable ? ScrollableTabBar : TabBar;
|
|
@@ -46,16 +69,23 @@ const TabView: FC<TabViewProps> = ({
|
|
|
46
69
|
|
|
47
70
|
const onPageScroll = (e: {nativeEvent: {position: any; offset: any}}) => {
|
|
48
71
|
const {position, offset} = e.nativeEvent;
|
|
49
|
-
|
|
72
|
+
scrollX.current.setValue(position + offset);
|
|
50
73
|
};
|
|
51
74
|
|
|
52
75
|
const renderScreen = useCallback(
|
|
53
|
-
(tab: Tab) => {
|
|
76
|
+
(tab: Tab, index: number) => {
|
|
77
|
+
if (!lazy.includes(index)) return <View />;
|
|
54
78
|
return <View>{tab.component}</View>;
|
|
55
79
|
},
|
|
56
|
-
[tabs]
|
|
80
|
+
[tabs]
|
|
57
81
|
);
|
|
58
82
|
|
|
83
|
+
useImperativeHandle(ref, () => ({
|
|
84
|
+
goToPage: (page: number) => {
|
|
85
|
+
if (page >= 0 && page <= tabs.length) pagerRef.current?.setPage(page);
|
|
86
|
+
},
|
|
87
|
+
}));
|
|
88
|
+
|
|
59
89
|
return (
|
|
60
90
|
<View onLayout={onLayout} style={[styles.tabView, {flex: 1}]}>
|
|
61
91
|
<View>
|
|
@@ -64,7 +94,10 @@ const TabView: FC<TabViewProps> = ({
|
|
|
64
94
|
selectedIndex={selectedIndex}
|
|
65
95
|
tabs={tabs}
|
|
66
96
|
containerWidth={tabBarWidth}
|
|
67
|
-
scrollX={scrollX}
|
|
97
|
+
scrollX={scrollX.current}
|
|
98
|
+
direction={direction}
|
|
99
|
+
selectedColor={selectedColor ?? theme.colors.primary}
|
|
100
|
+
unselectedColor={unselectedColor ?? theme.colors.text.default}
|
|
68
101
|
/>
|
|
69
102
|
</View>
|
|
70
103
|
|
|
@@ -75,11 +108,13 @@ const TabView: FC<TabViewProps> = ({
|
|
|
75
108
|
onPageSelected={onPageSelected}
|
|
76
109
|
style={styles.pagerView}
|
|
77
110
|
initialPage={startPage}>
|
|
78
|
-
{tabs.map(tab => renderScreen(tab))}
|
|
111
|
+
{tabs.map((tab, index) => renderScreen(tab, index))}
|
|
79
112
|
</PagerView>
|
|
80
113
|
</View>
|
|
81
114
|
);
|
|
82
115
|
};
|
|
83
116
|
|
|
117
|
+
const TabView = forwardRef(TabComponent);
|
|
118
|
+
|
|
84
119
|
export {CardTabBar, ScrollableTabBar, TabBar, TabView};
|
|
85
120
|
export type {TabViewProps, Tab};
|
package/package.json
CHANGED
package/publish.sh
CHANGED
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Prepare dist files
|
|
2
4
|
rm -rf dist
|
|
3
5
|
mkdir dist
|
|
4
|
-
|
|
5
|
-
cp . ./dist
|
|
6
|
-
|
|
7
|
-
# GET VERSION from mck_package.json
|
|
8
|
-
VERSIONSTRING=( v$(jq .version package.json) )
|
|
9
|
-
VERSION=(${VERSIONSTRING//[\"]/})
|
|
10
|
-
echo VERSION: $VERSION
|
|
11
|
-
|
|
12
|
-
rsync -r --verbose --exclude '*.mdx' --exclude '*Demo.js' --exclude 'props-type.js' --exclude 'prop-types.js' ./* dist
|
|
13
|
-
|
|
14
|
-
# #babel component to dist
|
|
15
|
-
#babel ./dist -d dist --copy-files
|
|
16
|
-
|
|
17
|
-
#copy option
|
|
18
|
-
#cp -r ./src/ dist
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
#npm login
|
|
22
|
-
#publish dist to npm
|
|
6
|
+
rsync -r --exclude=/dist ./* dist
|
|
23
7
|
cd dist
|
|
24
|
-
|
|
8
|
+
|
|
9
|
+
if [ "$1" == "stable" ]; then
|
|
10
|
+
npm version $(npm view @momo-kits/foundation@stable version)
|
|
11
|
+
npm version patch
|
|
12
|
+
npm publish --tag stable --access=public
|
|
13
|
+
elif [ "$1" == "latest" ]; then
|
|
14
|
+
npm version $(npm view @momo-kits/foundation@latest version)
|
|
15
|
+
npm publish --tag latest --access=public
|
|
16
|
+
else
|
|
17
|
+
npm version $(npm view @momo-kits/tab-view@beta version)
|
|
18
|
+
npm version prerelease --preid=beta
|
|
19
|
+
npm publish --tag beta --access=public
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
PACKAGE_NAME=$(npm pkg get name)
|
|
23
|
+
NEW_PACKAGE_VERSION=$(npm pkg get version)
|
|
24
|
+
|
|
25
|
+
# Clean up
|
|
25
26
|
cd ..
|
|
26
27
|
rm -rf dist
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
##curl -X POST -H 'Content-Type: application/json' 'https://chat.googleapis.com/v1/spaces/AAAAbP8987c/messages?key=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI&token=UGSFRvk_oYb9uGsAgs31bVvMm6jDkmD8zihGm3eyaQA%3D&threadKey=JoaXTEYaNNkl' -d '{"text": "@momo-kits/tab-view new version release: '*"$VERSION"*' https://www.npmjs.com/package/@momo-kits/tab-view"}'
|
package/styles.ts
CHANGED
|
@@ -14,7 +14,6 @@ export default StyleSheet.create({
|
|
|
14
14
|
borderBottomWidth: 1,
|
|
15
15
|
},
|
|
16
16
|
tabItem: {
|
|
17
|
-
padding: Spacing.M,
|
|
18
17
|
alignItems: 'center',
|
|
19
18
|
justifyContent: 'center',
|
|
20
19
|
},
|
|
@@ -26,9 +25,15 @@ export default StyleSheet.create({
|
|
|
26
25
|
bottom: 0,
|
|
27
26
|
},
|
|
28
27
|
icon: {
|
|
28
|
+
borderRadius: Radius.XS,
|
|
29
|
+
},
|
|
30
|
+
iconHolder: {
|
|
31
|
+
marginRight: Spacing.XS,
|
|
29
32
|
width: 24,
|
|
30
33
|
height: 24,
|
|
31
|
-
|
|
34
|
+
justifyContent: 'center',
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
overflow: 'hidden',
|
|
32
37
|
},
|
|
33
38
|
tabItemWrapper: {
|
|
34
39
|
paddingHorizontal: Spacing.XS,
|
|
@@ -47,6 +52,9 @@ export default StyleSheet.create({
|
|
|
47
52
|
flexDirection: 'row',
|
|
48
53
|
justifyContent: 'center',
|
|
49
54
|
alignItems: 'center',
|
|
55
|
+
paddingHorizontal: Spacing.S,
|
|
56
|
+
paddingTop: Spacing.S,
|
|
57
|
+
paddingBottom: Spacing.M,
|
|
50
58
|
},
|
|
51
59
|
cardTabOverlay: {
|
|
52
60
|
flexDirection: 'row',
|
|
@@ -60,19 +68,26 @@ export default StyleSheet.create({
|
|
|
60
68
|
height: '100%',
|
|
61
69
|
alignItems: 'center',
|
|
62
70
|
justifyContent: 'center',
|
|
63
|
-
|
|
71
|
+
paddingVertical: Spacing.M,
|
|
72
|
+
paddingHorizontal: Spacing.S,
|
|
64
73
|
flexDirection: 'row',
|
|
65
74
|
},
|
|
66
75
|
dotSmall: {
|
|
67
76
|
position: 'absolute',
|
|
68
|
-
top: -Spacing.
|
|
69
|
-
right: -Spacing.
|
|
77
|
+
top: -Spacing.XS,
|
|
78
|
+
right: -Spacing.XS,
|
|
70
79
|
zIndex: 2,
|
|
71
80
|
},
|
|
72
81
|
dot: {
|
|
73
82
|
position: 'absolute',
|
|
74
83
|
top: -Spacing.S,
|
|
75
|
-
right: -Spacing.
|
|
84
|
+
right: -Spacing.S,
|
|
85
|
+
zIndex: 2,
|
|
86
|
+
},
|
|
87
|
+
badge: {
|
|
88
|
+
position: 'absolute',
|
|
89
|
+
top: -Spacing.S,
|
|
90
|
+
left: Spacing.S,
|
|
76
91
|
zIndex: 2,
|
|
77
92
|
},
|
|
78
93
|
});
|
package/tabBar/CardTabBar.tsx
CHANGED
|
@@ -4,7 +4,9 @@ import {TabBarProps} from '../types';
|
|
|
4
4
|
import styles from '../styles';
|
|
5
5
|
import {
|
|
6
6
|
ApplicationContext,
|
|
7
|
-
|
|
7
|
+
Badge,
|
|
8
|
+
BadgeDot,
|
|
9
|
+
Icon,
|
|
8
10
|
Radius,
|
|
9
11
|
Shadow,
|
|
10
12
|
Spacing,
|
|
@@ -18,6 +20,8 @@ const CardTabBar: FC<TabBarProps> = ({
|
|
|
18
20
|
onPressTabItem,
|
|
19
21
|
containerWidth,
|
|
20
22
|
selectedIndex,
|
|
23
|
+
selectedColor,
|
|
24
|
+
unselectedColor,
|
|
21
25
|
}) => {
|
|
22
26
|
const {theme} = useContext(ApplicationContext);
|
|
23
27
|
const itemWidth = containerWidth / tabs.length;
|
|
@@ -36,6 +40,8 @@ const CardTabBar: FC<TabBarProps> = ({
|
|
|
36
40
|
onPressTabItem?.(index);
|
|
37
41
|
}}
|
|
38
42
|
width={itemWidth}
|
|
43
|
+
selectedColor={selectedColor}
|
|
44
|
+
unselectedColor={unselectedColor}
|
|
39
45
|
/>
|
|
40
46
|
);
|
|
41
47
|
})}
|
|
@@ -52,6 +58,7 @@ const CardTabBar: FC<TabBarProps> = ({
|
|
|
52
58
|
<PathSvg style={{transform: [{rotateY: '180deg'}], left: 8}} />
|
|
53
59
|
)}
|
|
54
60
|
<View
|
|
61
|
+
accessibilityLabel={tabs[selectedIndex]?.accessibilityLabel}
|
|
55
62
|
style={[
|
|
56
63
|
styles.overlayTextWrapper,
|
|
57
64
|
{
|
|
@@ -59,21 +66,50 @@ const CardTabBar: FC<TabBarProps> = ({
|
|
|
59
66
|
width: containerWidth / tabs.length,
|
|
60
67
|
borderTopRightRadius: Radius.M,
|
|
61
68
|
borderTopLeftRadius: Radius.M,
|
|
69
|
+
paddingHorizontal: tabs.length === 3 ? Spacing.S : Spacing.M,
|
|
62
70
|
},
|
|
63
71
|
]}>
|
|
64
|
-
{!!tabs[selectedIndex]?.
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
{!!tabs[selectedIndex]?.renderIcon &&
|
|
73
|
+
typeof tabs[selectedIndex]?.renderIcon === 'function' && (
|
|
74
|
+
<View
|
|
75
|
+
style={[
|
|
76
|
+
styles.icon,
|
|
77
|
+
styles.iconHolder,
|
|
78
|
+
{marginRight: Spacing.XS},
|
|
79
|
+
]}>
|
|
80
|
+
{tabs[selectedIndex]?.renderIcon?.(true)}
|
|
81
|
+
</View>
|
|
82
|
+
)}
|
|
83
|
+
{!tabs[selectedIndex]?.renderIcon && !!tabs[selectedIndex]?.icon && (
|
|
84
|
+
<Icon
|
|
85
|
+
color={selectedColor}
|
|
86
|
+
source={tabs[selectedIndex]?.icon as string}
|
|
67
87
|
style={[styles.icon, {marginRight: Spacing.XS}]}
|
|
68
88
|
/>
|
|
69
89
|
)}
|
|
70
90
|
<Text
|
|
71
|
-
color={
|
|
91
|
+
color={selectedColor}
|
|
72
92
|
numberOfLines={1}
|
|
73
|
-
style={styles.textCenter}
|
|
93
|
+
style={[styles.textCenter, {flexShrink: 1}]}
|
|
74
94
|
typography={'header_s_semibold'}>
|
|
75
95
|
{tabs[selectedIndex]?.title || ''}
|
|
76
96
|
</Text>
|
|
97
|
+
{tabs[selectedIndex]?.showDot && (
|
|
98
|
+
<BadgeDot
|
|
99
|
+
size={tabs[selectedIndex]?.dotSize ?? 'small'}
|
|
100
|
+
style={{
|
|
101
|
+
marginLeft: Spacing.XS,
|
|
102
|
+
}}
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
105
|
+
{!tabs[selectedIndex]?.showDot && tabs[selectedIndex]?.badgeValue && (
|
|
106
|
+
<View>
|
|
107
|
+
<Badge
|
|
108
|
+
label={tabs[selectedIndex]?.badgeValue}
|
|
109
|
+
style={{marginLeft: Spacing.XS}}
|
|
110
|
+
/>
|
|
111
|
+
</View>
|
|
112
|
+
)}
|
|
77
113
|
</View>
|
|
78
114
|
{!isLast && <PathSvg style={{right: 8}} />}
|
|
79
115
|
</View>
|
|
@@ -9,12 +9,16 @@ const ScrollableTabBar: FC<TabBarProps> = ({
|
|
|
9
9
|
tabs = [],
|
|
10
10
|
onPressTabItem,
|
|
11
11
|
selectedIndex,
|
|
12
|
+
selectedColor,
|
|
13
|
+
unselectedColor,
|
|
14
|
+
direction,
|
|
12
15
|
}) => {
|
|
13
16
|
const {theme} = useContext(ApplicationContext);
|
|
14
17
|
const [itemWidthMap, setItemWidthMap] = useState<
|
|
15
18
|
{width: number; x: number}[]
|
|
16
19
|
>([]);
|
|
17
20
|
const animatedWidth = useRef(new Animated.Value(0)).current;
|
|
21
|
+
const animX = useRef(new Animated.Value(0)).current;
|
|
18
22
|
const scrollViewRef = useRef<ScrollView>(null);
|
|
19
23
|
|
|
20
24
|
useEffect(() => {
|
|
@@ -22,11 +26,18 @@ const ScrollableTabBar: FC<TabBarProps> = ({
|
|
|
22
26
|
scrollToIndex(selectedIndex);
|
|
23
27
|
}, [selectedIndex, itemWidthMap]);
|
|
24
28
|
const moveIndicator = () => {
|
|
25
|
-
Animated.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
Animated.parallel([
|
|
30
|
+
Animated.timing(animatedWidth, {
|
|
31
|
+
toValue: itemWidthMap[selectedIndex]?.width || 0,
|
|
32
|
+
duration: 250,
|
|
33
|
+
useNativeDriver: false,
|
|
34
|
+
}),
|
|
35
|
+
Animated.timing(animX, {
|
|
36
|
+
toValue: itemWidthMap[selectedIndex]?.x || 0,
|
|
37
|
+
duration: 250,
|
|
38
|
+
useNativeDriver: false,
|
|
39
|
+
}),
|
|
40
|
+
]).start();
|
|
30
41
|
};
|
|
31
42
|
|
|
32
43
|
const onLayout = (e: LayoutChangeEvent, index: number) => {
|
|
@@ -62,6 +73,9 @@ const ScrollableTabBar: FC<TabBarProps> = ({
|
|
|
62
73
|
}}
|
|
63
74
|
active={isActive}
|
|
64
75
|
tab={item}
|
|
76
|
+
selectedColor={selectedColor}
|
|
77
|
+
unselectedColor={unselectedColor}
|
|
78
|
+
direction={direction}
|
|
65
79
|
/>
|
|
66
80
|
</View>
|
|
67
81
|
);
|
|
@@ -84,9 +98,9 @@ const ScrollableTabBar: FC<TabBarProps> = ({
|
|
|
84
98
|
style={[
|
|
85
99
|
styles.indicator,
|
|
86
100
|
{
|
|
87
|
-
left:
|
|
101
|
+
left: animX,
|
|
88
102
|
width: animatedWidth,
|
|
89
|
-
backgroundColor:
|
|
103
|
+
backgroundColor: selectedColor,
|
|
90
104
|
},
|
|
91
105
|
]}
|
|
92
106
|
/>
|
package/tabBar/TabBar.tsx
CHANGED
|
@@ -11,9 +11,11 @@ const TabBar: FC<TabBarProps> = ({
|
|
|
11
11
|
onPressTabItem,
|
|
12
12
|
containerWidth = 0,
|
|
13
13
|
scrollX,
|
|
14
|
+
direction,
|
|
15
|
+
selectedColor,
|
|
16
|
+
unselectedColor,
|
|
14
17
|
}) => {
|
|
15
18
|
const {theme} = useContext(ApplicationContext);
|
|
16
|
-
|
|
17
19
|
const itemWidth = containerWidth / tabs.length;
|
|
18
20
|
|
|
19
21
|
return (
|
|
@@ -35,6 +37,9 @@ const TabBar: FC<TabBarProps> = ({
|
|
|
35
37
|
}}
|
|
36
38
|
active={selectedIndex === index}
|
|
37
39
|
tab={tab}
|
|
40
|
+
direction={direction}
|
|
41
|
+
selectedColor={selectedColor}
|
|
42
|
+
unselectedColor={unselectedColor}
|
|
38
43
|
/>
|
|
39
44
|
);
|
|
40
45
|
})}
|
|
@@ -42,9 +47,16 @@ const TabBar: FC<TabBarProps> = ({
|
|
|
42
47
|
style={[
|
|
43
48
|
styles.indicator,
|
|
44
49
|
{
|
|
45
|
-
left: scrollX
|
|
50
|
+
left: scrollX.interpolate({
|
|
51
|
+
inputRange: [0, tabs.length - 1],
|
|
52
|
+
outputRange: [
|
|
53
|
+
Spacing.XS,
|
|
54
|
+
(tabs.length - 1) * itemWidth + Spacing.XS,
|
|
55
|
+
],
|
|
56
|
+
extrapolate: 'clamp',
|
|
57
|
+
}),
|
|
46
58
|
width: itemWidth - Spacing.XS * 2,
|
|
47
|
-
backgroundColor:
|
|
59
|
+
backgroundColor: selectedColor,
|
|
48
60
|
},
|
|
49
61
|
]}
|
|
50
62
|
/>
|
package/tabItem/CardTabItem.tsx
CHANGED
|
@@ -1,28 +1,66 @@
|
|
|
1
1
|
import React, {FC} from 'react';
|
|
2
|
-
import {TouchableOpacity} from 'react-native';
|
|
2
|
+
import {TouchableOpacity, View} from 'react-native';
|
|
3
3
|
import {TabItemProps} from '../types';
|
|
4
4
|
import styles from '../styles';
|
|
5
|
-
import {
|
|
5
|
+
import {Badge, BadgeDot, Icon, Spacing, Text} from '@momo-kits/foundation';
|
|
6
6
|
|
|
7
|
-
const CardTabItem: FC<TabItemProps> = ({
|
|
8
|
-
|
|
7
|
+
const CardTabItem: FC<TabItemProps> = ({
|
|
8
|
+
tab,
|
|
9
|
+
active,
|
|
10
|
+
onPressTabItem,
|
|
11
|
+
width,
|
|
12
|
+
selectedColor,
|
|
13
|
+
unselectedColor,
|
|
14
|
+
}) => {
|
|
15
|
+
const {
|
|
16
|
+
title,
|
|
17
|
+
icon,
|
|
18
|
+
renderIcon,
|
|
19
|
+
showDot = false,
|
|
20
|
+
dotSize = 'small',
|
|
21
|
+
badgeValue,
|
|
22
|
+
accessibilityLabel,
|
|
23
|
+
} = tab;
|
|
24
|
+
const color = active ? selectedColor : unselectedColor;
|
|
9
25
|
|
|
10
26
|
return (
|
|
11
27
|
<TouchableOpacity
|
|
12
28
|
onPress={onPressTabItem}
|
|
13
|
-
style={[styles.cardTabItem, {width}]}
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
-
|
|
29
|
+
style={[styles.cardTabItem, {width}]}
|
|
30
|
+
accessibilityLabel={accessibilityLabel}>
|
|
31
|
+
{renderIcon &&
|
|
32
|
+
!['string', 'boolean', 'number'].includes(typeof renderIcon) && (
|
|
33
|
+
<View
|
|
34
|
+
style={[styles.icon, styles.iconHolder, {marginRight: Spacing.XS}]}>
|
|
35
|
+
{renderIcon}
|
|
36
|
+
</View>
|
|
37
|
+
)}
|
|
38
|
+
{!renderIcon && !!icon && (
|
|
39
|
+
<Icon
|
|
40
|
+
color={color}
|
|
17
41
|
style={[styles.icon, {marginRight: Spacing.XS}]}
|
|
42
|
+
source={icon}
|
|
18
43
|
/>
|
|
19
44
|
)}
|
|
20
45
|
<Text
|
|
21
46
|
numberOfLines={1}
|
|
22
|
-
style={styles.textCenter}
|
|
47
|
+
style={[styles.textCenter, {flexShrink: 1}]}
|
|
23
48
|
typography={'body_default_regular'}>
|
|
24
49
|
{title}
|
|
25
50
|
</Text>
|
|
51
|
+
{showDot && (
|
|
52
|
+
<BadgeDot
|
|
53
|
+
size={dotSize}
|
|
54
|
+
style={{
|
|
55
|
+
marginLeft: Spacing.XS,
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
{!showDot && badgeValue && (
|
|
60
|
+
<View>
|
|
61
|
+
<Badge label={badgeValue} style={{marginLeft: Spacing.XS}} />
|
|
62
|
+
</View>
|
|
63
|
+
)}
|
|
26
64
|
</TouchableOpacity>
|
|
27
65
|
);
|
|
28
66
|
};
|
package/tabItem/TabItem.tsx
CHANGED
|
@@ -1,58 +1,143 @@
|
|
|
1
|
-
import React, {FC
|
|
2
|
-
import {
|
|
3
|
-
import {TouchableOpacity, View, ViewStyle} from 'react-native';
|
|
1
|
+
import React, {FC} from 'react';
|
|
2
|
+
import {TouchableOpacity, View} from 'react-native';
|
|
4
3
|
import {TabItemProps} from '../types';
|
|
5
4
|
import {
|
|
6
|
-
|
|
5
|
+
Badge,
|
|
7
6
|
BadgeDot,
|
|
8
|
-
|
|
7
|
+
Icon,
|
|
8
|
+
scaleSize,
|
|
9
9
|
Spacing,
|
|
10
10
|
Text,
|
|
11
11
|
Typography,
|
|
12
12
|
} from '@momo-kits/foundation';
|
|
13
13
|
import styles from '../styles';
|
|
14
14
|
|
|
15
|
-
const TabItem: FC<TabItemProps> = ({
|
|
16
|
-
|
|
15
|
+
const TabItem: FC<TabItemProps> = ({
|
|
16
|
+
tab,
|
|
17
|
+
active,
|
|
18
|
+
onPressTabItem,
|
|
19
|
+
width,
|
|
20
|
+
direction,
|
|
21
|
+
selectedColor,
|
|
22
|
+
unselectedColor,
|
|
23
|
+
}) => {
|
|
17
24
|
const {
|
|
18
25
|
title,
|
|
19
26
|
icon,
|
|
20
|
-
direction = 'row',
|
|
21
27
|
showDot = false,
|
|
22
28
|
dotSize = 'small',
|
|
29
|
+
badgeValue,
|
|
30
|
+
renderIcon,
|
|
31
|
+
accessibilityLabel,
|
|
23
32
|
} = tab;
|
|
24
33
|
const typography: Typography = active
|
|
25
34
|
? 'header_s_semibold'
|
|
26
35
|
: 'body_default_regular';
|
|
27
|
-
const color = active ?
|
|
36
|
+
const color = active ? selectedColor : unselectedColor;
|
|
28
37
|
const dotStyle = dotSize === 'large' ? styles.dot : styles.dotSmall;
|
|
29
|
-
const iconStyle: ViewStyle =
|
|
30
|
-
direction === 'row'
|
|
31
|
-
? {marginRight: Spacing.XS}
|
|
32
|
-
: {marginBottom: Spacing.XS};
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const renderRowItem = () => {
|
|
40
|
+
return (
|
|
41
|
+
<TouchableOpacity
|
|
42
|
+
accessibilityLabel={accessibilityLabel}
|
|
43
|
+
style={[
|
|
44
|
+
styles.tabItem,
|
|
45
|
+
{
|
|
46
|
+
width,
|
|
47
|
+
height: scaleSize(48),
|
|
48
|
+
flexDirection: 'row',
|
|
49
|
+
overflow: 'hidden',
|
|
50
|
+
paddingVertical: Spacing.M,
|
|
51
|
+
paddingHorizontal: Spacing.M,
|
|
52
|
+
},
|
|
53
|
+
]}
|
|
54
|
+
onPress={onPressTabItem}>
|
|
55
|
+
{renderIcon && typeof renderIcon === 'function' && (
|
|
56
|
+
<View
|
|
57
|
+
style={[styles.icon, styles.iconHolder, {marginRight: Spacing.S}]}>
|
|
58
|
+
{renderIcon(active)}
|
|
59
|
+
</View>
|
|
60
|
+
)}
|
|
61
|
+
{!renderIcon && !!icon && (
|
|
62
|
+
<Icon
|
|
63
|
+
style={[styles.icon, {marginRight: Spacing.S}]}
|
|
64
|
+
source={icon}
|
|
65
|
+
color={color}
|
|
44
66
|
/>
|
|
67
|
+
)}
|
|
68
|
+
<Text
|
|
69
|
+
numberOfLines={1}
|
|
70
|
+
typography={typography}
|
|
71
|
+
color={color}
|
|
72
|
+
style={{flexShrink: 1}}>
|
|
73
|
+
{title}
|
|
74
|
+
</Text>
|
|
75
|
+
{showDot && (
|
|
76
|
+
<BadgeDot size={dotSize} style={{marginLeft: Spacing.XS}} />
|
|
77
|
+
)}
|
|
78
|
+
{!showDot && badgeValue && (
|
|
79
|
+
<View>
|
|
80
|
+
<Badge label={badgeValue} style={{marginLeft: Spacing.XS}} />
|
|
81
|
+
</View>
|
|
82
|
+
)}
|
|
83
|
+
</TouchableOpacity>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const renderColumnItem = () => {
|
|
88
|
+
return (
|
|
89
|
+
<TouchableOpacity
|
|
90
|
+
style={[
|
|
91
|
+
styles.tabItem,
|
|
92
|
+
{
|
|
93
|
+
width,
|
|
94
|
+
height: scaleSize(68),
|
|
95
|
+
flexDirection: 'column',
|
|
96
|
+
overflow: 'hidden',
|
|
97
|
+
paddingHorizontal: Spacing.M,
|
|
98
|
+
paddingVertical: Spacing.M,
|
|
99
|
+
},
|
|
100
|
+
]}
|
|
101
|
+
onPress={onPressTabItem}
|
|
102
|
+
accessibilityLabel={accessibilityLabel}>
|
|
103
|
+
<View>
|
|
104
|
+
{renderIcon && typeof renderIcon === 'function' && (
|
|
105
|
+
<View
|
|
106
|
+
style={[
|
|
107
|
+
styles.icon,
|
|
108
|
+
styles.iconHolder,
|
|
109
|
+
{marginBottom: Spacing.XS},
|
|
110
|
+
]}>
|
|
111
|
+
{renderIcon(active)}
|
|
112
|
+
</View>
|
|
113
|
+
)}
|
|
114
|
+
{!renderIcon && !!icon && (
|
|
115
|
+
<Icon
|
|
116
|
+
style={[
|
|
117
|
+
styles.icon,
|
|
118
|
+
{
|
|
119
|
+
marginBottom: Spacing.XS,
|
|
120
|
+
},
|
|
121
|
+
]}
|
|
122
|
+
source={icon}
|
|
123
|
+
color={color}
|
|
124
|
+
/>
|
|
125
|
+
)}
|
|
126
|
+
{(renderIcon || !!icon) && showDot && (
|
|
127
|
+
<BadgeDot style={dotStyle} size={dotSize} />
|
|
128
|
+
)}
|
|
129
|
+
{(renderIcon || !!icon) && !showDot && badgeValue && (
|
|
130
|
+
<Badge label={badgeValue} style={styles.badge} />
|
|
131
|
+
)}
|
|
45
132
|
</View>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
</TouchableOpacity>
|
|
55
|
-
);
|
|
133
|
+
<Text numberOfLines={1} typography={typography} color={color}>
|
|
134
|
+
{title}
|
|
135
|
+
</Text>
|
|
136
|
+
</TouchableOpacity>
|
|
137
|
+
);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return direction === 'row' ? renderRowItem() : renderColumnItem();
|
|
56
141
|
};
|
|
57
142
|
|
|
58
143
|
export default TabItem;
|
package/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {ReactElement} from 'react';
|
|
2
2
|
import {PagerViewProps} from 'react-native-pager-view';
|
|
3
|
+
import {Animated} from 'react-native';
|
|
3
4
|
|
|
4
5
|
export type Tab = {
|
|
5
6
|
/**
|
|
@@ -12,10 +13,7 @@ export type Tab = {
|
|
|
12
13
|
*/
|
|
13
14
|
icon?: string;
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
* Optional. Specifies the layout direction for the tab's content, either in a row or column format.
|
|
17
|
-
*/
|
|
18
|
-
direction?: 'row' | 'column';
|
|
16
|
+
renderIcon?: (active: boolean) => ReactElement;
|
|
19
17
|
|
|
20
18
|
/**
|
|
21
19
|
* The main component or content to be displayed when this tab is active.
|
|
@@ -31,6 +29,10 @@ export type Tab = {
|
|
|
31
29
|
* Optional. Specifies the size of the dot indicator, either 'small' or 'large'.
|
|
32
30
|
*/
|
|
33
31
|
dotSize?: 'small' | 'large';
|
|
32
|
+
|
|
33
|
+
badgeValue?: string | number;
|
|
34
|
+
|
|
35
|
+
accessibilityLabel?: string;
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
export type TabItemProps = {
|
|
@@ -53,6 +55,15 @@ export type TabItemProps = {
|
|
|
53
55
|
* Optional. Specifies the width of the tab item. Useful for customizing layout and appearance.
|
|
54
56
|
*/
|
|
55
57
|
width?: number;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Optional. Specifies the layout direction for the tab's content, either in a row or column format.
|
|
61
|
+
*/
|
|
62
|
+
direction?: 'row' | 'column';
|
|
63
|
+
|
|
64
|
+
selectedColor?: string;
|
|
65
|
+
|
|
66
|
+
unselectedColor?: string;
|
|
56
67
|
};
|
|
57
68
|
|
|
58
69
|
export type TabViewProps = {
|
|
@@ -85,6 +96,15 @@ export type TabViewProps = {
|
|
|
85
96
|
* Optional. Additional properties to pass to the underlying pager view component.
|
|
86
97
|
*/
|
|
87
98
|
pagerProps?: PagerViewProps;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Optional. Specifies the layout direction for the tab's content, either in a row or column format.
|
|
102
|
+
*/
|
|
103
|
+
direction?: 'row' | 'column';
|
|
104
|
+
|
|
105
|
+
selectedColor?: string;
|
|
106
|
+
|
|
107
|
+
unselectedColor?: string;
|
|
88
108
|
};
|
|
89
109
|
|
|
90
110
|
/**
|
|
@@ -114,5 +134,18 @@ export type TabBarProps = {
|
|
|
114
134
|
/**
|
|
115
135
|
* Represents the horizontal scroll position in a scrollable tab bar.
|
|
116
136
|
*/
|
|
117
|
-
scrollX:
|
|
137
|
+
scrollX: Animated.Value;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Optional. Specifies the layout direction for the tab's content, either in a row or column format.
|
|
141
|
+
*/
|
|
142
|
+
direction?: 'row' | 'column';
|
|
143
|
+
|
|
144
|
+
selectedColor?: string;
|
|
145
|
+
|
|
146
|
+
unselectedColor?: string;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export type TabViewRef = {
|
|
150
|
+
goToPage: (page: number) => void;
|
|
118
151
|
};
|