├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── README.md ├── demo.gif ├── demo2.png ├── jsconfig.json ├── package.json └── src ├── Nav.js ├── Tab.js └── main.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | .*/Libraries/react-native/ReactNative.js 16 | 17 | [include] 18 | 19 | [libs] 20 | node_modules/react-native/Libraries/react-native/react-native-interface.js 21 | node_modules/react-native/flow 22 | flow/ 23 | 24 | [options] 25 | module.system=haste 26 | 27 | experimental.strict_type_args=true 28 | 29 | munge_underscores=true 30 | 31 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 32 | 33 | suppress_type=$FlowIssue 34 | suppress_type=$FlowFixMe 35 | suppress_type=$FixMe 36 | 37 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 38 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 39 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 40 | 41 | unsafe.enable_getters_and_setters=true 42 | 43 | [version] 44 | ^0.36.0 45 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # yarn 2 | *yarn.lock 3 | 4 | # OSX 5 | # 6 | .DS_Store 7 | 8 | # Xcode 9 | # 10 | build/ 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | *.xccheckout 21 | *.moved-aside 22 | DerivedData 23 | *.hmap 24 | *.ipa 25 | *.xcuserstate 26 | project.xcworkspace 27 | 28 | # Android/IntelliJ 29 | # 30 | build/ 31 | .idea 32 | .gradle 33 | local.properties 34 | *.iml 35 | 36 | # node.js 37 | # 38 | node_modules/ 39 | npm-debug.log 40 | 41 | # BUCK 42 | buck-out/ 43 | \.buckd/ 44 | android/app/libs 45 | *.keystore 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 53 | 54 | fastlane/report.xml 55 | fastlane/Preview.html 56 | fastlane/screenshots 57 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-simple-tab 2 | 3 | A React Native Tab Navigation, made for simplicity, works on both Android and iOS. 4 | 5 | ![demo](https://raw.githubusercontent.com/dwicao/react-native-simple-tab/master/demo.gif) 6 | 7 | > iOS (Left) | Android (right) 8 | 9 | ![](https://raw.githubusercontent.com/dwicao/react-native-simple-tab/master/demo2.png) 10 | > `onlyIcon` props on Nav set to `true` 11 | 12 | ## Features 13 | * worked with (react-native-router-flux) or any kind of navigation solutions 14 | * cross-platform icons by default 15 | * simple and clean UI 16 | 17 | ## Inspiration 18 | [Salestock Indonesia](https://play.google.com/store/apps/details?id=id.salestock.mobile) 19 | 20 | ## Installation 21 | * `npm install --save react-native-simple-tab react-native-vector-icons` 22 | 23 | ## After Installation (this step is required) 24 | * `react-native link` 25 | 26 | ## Basic Usage 27 | ```js 28 | import React, { Component } from 'react'; 29 | import { 30 | StyleSheet, 31 | Text, 32 | View, 33 | } from 'react-native'; 34 | import { Nav, Tab } from 'react-native-simple-tab'; 35 | 36 | class MyExample extends Component { 37 | constructor() { 38 | super(); 39 | 40 | this.state = { 41 | page: 0, 42 | }; 43 | 44 | this._onTabChange = this._onTabChange.bind(this); 45 | } 46 | 47 | _onTabChange(tabIndex) { 48 | this.setState({ page: tabIndex }); 49 | } 50 | 51 | render() { 52 | return ( 53 | 54 | 55 | Welcome to React Native! 56 | 57 | 58 | react-native-cross-platform-tab 59 | 60 | 61 | 62 | Selected Tab Index 63 | 64 | 65 | {this.state.page} 66 | 67 | 68 | 77 | 78 | ); 79 | } 80 | } 81 | 82 | const styles = StyleSheet.create({ 83 | container: { 84 | flex: 1, 85 | backgroundColor: '#F5FCFF', 86 | }, 87 | welcome: { 88 | flex: 1, 89 | fontSize: 20, 90 | textAlign: 'center', 91 | marginTop: 40, 92 | }, 93 | myPackage: { 94 | flex: 1, 95 | textAlign: 'center', 96 | color: '#333333', 97 | marginBottom: 5, 98 | }, 99 | myText: { 100 | textAlign: 'center', 101 | }, 102 | myTabIndex: { 103 | fontSize: 20, 104 | textAlign: 'center', 105 | fontWeight: 'bold', 106 | }, 107 | }); 108 | 109 | export default MyExample; 110 | ``` 111 | 112 | ## More Icons 113 | [Check Out Here](http://ionicframework.com/docs/v2/ionicons/) and use it's name into Tab `name` props. 114 | 115 | ## Nav props 116 | 117 | | Props | Type | Default | Description | 118 | | --- | --- | --- | --- | 119 | | selected | number | - | used to determine what `tabIndex` that currently selected | 120 | | onTabChange | function | () => {} | event onTabChange with param `tabIndex` | 121 | | backgroundColor | string | 'white' | Nav background color | 122 | | borderTopColor | string | '#DDDDDD' | Nav border top color | 123 | | height | number | 42 | Nav height | 124 | | onlyIcon | boolean | false | display only icon or icon with label | 125 | | pressOpacity | number | 0.7 | opacity when Tab be pressed (min: 0, max: 1) | 126 | | activeColor | string | 'black' | active color on all Tabs | 127 | | unActiveColor | string | 'gray' | unactive color on all Tabs | 128 | | iconSize | number | 22 | icon size on all Tabs | 129 | | fontSize | number | 11 | font size on all Tabs | 130 | | style | object | - | your own custom style for Nav wrapper | 131 | 132 | ## Tab props 133 | | Props | Type | isRequired? | Description | 134 | | --- | --- | --- | --- | 135 | | name | string | Required | used for icon name | 136 | | label | string | Optional | Tab label | 137 | | fontStyle | object | Optional | your own custom style for each Text on Tab | 138 | | style | object | Optional | your own custom style for Tab wrapper | 139 | 140 | ## License 141 | MIT 142 | 143 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwicao/react-native-simple-tab/0cc0e63eb785f50e0ef5bc0c24d6e9884b63739f/demo.gif -------------------------------------------------------------------------------- /demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwicao/react-native-simple-tab/0cc0e63eb785f50e0ef5bc0c24d6e9884b63739f/demo2.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-simple-tab", 3 | "version": "1.0.8", 4 | "description": "React Native Simple Tab", 5 | "main": "src/main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "dependencies": { 10 | "react-native-cross-platform-icons": "^1.0.4" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/dwicao/react-native-simple-tab.git.git" 15 | }, 16 | "keywords": [ 17 | "react", 18 | "native", 19 | "cross", 20 | "platform", 21 | "icons", 22 | "simple", 23 | "tab" 24 | ], 25 | "files": [ 26 | "src", 27 | "README.md" 28 | ], 29 | "author": "Lutfian Dwi Cahyono (https://dwicao.github.io/)", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/dwicao/react-native-simple-tab.git/issues" 33 | }, 34 | "homepage": "https://github.com/dwicao/react-native-simple-tab.git#readme" 35 | } 36 | -------------------------------------------------------------------------------- /src/Nav.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent, PropTypes } from 'react'; 2 | import { 3 | View, 4 | StyleSheet, 5 | } from 'react-native'; 6 | 7 | class Nav extends PureComponent { 8 | constructor() { 9 | super(); 10 | 11 | this._handleTabChange = this._handleTabChange.bind(this); 12 | } 13 | 14 | _handleTabChange(tabIndex) { 15 | this.props.onTabChange(tabIndex); 16 | } 17 | 18 | _containerStyle() { 19 | return { 20 | flexDirection: 'row', 21 | borderTopWidth: StyleSheet.hairlineWidth, 22 | backgroundColor: this.props.backgroundColor, 23 | borderTopColor: this.props.borderTopColor, 24 | height: this.props.height, 25 | } 26 | } 27 | 28 | render() { 29 | return ( 30 | 31 | {React.Children.map(this.props.children, (child, tabIndex) => ( 32 | React.cloneElement(child, { 33 | tabIndex, 34 | selected: this.props.selected, 35 | activeColor: this.props.activeColor, 36 | unActiveColor: this.props.unActiveColor, 37 | onTabPress: this._handleTabChange, 38 | iconSize: this.props.iconSize, 39 | onlyIcon: this.props.onlyIcon, 40 | pressOpacity: this.props.pressOpacity, 41 | fontSize: this.props.fontSize, 42 | }) 43 | ))} 44 | 45 | ); 46 | } 47 | } 48 | 49 | Nav.defaultProps = { 50 | onTabChange: () => {}, 51 | activeColor: 'black', 52 | unActiveColor: 'gray', 53 | backgroundColor: 'white', 54 | borderTopColor: '#DDDDDD', 55 | height: 42, 56 | iconSize: 22, 57 | onlyIcon: false, 58 | pressOpacity: 0.7, 59 | fontSize: 11, 60 | }; 61 | 62 | Nav.propTypes = { 63 | selected: PropTypes.number, 64 | onTabChange: PropTypes.func, 65 | backgroundColor: PropTypes.string, 66 | borderTopColor: PropTypes.string, 67 | height: PropTypes.number, 68 | iconSize: PropTypes.number, 69 | onlyIcon: PropTypes.bool, 70 | pressOpacity: PropTypes.number, 71 | fontStyle: PropTypes.object, 72 | style: PropTypes.oneOfType([ 73 | PropTypes.object, 74 | PropTypes.number 75 | ]), 76 | }; 77 | 78 | export default Nav; -------------------------------------------------------------------------------- /src/Tab.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent, PropTypes } from 'react'; 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | TouchableOpacity, 7 | } from 'react-native'; 8 | import CrossPlatformIcon from 'react-native-cross-platform-icons'; 9 | 10 | class Tab extends PureComponent { 11 | constructor() { 12 | super(); 13 | 14 | this._handleTabPress = this._handleTabPress.bind(this); 15 | } 16 | 17 | _handleTabPress() { 18 | this.props.onTabPress(this.props.tabIndex); 19 | } 20 | 21 | _getColor() { 22 | if (this.props.selected === this.props.tabIndex) { 23 | return this.props.activeColor; 24 | } 25 | 26 | return this.props.unActiveColor; 27 | } 28 | 29 | render() { 30 | return ( 31 | 36 | 41 | {!this.props.onlyIcon && 42 | 43 | {this.props.label} 44 | 45 | } 46 | 47 | ); 48 | } 49 | } 50 | 51 | Tab.propTypes = { 52 | name: PropTypes.string.isRequired, 53 | label: PropTypes.string, 54 | fontStyle: PropTypes.object, 55 | style: PropTypes.oneOfType([ 56 | PropTypes.object, 57 | PropTypes.number 58 | ]), 59 | }; 60 | 61 | const styles = StyleSheet.create({ 62 | container: { 63 | flex: 1, 64 | alignItems: 'center', 65 | justifyContent: 'center', 66 | }, 67 | }); 68 | 69 | export default Tab; -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | export { default as Nav } from './Nav'; 2 | export { default as Tab } from './Tab'; 3 | --------------------------------------------------------------------------------