├── .eslintrc ├── .gitignore ├── README.md ├── index.js └── package.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "rules": { 4 | "import/no-extraneous-dependencies": 0, 5 | "import/no-unresolved": 0, 6 | "import/extensions": 0, 7 | "no-use-before-define": 0, 8 | "react/jsx-filename-extension": 0 9 | } 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | npm-debug.log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Modal WalkThrough 2 | A modal to walk through different steps in react native 3 | 4 | ## Installation 5 | Install with yarn or npm 6 | ``` 7 | yarn add react-native-modal-walk-through 8 | ``` 9 | 10 | Import the lib 11 | ``` 12 | import ModalWalkThrough from 'react-native-modal-walk-through'; 13 | ``` 14 | 15 | Add scenes 16 | ``` 17 | 20 | {['scene1', 'scene2'].map(scene => ( 21 | 22 | {scene} 23 | 24 | ))} 25 | 26 | ``` 27 | 28 | ## Preview 29 | ![preview](https://media.giphy.com/media/U1rr1gjlYDh0k/giphy.gif) 30 | 31 | ## Properties 32 | |Property |Type |Description |Default value | 33 | |---------|-----------|--------------------------------|--------------------| 34 | |height |Number |Height of the walkthrough |40% of screen height| 35 | |width |Number |Width of the walkthrough |80% of screen width | 36 | |onFinish |Function |When the user went throught the entire walkthrough| | 37 | |onStepChange|Function |When the user swiped to another step | | 38 | 39 | ## Methods 40 | ### goToStep (step: Number) 41 | Go to a particular step in the walkthrough, 42 | in case the number is bigger than the last step index, it will close the modal 43 | 44 | ### show 45 | Show the modal 46 | 47 | ### hide 48 | Hide the modal 49 | 50 | [![gitcheese.com](https://s3.amazonaws.com/gitcheese-ui-master/images/badge.svg)](https://www.gitcheese.com/donate/users/5782495/repos/93313202) 51 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | Modal, 5 | FlatList, 6 | View, 7 | Dimensions, 8 | StyleSheet, 9 | BackHandler, 10 | BackAndroid, 11 | TouchableWithoutFeedback, 12 | } from 'react-native'; 13 | 14 | /** 15 | * 16 | * 17 | * @returs {React.Component} 18 | */ 19 | class ModalWalkThrough extends Component { 20 | static get propTypes() { 21 | return { 22 | height: PropTypes.number, 23 | width: PropTypes.number, 24 | onStepChange: PropTypes.func, 25 | onFinish: PropTypes.func, 26 | children: PropTypes.children, 27 | }; 28 | } 29 | 30 | static get defaultProps() { 31 | return { 32 | height: null, 33 | width: null, 34 | onStepChange: null, 35 | onFinish: null, 36 | children: null, 37 | }; 38 | } 39 | 40 | /** 41 | * Get the actual window height in pixels 42 | * @returns {Number} value in px 43 | */ 44 | static get screenHeight() { 45 | return Dimensions.get('window').height; 46 | } 47 | 48 | /** 49 | * Get the actual window width in pixels 50 | * @returns {Number} value in px 51 | */ 52 | static get screenWidth() { 53 | return Dimensions.get('window').width; 54 | } 55 | 56 | 57 | constructor() { 58 | super(); 59 | 60 | this.goToStep = this.goToStep.bind(this); 61 | this.handleScroll = this.handleScroll.bind(this); 62 | this.handleHardwareBackPress = this.handleHardwareBackPress.bind(this); 63 | this.show = this.show.bind(this); 64 | this.hide = this.hide.bind(this); 65 | this.renderChild = this.renderChild.bind(this); 66 | } 67 | 68 | componentWillMount() { 69 | this.state = { 70 | visible: false, 71 | }; 72 | } 73 | 74 | componentDidMount() { 75 | if ( 76 | BackHandler && 77 | typeof BackHandler.addEventListener === 'function' 78 | ) { 79 | BackHandler.addEventListener('hardwareBackPress', this.handleHardwareBackPress); 80 | } else { 81 | BackAndroid.addEventListener('hardwareBackPress', this.handleHardwareBackPress); 82 | } 83 | } 84 | 85 | componentWillUnmount() { 86 | if ( 87 | BackHandler && 88 | typeof BackHandler.addEventListener === 'function' 89 | ) { 90 | BackHandler.removeEventListener('hardwareBackPress', this.handleHardwareBackPress); 91 | } else { 92 | BackAndroid.removeEventListener('hardwareBackPress', this.handleHardwareBackPress); 93 | } 94 | } 95 | 96 | /** 97 | * Height of the scene 98 | * @return {Number|String} the height value 99 | */ 100 | get height() { 101 | return this.props.height || '40%'; 102 | } 103 | 104 | /** 105 | * Width of the scene 106 | * @return {Number|String} the width value 107 | */ 108 | get width() { 109 | return this.props.width || (ModalWalkThrough.screenWidth * 0.8); 110 | } 111 | 112 | /** 113 | * Go to a specific step in the walkthrough 114 | * @param {Number} [index=0] the index to scroll to 115 | * @returns {void} 116 | */ 117 | goToStep(index = 0) { 118 | if (index > this.props.children.length - 1) { 119 | // In case of going beyond last step, close the modal 120 | this.setState({ visible: false }); 121 | 122 | // Trigger onFinish in case defined 123 | if (typeof this.props.onFinish === 'function') { 124 | this.props.onFinish(); 125 | } 126 | } else { 127 | // Scroll the walkthrough to a specific step 128 | this.flatList.scrollToOffset({ 129 | animated: true, 130 | offset: index * ModalWalkThrough.screenWidth, 131 | }); 132 | } 133 | } 134 | 135 | /** 136 | * Handle user scroll event 137 | * @param {Object} eventData 138 | * @returns {void} 139 | */ 140 | handleScroll(eventData) { 141 | // Only when onStepChange was defined 142 | if (typeof this.props.onStepChange === 'function') { 143 | const { x } = eventData.nativeEvent.contentOffset; 144 | const step = Math.round(x / ModalWalkThrough.screenWidth); 145 | 146 | // Trigger an onStepChange event with the current step 147 | this.props.onStepChange(step); 148 | } 149 | } 150 | 151 | /** 152 | * Handle a press on the physical back button (Android, tvOS) 153 | * @returns {void} 154 | */ 155 | handleHardwareBackPress() { 156 | this.hide(); 157 | } 158 | 159 | /** 160 | * Make the modal visible 161 | * @returns {void} 162 | */ 163 | show() { 164 | this.setState({ visible: true }); 165 | } 166 | 167 | /** 168 | * Make the modal invisible 169 | * @returns {void} 170 | */ 171 | hide() { 172 | this.setState({ visible: false }); 173 | } 174 | 175 | /** 176 | * Render child of the walkThrough 177 | * @param {Object} param0 Options param 178 | * @param {Object} param0.item Item component to render 179 | * @param {Nummber} param0.index Index of component 180 | * @returns {React.Component} component 181 | */ 182 | renderChild({ item, index }) { 183 | return ( 184 | 190 | 194 | {item} 195 | 196 | 197 | ); 198 | } 199 | 200 | render() { 201 | return ( 202 | 207 | 210 | 213 | { this.flatList = flatList; }} 220 | onScroll={this.handleScroll} 221 | bounces={false} 222 | /> 223 | 224 | 225 | 226 | ); 227 | } 228 | } 229 | 230 | const styles = StyleSheet.create({ 231 | overlay: { 232 | backgroundColor: 'rgba(0,0,0,0.8)', 233 | flex: 1, 234 | }, 235 | sceneWrapper: { 236 | alignItems: 'center', 237 | justifyContent: 'center', 238 | }, 239 | scene: { 240 | backgroundColor: '#ffffff', 241 | borderRadius: 3, 242 | }, 243 | }); 244 | 245 | export default ModalWalkThrough; 246 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-modal-walk-through", 3 | "version": "0.1.5", 4 | "description": "Walkthrough in a React Native modal", 5 | "main": "index.js", 6 | "repository": "http://github.com/dejakob/react-native-modal-walk-through", 7 | "author": "dejakob", 8 | "license": "MIT", 9 | "devDependencies": { 10 | "eslint": "^3.19.0", 11 | "eslint-config-airbnb": "^15.0.1", 12 | "eslint-plugin-import": "^2.3.0", 13 | "eslint-plugin-jsx-a11y": "^5.0.3", 14 | "eslint-plugin-react": "^7.0.1" 15 | }, 16 | "scripts": { 17 | "lint": "eslint **/*.js" 18 | } 19 | } 20 | --------------------------------------------------------------------------------