@momo-kits/tab-view 0.0.55-alpha.31 → 0.74.2-react-native.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/assets/Path.tsx +16 -0
- package/index.tsx +80 -10
- package/package.json +11 -13
- package/publish.sh +2 -2
- package/styles.ts +78 -0
- package/tabBar/CardTabBar.tsx +84 -0
- package/tabBar/SrollableTabBar.tsx +97 -0
- package/tabBar/TabBar.tsx +55 -0
- package/tabItem/CardTabItem.tsx +30 -0
- package/tabItem/TabItem.tsx +58 -0
- package/types.ts +118 -0
- package/Pager.android.tsx +0 -1
- package/Pager.ios.tsx +0 -1
- package/Pager.tsx +0 -1
- package/PagerViewAdapter.tsx +0 -160
- package/PanResponderAdapter.tsx +0 -331
- package/PlatformPressable.tsx +0 -45
- package/SceneMap.tsx +0 -25
- package/SceneView.tsx +0 -142
- package/TabBar.tsx +0 -559
- package/TabBarIndicator.tsx +0 -160
- package/TabBarItem.tsx +0 -296
- package/TabView.tsx +0 -156
- package/types.tsx +0 -58
- package/useAnimatedValue.tsx +0 -12
package/assets/Path.tsx
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React, {FC} from 'react';
|
|
2
|
+
import {ViewProps} from 'react-native';
|
|
3
|
+
import Svg, {Path} from 'react-native-svg';
|
|
4
|
+
|
|
5
|
+
const PathSvg: FC<ViewProps> = ({style}) => {
|
|
6
|
+
return (
|
|
7
|
+
<Svg width="28" height="44" viewBox="0 0 28 44" fill="none" style={style}>
|
|
8
|
+
<Path
|
|
9
|
+
d="M0 0H0.60765C6.47582 0 11.4819 4.23831 12.4602 10.0244C13.9248 18.6872 15.9294 30.37 16.6925 34C17.9536 40 22.259 44 27.9996 44H0V0Z"
|
|
10
|
+
fill="white"
|
|
11
|
+
/>
|
|
12
|
+
</Svg>
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default PathSvg;
|
package/index.tsx
CHANGED
|
@@ -1,15 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React, {FC, useCallback, useRef, useState} from 'react';
|
|
2
|
+
import {LayoutChangeEvent, View} from 'react-native';
|
|
3
|
+
import {Tab, TabViewProps} from './types';
|
|
4
|
+
import TabBar from './tabBar/TabBar';
|
|
5
|
+
import styles from './styles';
|
|
6
|
+
import CardTabBar from './tabBar/CardTabBar';
|
|
7
|
+
import ScrollableTabBar from './tabBar/SrollableTabBar';
|
|
8
|
+
import PagerView from 'react-native-pager-view';
|
|
3
9
|
|
|
4
|
-
|
|
5
|
-
|
|
10
|
+
const TabView: FC<TabViewProps> = ({
|
|
11
|
+
scrollable = false,
|
|
12
|
+
tabs = [],
|
|
13
|
+
onPressTabItem,
|
|
14
|
+
type = 'default',
|
|
15
|
+
initialPage = 0,
|
|
16
|
+
pagerProps,
|
|
17
|
+
}) => {
|
|
18
|
+
const startPage =
|
|
19
|
+
initialPage > tabs.length - 1 ? tabs.length - 1 : initialPage;
|
|
20
|
+
const [selectedIndex, setSelectedIndex] = useState(startPage);
|
|
21
|
+
const [tabBarWidth, setTabBarWidth] = useState(0);
|
|
22
|
+
const isCardTab = type === 'card';
|
|
23
|
+
const pagerRef = useRef<PagerView>(null);
|
|
24
|
+
const [scrollX, setScrollX] = useState(0);
|
|
25
|
+
const _onPressTabItem = (index: number) => {
|
|
26
|
+
pagerRef.current?.setPage(index);
|
|
27
|
+
onPressTabItem?.(index);
|
|
28
|
+
setSelectedIndex(index);
|
|
29
|
+
};
|
|
6
30
|
|
|
7
|
-
|
|
8
|
-
|
|
31
|
+
const onLayout = (e: LayoutChangeEvent) => {
|
|
32
|
+
setTabBarWidth(e.nativeEvent.layout.width);
|
|
33
|
+
};
|
|
9
34
|
|
|
10
|
-
|
|
11
|
-
|
|
35
|
+
const onPageSelected = (e: {
|
|
36
|
+
nativeEvent: {position: React.SetStateAction<number>};
|
|
37
|
+
}) => {
|
|
38
|
+
setSelectedIndex(e.nativeEvent.position);
|
|
39
|
+
};
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
let TabBarComponent = scrollable ? ScrollableTabBar : TabBar;
|
|
14
42
|
|
|
15
|
-
|
|
43
|
+
if (isCardTab) {
|
|
44
|
+
TabBarComponent = CardTabBar;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const onPageScroll = (e: {nativeEvent: {position: any; offset: any}}) => {
|
|
48
|
+
const {position, offset} = e.nativeEvent;
|
|
49
|
+
setScrollX(position + offset);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const renderScreen = useCallback(
|
|
53
|
+
(tab: Tab) => {
|
|
54
|
+
return <View>{tab.component}</View>;
|
|
55
|
+
},
|
|
56
|
+
[tabs],
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<View onLayout={onLayout} style={[styles.tabView, {flex: 1}]}>
|
|
61
|
+
<View>
|
|
62
|
+
<TabBarComponent
|
|
63
|
+
onPressTabItem={_onPressTabItem}
|
|
64
|
+
selectedIndex={selectedIndex}
|
|
65
|
+
tabs={tabs}
|
|
66
|
+
containerWidth={tabBarWidth}
|
|
67
|
+
scrollX={scrollX}
|
|
68
|
+
/>
|
|
69
|
+
</View>
|
|
70
|
+
|
|
71
|
+
<PagerView
|
|
72
|
+
{...pagerProps}
|
|
73
|
+
onPageScroll={onPageScroll}
|
|
74
|
+
ref={pagerRef}
|
|
75
|
+
onPageSelected={onPageSelected}
|
|
76
|
+
style={styles.pagerView}
|
|
77
|
+
initialPage={startPage}>
|
|
78
|
+
{tabs.map(tab => renderScreen(tab))}
|
|
79
|
+
</PagerView>
|
|
80
|
+
</View>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export {CardTabBar, ScrollableTabBar, TabBar, TabView};
|
|
85
|
+
export type {TabViewProps, Tab};
|
package/package.json
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momo-kits/tab-view",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.74.2-react-native.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "index.tsx",
|
|
6
|
-
"dependencies": {
|
|
7
|
-
"@momo-platform/versions": "4.0.2"
|
|
8
|
-
},
|
|
9
6
|
"peerDependencies": {
|
|
10
|
-
"@momo-kits/
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"prop-types": "
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
"@momo-kits/foundation": "latest",
|
|
8
|
+
"react": "*",
|
|
9
|
+
"react-native": "*",
|
|
10
|
+
"prop-types": "15.7.2"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@momo-platform/versions": "4.1.11"
|
|
16
14
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
}
|
|
15
|
+
"license": "MoMo",
|
|
16
|
+
"dependencies": {}
|
|
17
|
+
}
|
package/publish.sh
CHANGED
|
@@ -9,7 +9,7 @@ VERSIONSTRING=( v$(jq .version package.json) )
|
|
|
9
9
|
VERSION=(${VERSIONSTRING//[\"]/})
|
|
10
10
|
echo VERSION: $VERSION
|
|
11
11
|
|
|
12
|
-
rsync -r --verbose --exclude '*.mdx' --exclude '*Demo.js' --exclude 'props-type.js' --exclude 'prop-types.js'
|
|
12
|
+
rsync -r --verbose --exclude '*.mdx' --exclude '*Demo.js' --exclude 'props-type.js' --exclude 'prop-types.js' ./* dist
|
|
13
13
|
|
|
14
14
|
# #babel component to dist
|
|
15
15
|
#babel ./dist -d dist --copy-files
|
|
@@ -26,4 +26,4 @@ cd ..
|
|
|
26
26
|
rm -rf dist
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
|
|
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
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {StyleSheet} from 'react-native';
|
|
2
|
+
import {Colors, Radius, Spacing} from '@momo-kits/foundation';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
pagerView: {flex: 1},
|
|
6
|
+
textCenter: {
|
|
7
|
+
textAlign: 'center',
|
|
8
|
+
},
|
|
9
|
+
tabView: {
|
|
10
|
+
width: '100%',
|
|
11
|
+
},
|
|
12
|
+
tabBar: {
|
|
13
|
+
flexDirection: 'row',
|
|
14
|
+
borderBottomWidth: 1,
|
|
15
|
+
},
|
|
16
|
+
tabItem: {
|
|
17
|
+
padding: Spacing.M,
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
justifyContent: 'center',
|
|
20
|
+
},
|
|
21
|
+
indicator: {
|
|
22
|
+
height: 2,
|
|
23
|
+
borderTopLeftRadius: Radius.XXS,
|
|
24
|
+
borderTopRightRadius: Radius.XXS,
|
|
25
|
+
position: 'absolute',
|
|
26
|
+
bottom: 0,
|
|
27
|
+
},
|
|
28
|
+
icon: {
|
|
29
|
+
width: 24,
|
|
30
|
+
height: 24,
|
|
31
|
+
borderRadius: Radius.XS,
|
|
32
|
+
},
|
|
33
|
+
tabItemWrapper: {
|
|
34
|
+
paddingHorizontal: Spacing.XS,
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
},
|
|
37
|
+
cardTabBar: {
|
|
38
|
+
flexDirection: 'row',
|
|
39
|
+
marginTop: Spacing.XS,
|
|
40
|
+
alignItems: 'flex-end',
|
|
41
|
+
},
|
|
42
|
+
cardTabItem: {
|
|
43
|
+
borderTopLeftRadius: Spacing.M,
|
|
44
|
+
borderTopRightRadius: Spacing.M,
|
|
45
|
+
height: 40,
|
|
46
|
+
backgroundColor: Colors.black_02,
|
|
47
|
+
flexDirection: 'row',
|
|
48
|
+
justifyContent: 'center',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
},
|
|
51
|
+
cardTabOverlay: {
|
|
52
|
+
flexDirection: 'row',
|
|
53
|
+
height: 44,
|
|
54
|
+
position: 'absolute',
|
|
55
|
+
top: -Spacing.XS,
|
|
56
|
+
borderTopLeftRadius: Spacing.M,
|
|
57
|
+
borderTopRightRadius: Spacing.M,
|
|
58
|
+
},
|
|
59
|
+
overlayTextWrapper: {
|
|
60
|
+
height: '100%',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
justifyContent: 'center',
|
|
63
|
+
paddingTop: Spacing.XS,
|
|
64
|
+
flexDirection: 'row',
|
|
65
|
+
},
|
|
66
|
+
dotSmall: {
|
|
67
|
+
position: 'absolute',
|
|
68
|
+
top: -Spacing.XXS,
|
|
69
|
+
right: -Spacing.XXS,
|
|
70
|
+
zIndex: 2,
|
|
71
|
+
},
|
|
72
|
+
dot: {
|
|
73
|
+
position: 'absolute',
|
|
74
|
+
top: -Spacing.S,
|
|
75
|
+
right: -Spacing.XS,
|
|
76
|
+
zIndex: 2,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, {FC, useContext} from 'react';
|
|
2
|
+
import {Platform, View} from 'react-native';
|
|
3
|
+
import {TabBarProps} from '../types';
|
|
4
|
+
import styles from '../styles';
|
|
5
|
+
import {
|
|
6
|
+
ApplicationContext,
|
|
7
|
+
Image,
|
|
8
|
+
Radius,
|
|
9
|
+
Shadow,
|
|
10
|
+
Spacing,
|
|
11
|
+
Text,
|
|
12
|
+
} from '@momo-kits/foundation';
|
|
13
|
+
import CardTabItem from '../tabItem/CardTabItem';
|
|
14
|
+
import PathSvg from '../assets/Path';
|
|
15
|
+
|
|
16
|
+
const CardTabBar: FC<TabBarProps> = ({
|
|
17
|
+
tabs,
|
|
18
|
+
onPressTabItem,
|
|
19
|
+
containerWidth,
|
|
20
|
+
selectedIndex,
|
|
21
|
+
}) => {
|
|
22
|
+
const {theme} = useContext(ApplicationContext);
|
|
23
|
+
const itemWidth = containerWidth / tabs.length;
|
|
24
|
+
const isFirst = selectedIndex === 0;
|
|
25
|
+
const isLast = selectedIndex === tabs.length - 1;
|
|
26
|
+
const backSpace = isFirst ? 0 : 28;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<View style={styles.cardTabBar}>
|
|
30
|
+
{tabs.map((tab, index) => {
|
|
31
|
+
return (
|
|
32
|
+
<CardTabItem
|
|
33
|
+
tab={tab}
|
|
34
|
+
active={selectedIndex === index}
|
|
35
|
+
onPressTabItem={() => {
|
|
36
|
+
onPressTabItem?.(index);
|
|
37
|
+
}}
|
|
38
|
+
width={itemWidth}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
})}
|
|
42
|
+
<View
|
|
43
|
+
style={[
|
|
44
|
+
styles.cardTabOverlay,
|
|
45
|
+
Platform.OS === 'ios' && Shadow.Light,
|
|
46
|
+
{
|
|
47
|
+
width: containerWidth / tabs.length,
|
|
48
|
+
left: itemWidth * selectedIndex - backSpace,
|
|
49
|
+
},
|
|
50
|
+
]}>
|
|
51
|
+
{!isFirst && (
|
|
52
|
+
<PathSvg style={{transform: [{rotateY: '180deg'}], left: 8}} />
|
|
53
|
+
)}
|
|
54
|
+
<View
|
|
55
|
+
style={[
|
|
56
|
+
styles.overlayTextWrapper,
|
|
57
|
+
{
|
|
58
|
+
backgroundColor: theme.colors.background.surface,
|
|
59
|
+
width: containerWidth / tabs.length,
|
|
60
|
+
borderTopRightRadius: Radius.M,
|
|
61
|
+
borderTopLeftRadius: Radius.M,
|
|
62
|
+
},
|
|
63
|
+
]}>
|
|
64
|
+
{!!tabs[selectedIndex]?.icon && (
|
|
65
|
+
<Image
|
|
66
|
+
source={{uri: tabs[selectedIndex]?.icon}}
|
|
67
|
+
style={[styles.icon, {marginRight: Spacing.XS}]}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
<Text
|
|
71
|
+
color={theme.colors.primary}
|
|
72
|
+
numberOfLines={1}
|
|
73
|
+
style={styles.textCenter}
|
|
74
|
+
typography={'header_s_semibold'}>
|
|
75
|
+
{tabs[selectedIndex]?.title || ''}
|
|
76
|
+
</Text>
|
|
77
|
+
</View>
|
|
78
|
+
{!isLast && <PathSvg style={{right: 8}} />}
|
|
79
|
+
</View>
|
|
80
|
+
</View>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default CardTabBar;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React, {FC, useContext, useEffect, useRef, useState} from 'react';
|
|
2
|
+
import {Animated, LayoutChangeEvent, ScrollView, View} from 'react-native';
|
|
3
|
+
import {Tab, TabBarProps} from '../types';
|
|
4
|
+
import styles from '../styles';
|
|
5
|
+
import {ApplicationContext} from '@momo-kits/foundation';
|
|
6
|
+
import TabItem from '../tabItem/TabItem';
|
|
7
|
+
|
|
8
|
+
const ScrollableTabBar: FC<TabBarProps> = ({
|
|
9
|
+
tabs = [],
|
|
10
|
+
onPressTabItem,
|
|
11
|
+
selectedIndex,
|
|
12
|
+
}) => {
|
|
13
|
+
const {theme} = useContext(ApplicationContext);
|
|
14
|
+
const [itemWidthMap, setItemWidthMap] = useState<
|
|
15
|
+
{width: number; x: number}[]
|
|
16
|
+
>([]);
|
|
17
|
+
const animatedWidth = useRef(new Animated.Value(0)).current;
|
|
18
|
+
const scrollViewRef = useRef<ScrollView>(null);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
moveIndicator();
|
|
22
|
+
scrollToIndex(selectedIndex);
|
|
23
|
+
}, [selectedIndex, itemWidthMap]);
|
|
24
|
+
const moveIndicator = () => {
|
|
25
|
+
Animated.timing(animatedWidth, {
|
|
26
|
+
toValue: itemWidthMap[selectedIndex]?.width || 0,
|
|
27
|
+
duration: 250,
|
|
28
|
+
useNativeDriver: false,
|
|
29
|
+
}).start();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const onLayout = (e: LayoutChangeEvent, index: number) => {
|
|
33
|
+
const {width, x} = e.nativeEvent.layout;
|
|
34
|
+
|
|
35
|
+
const newMap: Array<any> = [...itemWidthMap];
|
|
36
|
+
newMap[index] = {width, x};
|
|
37
|
+
setItemWidthMap(newMap);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const scrollToIndex = (index: number) => {
|
|
41
|
+
scrollViewRef.current?.scrollTo({
|
|
42
|
+
x: itemWidthMap[index]?.x || 0,
|
|
43
|
+
animated: true,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const _onPressTabItem = (index: number) => {
|
|
48
|
+
scrollToIndex(index);
|
|
49
|
+
onPressTabItem?.(index);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const renderItem = (params: {item: Tab; index: number}) => {
|
|
53
|
+
const {item, index} = params;
|
|
54
|
+
const isActive = selectedIndex === index;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<View onLayout={e => onLayout(e, index)} style={styles.tabItemWrapper}>
|
|
58
|
+
<TabItem
|
|
59
|
+
key={`Tab View Item ${item.title}-${index}`}
|
|
60
|
+
onPressTabItem={() => {
|
|
61
|
+
_onPressTabItem(index);
|
|
62
|
+
}}
|
|
63
|
+
active={isActive}
|
|
64
|
+
tab={item}
|
|
65
|
+
/>
|
|
66
|
+
</View>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<ScrollView
|
|
72
|
+
ref={scrollViewRef}
|
|
73
|
+
showsHorizontalScrollIndicator={false}
|
|
74
|
+
style={[
|
|
75
|
+
styles.tabBar,
|
|
76
|
+
{
|
|
77
|
+
borderColor: theme.colors.border.default,
|
|
78
|
+
backgroundColor: theme.colors.background.surface,
|
|
79
|
+
},
|
|
80
|
+
]}
|
|
81
|
+
horizontal>
|
|
82
|
+
{tabs.map((item, index) => renderItem({item, index}))}
|
|
83
|
+
<Animated.View
|
|
84
|
+
style={[
|
|
85
|
+
styles.indicator,
|
|
86
|
+
{
|
|
87
|
+
left: itemWidthMap[selectedIndex]?.x || 0,
|
|
88
|
+
width: animatedWidth,
|
|
89
|
+
backgroundColor: theme.colors.primary,
|
|
90
|
+
},
|
|
91
|
+
]}
|
|
92
|
+
/>
|
|
93
|
+
</ScrollView>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default ScrollableTabBar;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, {FC, useContext} from 'react';
|
|
2
|
+
import {Animated, View} from 'react-native';
|
|
3
|
+
import {TabBarProps} from '../types';
|
|
4
|
+
import TabItem from '../tabItem/TabItem';
|
|
5
|
+
import styles from '../styles';
|
|
6
|
+
import {ApplicationContext, Spacing} from '@momo-kits/foundation';
|
|
7
|
+
|
|
8
|
+
const TabBar: FC<TabBarProps> = ({
|
|
9
|
+
tabs = [],
|
|
10
|
+
selectedIndex,
|
|
11
|
+
onPressTabItem,
|
|
12
|
+
containerWidth = 0,
|
|
13
|
+
scrollX,
|
|
14
|
+
}) => {
|
|
15
|
+
const {theme} = useContext(ApplicationContext);
|
|
16
|
+
|
|
17
|
+
const itemWidth = containerWidth / tabs.length;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<View
|
|
21
|
+
style={[
|
|
22
|
+
styles.tabBar,
|
|
23
|
+
{
|
|
24
|
+
borderColor: theme.colors.border.default,
|
|
25
|
+
backgroundColor: theme.colors.background.surface,
|
|
26
|
+
},
|
|
27
|
+
]}>
|
|
28
|
+
{tabs.map((tab, index) => {
|
|
29
|
+
return (
|
|
30
|
+
<TabItem
|
|
31
|
+
key={`Tab View Item ${tab.title}-${index}`}
|
|
32
|
+
width={itemWidth}
|
|
33
|
+
onPressTabItem={() => {
|
|
34
|
+
onPressTabItem?.(index);
|
|
35
|
+
}}
|
|
36
|
+
active={selectedIndex === index}
|
|
37
|
+
tab={tab}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
})}
|
|
41
|
+
<Animated.View
|
|
42
|
+
style={[
|
|
43
|
+
styles.indicator,
|
|
44
|
+
{
|
|
45
|
+
left: scrollX * itemWidth + Spacing.XS,
|
|
46
|
+
width: itemWidth - Spacing.XS * 2,
|
|
47
|
+
backgroundColor: theme.colors.primary,
|
|
48
|
+
},
|
|
49
|
+
]}
|
|
50
|
+
/>
|
|
51
|
+
</View>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default TabBar;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React, {FC} from 'react';
|
|
2
|
+
import {TouchableOpacity} from 'react-native';
|
|
3
|
+
import {TabItemProps} from '../types';
|
|
4
|
+
import styles from '../styles';
|
|
5
|
+
import {Image, Spacing, Text} from '@momo-kits/foundation';
|
|
6
|
+
|
|
7
|
+
const CardTabItem: FC<TabItemProps> = ({tab, onPressTabItem, width}) => {
|
|
8
|
+
const {title, icon} = tab;
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<TouchableOpacity
|
|
12
|
+
onPress={onPressTabItem}
|
|
13
|
+
style={[styles.cardTabItem, {width}]}>
|
|
14
|
+
{!!icon && (
|
|
15
|
+
<Image
|
|
16
|
+
source={{uri: icon}}
|
|
17
|
+
style={[styles.icon, {marginRight: Spacing.XS}]}
|
|
18
|
+
/>
|
|
19
|
+
)}
|
|
20
|
+
<Text
|
|
21
|
+
numberOfLines={1}
|
|
22
|
+
style={styles.textCenter}
|
|
23
|
+
typography={'body_default_regular'}>
|
|
24
|
+
{title}
|
|
25
|
+
</Text>
|
|
26
|
+
</TouchableOpacity>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default CardTabItem;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, {FC, useContext} from 'react';
|
|
2
|
+
import {ImageStyle} from 'react-native-fast-image';
|
|
3
|
+
import {TouchableOpacity, View, ViewStyle} from 'react-native';
|
|
4
|
+
import {TabItemProps} from '../types';
|
|
5
|
+
import {
|
|
6
|
+
ApplicationContext,
|
|
7
|
+
BadgeDot,
|
|
8
|
+
Image,
|
|
9
|
+
Spacing,
|
|
10
|
+
Text,
|
|
11
|
+
Typography,
|
|
12
|
+
} from '@momo-kits/foundation';
|
|
13
|
+
import styles from '../styles';
|
|
14
|
+
|
|
15
|
+
const TabItem: FC<TabItemProps> = ({tab, active, onPressTabItem, width}) => {
|
|
16
|
+
const {theme} = useContext(ApplicationContext);
|
|
17
|
+
const {
|
|
18
|
+
title,
|
|
19
|
+
icon,
|
|
20
|
+
direction = 'row',
|
|
21
|
+
showDot = false,
|
|
22
|
+
dotSize = 'small',
|
|
23
|
+
} = tab;
|
|
24
|
+
const typography: Typography = active
|
|
25
|
+
? 'header_s_semibold'
|
|
26
|
+
: 'body_default_regular';
|
|
27
|
+
const color = active ? theme.colors.primary : theme.colors.text.default;
|
|
28
|
+
const dotStyle = dotSize === 'large' ? styles.dot : styles.dotSmall;
|
|
29
|
+
const iconStyle: ViewStyle =
|
|
30
|
+
direction === 'row'
|
|
31
|
+
? {marginRight: Spacing.XS}
|
|
32
|
+
: {marginBottom: Spacing.XS};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<TouchableOpacity
|
|
36
|
+
onPress={onPressTabItem}
|
|
37
|
+
style={[styles.tabItem, {width, flexDirection: direction}]}>
|
|
38
|
+
{!!icon && (
|
|
39
|
+
<View>
|
|
40
|
+
{showDot && <BadgeDot style={dotStyle} size={dotSize} />}
|
|
41
|
+
<Image
|
|
42
|
+
style={[styles.icon, iconStyle as ImageStyle]}
|
|
43
|
+
source={{uri: icon}}
|
|
44
|
+
/>
|
|
45
|
+
</View>
|
|
46
|
+
)}
|
|
47
|
+
<Text
|
|
48
|
+
style={styles.textCenter}
|
|
49
|
+
numberOfLines={1}
|
|
50
|
+
color={color}
|
|
51
|
+
typography={typography}>
|
|
52
|
+
{title}
|
|
53
|
+
</Text>
|
|
54
|
+
</TouchableOpacity>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default TabItem;
|
package/types.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {ReactElement} from 'react';
|
|
2
|
+
import {PagerViewProps} from 'react-native-pager-view';
|
|
3
|
+
|
|
4
|
+
export type Tab = {
|
|
5
|
+
/**
|
|
6
|
+
* The title of the tab.
|
|
7
|
+
*/
|
|
8
|
+
title: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Optional. The icon associated with the tab, usually represented by a string referencing the icon's name or path.
|
|
12
|
+
*/
|
|
13
|
+
icon?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Optional. Specifies the layout direction for the tab's content, either in a row or column format.
|
|
17
|
+
*/
|
|
18
|
+
direction?: 'row' | 'column';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The main component or content to be displayed when this tab is active.
|
|
22
|
+
*/
|
|
23
|
+
component: ReactElement;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Optional. If `true`, shows a dot indicator on the tab, often used to signal notifications or updates.
|
|
27
|
+
*/
|
|
28
|
+
showDot?: boolean;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Optional. Specifies the size of the dot indicator, either 'small' or 'large'.
|
|
32
|
+
*/
|
|
33
|
+
dotSize?: 'small' | 'large';
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type TabItemProps = {
|
|
37
|
+
/**
|
|
38
|
+
* The Tab object containing the details and content of this tab item.
|
|
39
|
+
*/
|
|
40
|
+
tab: Tab;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Indicates if this tab item is currently active or selected.
|
|
44
|
+
*/
|
|
45
|
+
active: boolean;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Optional. A callback function that is triggered when this tab item is pressed.
|
|
49
|
+
*/
|
|
50
|
+
onPressTabItem?: () => void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Optional. Specifies the width of the tab item. Useful for customizing layout and appearance.
|
|
54
|
+
*/
|
|
55
|
+
width?: number;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type TabViewProps = {
|
|
59
|
+
/**
|
|
60
|
+
* Optional. If `true`, allows the tab bar to be scrollable, useful for a large number of tabs.
|
|
61
|
+
*/
|
|
62
|
+
scrollable?: boolean;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* An array of Tab objects representing each tab in the view.
|
|
66
|
+
*/
|
|
67
|
+
tabs: Tab[];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Optional. A callback function that is triggered when a tab item is pressed, receiving the index of the tab as a parameter.
|
|
71
|
+
*/
|
|
72
|
+
onPressTabItem?: (index: number) => void;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Optional. Specifies the visual style of the tab view, such as 'default' or 'card'.
|
|
76
|
+
*/
|
|
77
|
+
type?: 'default' | 'card';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Optional. Specifies the initial page or tab to be displayed.
|
|
81
|
+
*/
|
|
82
|
+
initialPage?: number;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Optional. Additional properties to pass to the underlying pager view component.
|
|
86
|
+
*/
|
|
87
|
+
pagerProps?: PagerViewProps;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Properties for the TabBar component, which displays the navigation tabs.
|
|
92
|
+
*/
|
|
93
|
+
export type TabBarProps = {
|
|
94
|
+
/**
|
|
95
|
+
* An array of Tab objects representing each tab in the tab bar.
|
|
96
|
+
*/
|
|
97
|
+
tabs: Tab[];
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The index of the currently selected or active tab.
|
|
101
|
+
*/
|
|
102
|
+
selectedIndex: number;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Optional. A callback function that is triggered when a tab item is pressed, receiving the index of the tab as a parameter.
|
|
106
|
+
*/
|
|
107
|
+
onPressTabItem?: (index: number) => void;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The width of the container in which the tab bar is placed. Useful for layout calculations.
|
|
111
|
+
*/
|
|
112
|
+
containerWidth: number;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Represents the horizontal scroll position in a scrollable tab bar.
|
|
116
|
+
*/
|
|
117
|
+
scrollX: number;
|
|
118
|
+
};
|
package/Pager.android.tsx
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './PagerViewAdapter';
|
package/Pager.ios.tsx
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './PagerViewAdapter';
|
package/Pager.tsx
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './PanResponderAdapter';
|