Skip to content

Commit 34664db

Browse files
committed
Initial commit
0 parents  commit 34664db

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed

index.js

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
'use strict';
2+
3+
var React = require('react-native');
4+
var {
5+
StyleSheet,
6+
Text,
7+
View,
8+
ScrollView,
9+
TouchableOpacity,
10+
PanResponder,
11+
} = React;
12+
13+
var deviceWidth = require('Dimensions').get('window').width;
14+
var rebound = require('rebound');
15+
var precomputeStyle = require('precomputeStyle');
16+
17+
var ScrollableTabView = React.createClass({
18+
componentDidMount() {
19+
this._scrollSpring.setCurrentValue(0);
20+
},
21+
22+
componentWillMount() {
23+
this.currentPage = 0;
24+
25+
// Initialize the spring that will drive animations
26+
this.springSystem = new rebound.SpringSystem();
27+
this._scrollSpring = this.springSystem.createSpring();
28+
var springConfig = this._scrollSpring.getSpringConfig();
29+
springConfig.tension = rebound.OrigamiValueConverter.tensionFromOrigamiValue(25);
30+
springConfig.friction = rebound.OrigamiValueConverter.frictionFromOrigamiValue(8);
31+
this._scrollSpring.setOvershootClampingEnabled(false);
32+
33+
this._scrollSpring.addListener({
34+
onSpringUpdate: () => {
35+
if (!this.scrollView || !this.tabUnderline) { return }
36+
37+
var numberOfTabs = this.props.children.length;
38+
var currentValue = this._scrollSpring.getCurrentValue();
39+
var offsetX = deviceWidth * currentValue;
40+
41+
this.scrollView.setNativeProps(precomputeStyle({
42+
transform: [{translateX: -1 * offsetX}],
43+
}));
44+
45+
this.tabUnderline.setNativeProps(precomputeStyle({
46+
left: offsetX / numberOfTabs
47+
}));
48+
},
49+
});
50+
51+
this._panResponder = PanResponder.create({
52+
// Claim responder if it's a horizontal pan
53+
onMoveShouldSetPanResponder: (e, gestureState) => {
54+
if (Math.abs(gestureState.dx) > Math.abs(gestureState.dy)) {
55+
return true;
56+
}
57+
},
58+
59+
// Touch is released, scroll to the one that you're closest to
60+
onPanResponderRelease: (e, gestureState) => {
61+
var relativeGestureDistance = gestureState.dx / deviceWidth,
62+
lastPageIndex = this.props.children.length - 1,
63+
vx = gestureState.vx;
64+
65+
if (this.currentPage != lastPageIndex && (relativeGestureDistance < -0.5 || (relativeGestureDistance < 0 && vx <= 0.5))) {
66+
this.currentPage = this.currentPage + 1;
67+
} else if (this.currentPage != 0 && (relativeGestureDistance > 0.5 || (relativeGestureDistance > 0 && vx >= 0.5))) {
68+
this.currentPage = this.currentPage - 1;
69+
}
70+
71+
this._scrollSpring.setEndValue(this.currentPage);
72+
},
73+
74+
// Dragging, move the view with the touch
75+
onPanResponderMove: (e, gestureState) => {
76+
var dx = gestureState.dx;
77+
var lastPageIndex = this.props.children.length - 1;
78+
79+
if (this.currentPage == 0 && dx > 0) {
80+
// Don't set the spring if we're on the first page and trying to move before it
81+
} else if (this.currentPage == lastPageIndex && dx < 0) {
82+
// Don't set the spring if we're already on the last page and trying to move to the next
83+
} else {
84+
// This is awkward because when we are scrolling we are offsetting the underlying view
85+
// to the left (-x)
86+
var offsetX = dx - (this.currentPage * deviceWidth);
87+
this._scrollSpring.setCurrentValue(-1 * offsetX / deviceWidth);
88+
}
89+
},
90+
});
91+
},
92+
93+
goToPage(pageNumber) {
94+
this._scrollSpring.setEndValue(pageNumber);
95+
this.props.onChangeTab &&
96+
this.props.onChangeTab({i: pageNumber, ref: this.props.children[pageNumber]});
97+
},
98+
99+
renderTabOption(name, page) {
100+
return (
101+
<TouchableOpacity key={name} onPress={() => this.goToPage(page)}>
102+
<View style={styles.tab}>
103+
<Text>{name}</Text>
104+
</View>
105+
</TouchableOpacity>
106+
);
107+
},
108+
109+
render() {
110+
var numberOfTabs = this.props.children.length;
111+
var tabUnderlineOptions = this.props.tabUnderlineOptions || {};
112+
var tabUnderlineStyle = {
113+
position: 'absolute',
114+
width: deviceWidth / numberOfTabs,
115+
height: tabUnderlineOptions.height || 4,
116+
backgroundColor: tabUnderlineOptions || 'navy',
117+
bottom: 0,
118+
}
119+
120+
var sceneContainerStyle = {
121+
width: deviceWidth * this.props.children.length,
122+
flex: 1,
123+
flexDirection: 'row'
124+
}
125+
126+
return (
127+
<View style={{flex: 1}}>
128+
<View style={styles.tabs}>
129+
{this.props.children.map((child, i) => this.renderTabOption(child.props.tabLabel, i))}
130+
<View style={tabUnderlineStyle}
131+
ref={view => { this.tabUnderline = view; }} />
132+
</View>
133+
134+
<View style={sceneContainerStyle} {...this._panResponder.panHandlers}
135+
ref={view => { this.scrollView = view; }}>
136+
{this.props.children}
137+
</View>
138+
</View>
139+
)
140+
}
141+
});
142+
143+
var styles = StyleSheet.create({
144+
tab: {
145+
flex: 1,
146+
alignItems: 'center',
147+
justifyContent: 'center',
148+
paddingBottom: 10,
149+
},
150+
151+
tabs: {
152+
height: 50,
153+
flexDirection: 'row',
154+
marginTop: 20,
155+
borderWidth: 1,
156+
borderTopWidth: 0,
157+
borderLeftWidth: 0,
158+
borderRightWidth: 0,
159+
borderBottomColor: '#ccc',
160+
},
161+
});
162+
163+
module.exports = ScrollableTabView;

package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "react-native-scrollable-tab-view",
3+
"version": "0.1.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/brentvatne/react-native-scrollable-tab-view.git"
12+
},
13+
"keywords": [
14+
"react-native",
15+
"tab",
16+
"scrollable"
17+
],
18+
"author": "Brent Vatne",
19+
"license": "MIT",
20+
"bugs": {
21+
"url": "https://github.com/brentvatne/react-native-scrollable-tab-view/issues"
22+
},
23+
"homepage": "https://github.com/brentvatne/react-native-scrollable-tab-view#readme"
24+
}

0 commit comments

Comments
 (0)