├── index.js
├── img
├── 1.png
├── 2.png
├── 3.png
└── 4.png
├── .gitignore
├── example
├── demo2.js
└── demo1.js
├── LICENSE
├── README.md
└── tab.js
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./tab');
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vczero/react-native-tab-menu/HEAD/img/1.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vczero/react-native-tab-menu/HEAD/img/2.png
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vczero/react-native-tab-menu/HEAD/img/3.png
--------------------------------------------------------------------------------
/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vczero/react-native-tab-menu/HEAD/img/4.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | logs
5 | *.log
6 |
7 | # Runtime data
8 | pids
9 | *.pid
10 | *.seed
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
19 | .grunt
20 |
21 | # node-waf configuration
22 | .lock-wscript
23 |
24 | # Compiled binary addons (http://nodejs.org/api/addons.html)
25 | build/Release
26 |
27 | # Dependency directory
28 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
29 | node_modules
30 |
31 |
32 | .idea
--------------------------------------------------------------------------------
/example/demo2.js:
--------------------------------------------------------------------------------
1 | var React = require('react-native');
2 | var MenuList = require('./../tab');
3 |
4 | var data = {
5 | "Language": {
6 | "All": ["All"],
7 | "Web Front End": [
8 | "HTML",
9 | "CSS",
10 | "JavaScript"
11 | ],
12 | "Server": [
13 | "Node.js",
14 | "PHP",
15 | "Python",
16 | "Ruby"
17 | ]
18 | },
19 | "Tool":{
20 | "All": ["All"],
21 | "Apple": ["Xcode"],
22 | "Other": ["Sublime Text", "WebStrom",]
23 | }
24 | };
25 |
26 |
27 | var App = React.createClass({
28 | render: function(){
29 | return (
30 |
31 |
32 |
33 | );
34 | },
35 | onPress: function(val){
36 | alert(val);
37 | }
38 | });
39 |
40 |
41 | AppRegistry.registerComponent('app', () => App);
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 vczero
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/example/demo1.js:
--------------------------------------------------------------------------------
1 | var React = require('react-native');
2 | var MenuList = require('./../tab');
3 |
4 | var {
5 | AppRegistry,
6 | StyleSheet,
7 | Text,
8 | View,
9 | ScrollView,
10 | TouchableOpacity,
11 | } = React;
12 |
13 | var data = {
14 | "全部区域": {
15 | "全部区域": ["全部区域"],
16 | "热门商圈": [
17 | "虹桥地区",
18 | "徐家汇地区",
19 | "淮海路商业区",
20 | "静安寺地区",
21 | "上海火车站地区",
22 | "浦东陆家嘴金融贸易区",
23 | "四川北路商业区",
24 | "人民广场地区",
25 | "南翔、安亭汽车城"
26 | ],
27 | "热门行政区": [
28 | "静安区",
29 | "徐汇区",
30 | "长宁区",
31 | "黄埔区",
32 | "虹口区",
33 | "宝山区",
34 | "闸北区"
35 | ]
36 | },
37 | "地铁沿线":{
38 | "地铁全线": ["地铁全线"],
39 | "一号线": ["莘庄站", "外环路站", "莲花路站", "锦江乐园站", "上海南站站", "漕宝路站"],
40 | "二号线": ["浦东国际机场站", "海天三路站", "远东大道站", "凌空路站"]
41 | }
42 | };
43 |
44 |
45 | var App = React.createClass({
46 | render: function(){
47 | return (
48 |
49 |
50 |
51 | );
52 | },
53 | onPress: function(val){
54 | alert(val);
55 | }
56 | });
57 |
58 |
59 | AppRegistry.registerComponent('app', () => App);
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-tab
2 | react-native-tab is a simple module for add a "Tab Menu" to your React Native app.
3 |
4 | ### Features
5 | 
6 | 
7 |
8 | ### Usage
9 |
10 | npm install react-native-tab
11 |
12 | 
13 |
14 |
15 | ##### Demo1:
16 |
17 | var React = require('react-native');
18 | var MenuList = require('react-native-tab');
19 | var {
20 | AppRegistry,
21 | StyleSheet,
22 | Text,
23 | View,
24 | ScrollView,
25 | TouchableOpacity,
26 | } = React;
27 |
28 | var data = {
29 | "Language": {
30 | "All": ["All"],
31 | "Web Front End": [
32 | "HTML",
33 | "CSS",
34 | "JavaScript"
35 | ],
36 | "Server": [
37 | "Node.js",
38 | "PHP",
39 | "Python",
40 | "Ruby"
41 | ]
42 | },
43 | "Tool":{
44 | "All": ["All"],
45 | "Apple": ["Xcode"],
46 | "Other": ["Sublime Text", "WebStrom",]
47 | }
48 | };
49 |
50 |
51 | var App = React.createClass({
52 | render: function(){
53 | return (
54 |
55 |
56 |
57 | );
58 | },
59 | onPress: function(val){
60 | alert(val);
61 | }
62 | });
63 |
64 |
65 | AppRegistry.registerComponent('app', () => App);
66 |
67 |
68 | ##### Demo2:
69 |
70 | var React = require('react-native');
71 | var MenuList = require('react-native-tab');
72 | var {
73 | AppRegistry,
74 | StyleSheet,
75 | Text,
76 | View,
77 | ScrollView,
78 | TouchableOpacity,
79 | } = React;
80 |
81 | var data = {
82 | "全部区域": {
83 | "全部区域": ["全部区域"],
84 | "热门商圈": [
85 | "虹桥地区",
86 | "徐家汇地区",
87 | "淮海路商业区",
88 | "静安寺地区",
89 | "上海火车站地区",
90 | "浦东陆家嘴金融贸易区",
91 | "四川北路商业区",
92 | "人民广场地区",
93 | "南翔、安亭汽车城"
94 | ],
95 | "热门行政区": [
96 | "静安区",
97 | "徐汇区",
98 | "长宁区",
99 | "黄埔区",
100 | "虹口区",
101 | "宝山区",
102 | "闸北区"
103 | ]
104 | },
105 | "地铁沿线":{
106 | "地铁全线": ["地铁全线"],
107 | "一号线": ["莘庄站", "外环路站", "莲花路站", "锦江乐园站", "上海南站站", "漕宝路站"],
108 | "二号线": ["浦东国际机场站", "海天三路站", "远东大道站", "凌空路站"]
109 | }
110 | };
111 |
112 |
113 | var App = React.createClass({
114 | render: function(){
115 | return (
116 |
117 |
118 |
119 | );
120 | },
121 | onPress: function(val){
122 | alert(val);
123 | }
124 | });
125 |
126 |
127 | AppRegistry.registerComponent('app', () => App);
128 |
129 | ### Properties
130 |
131 | + data: 你需要渲染的数据,格式参见demo。
132 | + tabSelected: 默认选中第几个tab,number类型。
133 | + nSelected: 默认选中tab下的二级菜单项,number类型。
--------------------------------------------------------------------------------
/tab.js:
--------------------------------------------------------------------------------
1 | var React = require('react-native');
2 | var {
3 | StyleSheet,
4 | Text,
5 | View,
6 | ScrollView,
7 | TouchableOpacity,
8 | } = React;
9 |
10 |
11 | //设定内置的属性
12 | //选中项,例如:_type_0_2 表示第一个Tab选中,并且第二个Tab中的第三项选中
13 | var prefixType = '_type_';
14 |
15 | //选中项样式,例如:_style_0_2 表示第一个Tab选中,并且第二个Tab中的第三项选中时的样式
16 | var prefixStyle = '_style_';
17 |
18 | //默认左侧选中的背景颜色
19 | var defaultBackgroundColor = {backgroundColor:'#fff'};
20 |
21 | var MenuList = React.createClass({
22 | getInitialState: function(){
23 | var data = this.props.data;
24 | //左侧选择的index
25 | var nSelected = this.props.nSelected;
26 | //头部选择的index
27 | var tabSelected = this.props.tabSelected;
28 | var obj = {};
29 | var kIndex = 0;
30 | for(var k in data){
31 | var childData = data[k];
32 | var cIndex = 0;
33 | for(var c in childData){
34 | var type = prefixType + k + '_' + c;
35 | var style = prefixStyle + k + '_' + c;
36 | obj[type] = false;
37 | obj[style] = {};
38 | //设定默认选中项
39 | if(nSelected === cIndex && tabSelected === kIndex){
40 | obj[type] = true;
41 | obj[style] = defaultBackgroundColor;
42 | }
43 | cIndex++;
44 | }
45 | kIndex++;
46 | }
47 | obj.tabSelected = tabSelected;
48 | obj.nSelected = nSelected;
49 | return obj;
50 | },
51 | render: function(){
52 | var header = this.renderlHeader();
53 | var left = this.renderLeft();
54 | var right = this.renderRight();
55 | return (
56 |
57 |
58 | {header}
59 |
60 |
61 |
62 | {left}
63 |
64 |
65 | {right}
66 |
67 |
68 |
69 |
70 | );
71 | },
72 |
73 | //渲染头部TabBar
74 | renderlHeader: function(){
75 | var data = this.props.data;
76 | var tabSelected = this.state.tabSelected;
77 | var header = [];
78 | var tabIndex = 0;
79 | for(var i in data){
80 | var tabStyle = null;
81 | if(tabIndex === tabSelected){
82 | tabStyle=[styles.header_text, styles.active_blue];
83 | }else{
84 | tabStyle = [styles.header_text];
85 | }
86 | header.push(
87 |
89 | {i}
90 |
91 | );
92 | tabIndex ++;
93 | }
94 | return header;
95 | },
96 |
97 | //渲染左侧
98 | renderLeft: function(){
99 | var data = this.props.data;
100 | var tabSelected = this.state.tabSelected;
101 | var leftPannel = [];
102 | var index = 0;
103 | for(var i in data){
104 | if(index === tabSelected){
105 | for(var k in data[i]){
106 | var style = this.state[prefixStyle + i + '_' + k];
107 | leftPannel.push(
108 | {k});
110 | }
111 | break;
112 | }
113 | index ++;
114 | }
115 | return leftPannel;
116 | },
117 | //渲染右边,二级菜单
118 | renderRight: function(){
119 | var data = this.props.data;
120 | var tabSelected = this.state.tabSelected;
121 | var nSelected = this.state.nSelected;
122 | var index = 0;
123 | var rightPannel = [];
124 | for(var i in data){
125 | if(tabSelected === index ){
126 | for(var k in data[i]){
127 | if(this.state[prefixType + i + '_' + k]){
128 | for(var j in data[i][k]){
129 | rightPannel.push(
130 | {data[i][k][j]});
131 | }
132 | break;
133 | }
134 | }
135 | }
136 | index ++;
137 | }
138 | return rightPannel;
139 | },
140 | //点击左侧,展示右侧二级菜单
141 | leftPress: function(tabIndex, nIndex){
142 | var obj = {};
143 | for(var k in this.state){
144 | //将prefixType或者prefixStyle类型全部置false
145 | if(k.indexOf(prefixType) > -1){
146 | var obj = {};
147 | obj[k] = false;
148 | this.setState(obj);
149 | }
150 | if(k.indexOf(prefixStyle) > -1){
151 | var obj = {};
152 | obj[k] = {};
153 | this.setState(obj);
154 | }
155 | }
156 | obj[prefixType + tabIndex + '_' + nIndex] = true;
157 | obj[prefixStyle + tabIndex + '_' + nIndex] = defaultBackgroundColor;
158 | this.setState(obj);
159 | },
160 | //头部点击事件即Tab切换事件
161 | headerPress: function(title){
162 | var data = this.props.data;
163 | var index = 0;
164 | for(var i in data){
165 | if(i === title){
166 | this.setState({
167 | tabSelected: index,
168 | });
169 | var obj = {};
170 | var n = 0;
171 | for(var k in data[i]){
172 | if(n !== 0){
173 | obj[prefixType + i + '_' + k] = false;
174 | obj[prefixStyle + i + '_' + k] = {};
175 | }else{
176 | obj[prefixType + i + '_' + k] = true;
177 | obj[prefixStyle + i + '_' + k] = defaultBackgroundColor;
178 | }
179 | n ++;
180 | }
181 | this.setState(obj);
182 | }
183 | index ++;
184 | }
185 | }
186 | });
187 |
188 | var styles = StyleSheet.create({
189 | container:{
190 | height:240,
191 | flex:1,
192 | borderTopWidth:1,
193 | borderBottomWidth:1,
194 | borderColor:'#ddd'
195 | },
196 | row:{
197 | flexDirection: 'row'
198 | },
199 | flex_1:{
200 | flex:1
201 | },
202 | header:{
203 | height:35,
204 | borderBottomWidth:1,
205 | borderColor:'#DFDFDF',
206 | backgroundColor:'#F5F5F5'
207 | },
208 | header_text:{
209 | color:'#7B7B7B',
210 | fontSize:15
211 | },
212 | center:{
213 | justifyContent:'center',
214 | alignItems:'center'
215 | },
216 | left_pannel:{
217 | backgroundColor:'#F2F2F2',
218 | },
219 | left_row:{
220 | height:30,
221 | lineHeight:20,
222 | fontSize:14,
223 | color:'#7C7C7C',
224 | },
225 | right_pannel:{
226 | marginLeft:10
227 | },
228 | active_blue:{
229 | color: '#00B7EB'
230 | },
231 | active_fff:{
232 | backgroundColor:'#fff'
233 | }
234 | });
235 |
236 | module.exports = MenuList;
237 |
238 |
239 |
240 |
241 |
--------------------------------------------------------------------------------