├── .gitignore ├── README.md ├── examples.gif ├── index.js ├── package.json └── src ├── styles.js └── switch.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Image examples 7 | examples.gif 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # Compiled binary addons (http://nodejs.org/api/addons.html) 31 | build/Release 32 | 33 | # Dependency directories 34 | node_modules 35 | jspm_packages 36 | 37 | # Optional npm cache directory 38 | .npm 39 | 40 | # Optional eslint cache 41 | .eslintcache 42 | 43 | # Optional REPL history 44 | .node_repl_history 45 | 46 | # Output of 'npm pack' 47 | *.tgz 48 | 49 | # Yarn Integrity file 50 | .yarn-integrity -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-customisable-switch 2 | A React Native module that allows you to customize switch (style, form and animation), available for android and IOS. 3 | 4 | ### Content 5 | - [Installation](#installation) 6 | - [Examples](#usage-example) 7 | - [Properties](#component-properties) 8 | - [Questions](#questions) 9 | 10 | ### Installation 11 | ```bash 12 | npm install --save react-native-customisable-switch 13 | ``` 14 | 15 | ### Examples 16 |

17 | 18 |

19 | 20 | ```javascript 21 | import React, { Component } from 'react'; 22 | import { Text, View } from 'react-native'; 23 | import Switch from 'react-native-customisable-switch'; 24 | 25 | export default class Test extends Component { 26 | constructor(props) { 27 | super(props); 28 | this.state = { 29 | switchOneValue: false, 30 | switchTwoValue: false, 31 | switchThreeValue: true, 32 | }; 33 | } 34 | 35 | render() { 36 | const { 37 | switchOneValue, 38 | switchTwoValue, 39 | switchOneValue, 40 | } = this.state; 41 | 42 | return( 43 | 44 | 45 | this.setState({ switchOneValue: !switchOneValue })} 48 | /> 49 | 50 | 51 | this.setState({ switchTwoValue: !switchTwoValue })} 54 | activeText={''} 55 | inactiveText={''} 56 | fontSize={16} 57 | activeTextColor={'rgba(255, 255, 255, 1)'} 58 | inactiveTextColor={'rgba(255, 255, 255, 1)'} 59 | activeBackgroundColor={'rgba(50, 163, 50, 1)'} 60 | inactiveBackgroundColor={'rgba(137, 137, 137, 1)'} 61 | activeButtonBackgroundColor={'rgba(255, 255, 255, 1)'} 62 | inactiveButtonBackgroundColor={'rgba(255, 255, 255, 1)'} 63 | switchWidth={70} 64 | switchHeight={30} 65 | switchBorderRadius={0} 66 | switchBorderColor={'rgba(0, 0, 0, 1)'} 67 | switchBorderWidth={0} 68 | buttonWidth={25} 69 | buttonHeight={25} 70 | buttonBorderRadius={0} 71 | buttonBorderColor={'rgba(0, 0, 0, 1)'} 72 | buttonBorderWidth={0} 73 | animationTime={150} 74 | padding={true} 75 | /> 76 | 77 | 78 | this.setState({ switchThreeValue: !switchThreeValue })} 81 | activeText={'On'} 82 | inactiveText={'Off'} 83 | fontSize={16} 84 | switchWidth={70} 85 | switchHeight={25} 86 | switchBorderRadius={12} 87 | switchBorderWidth={0} 88 | buttonWidth={25} 89 | buttonHeight={40} 90 | buttonBorderRadius={20} 91 | buttonBorderColor={'rgba(0, 0, 0, 1)'} 92 | buttonBorderWidth={0} 93 | animationTime={150} 94 | padding={true} 95 | /> 96 | 97 | 98 | ) 99 | } 100 | } 101 | 102 | const styles = StyleSheet.create({ 103 | container: { 104 | flex: 1, 105 | backgroundColor: '#464857', 106 | justifyContent: 'center', 107 | alignItems: 'center', 108 | }, 109 | )}; 110 | ``` 111 | 112 | ### Component Properties 113 | 114 | | Property | Type | Default Value | Description | 115 | |-------------------------|----------|--------------------------|---------------------------------------------------------------------------| 116 | | value | boolean | false | Switch value to determine whether or not the switch is active or inactive | 117 | | onChangeValue | function | () => null | Function to handle the change of the value property | 118 | | activeText | string | "On" | Text when the switch is activate | 119 | | inactiveText | string | "Off" | Text when the switch is inactive | 120 | | fontSize | number | 16 | Text Size for `activeText` and `inactiveText` | 121 | | activeTextColor | string | "rgba(255, 255, 255, 1)" | Text color of `activeText` | 122 | | inactiveTextColor | string | "rgba(255, 255, 255, 1)" | Text color of `inactiveText` | 123 | | activeBackgroundColor | string | "rgba(50, 163, 50, 1)" | Background color of the switch while active | 124 | | inactiveBackgroundColor | string | "rgba(137, 137, 137, 1)" | Background color of the switch while inactive | 125 | | switchWidth | number | 70 | Width of the switch | 126 | | switchHeight | number | 30 | Height of the switch | 127 | | switchBorderRadius | number | 15 | Border radius of the switch | 128 | | switchBorderColor | string | "rgba(0, 0, 0, 1)" | Border color of the switch | 129 | | switchBorderWidth | number | 0 | Border width of the switch | 130 | | buttonWidth | number | 25 | Width of the button in the switch | 131 | | buttonHeight | number | 25 | Height of the button in the switch | 132 | | buttonBorderRadius | number | 15 | Border radius of the button in the switch | 133 | | buttonBorderColor | string | "rgba(0, 0, 0, 1)" | Border color of the button in the switch | 134 | | buttonBorderWidth | number | 0 | Border width of the button in the switch | 135 | | animationTime | number | 150 | Time of toggle animation in milliseconds | 136 | | padding | boolean | true | Whether the switch has a horizontal padding of 5 or not | 137 | 138 | 139 | ### Questions 140 | Create an issue (https://github.com/baderahmed/react-native-customisable-switch/issues) 141 | -------------------------------------------------------------------------------- /examples.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baderahmed/react-native-customisable-switch/118fe5aa51150b8c471c081a23ba38087aa210c6/examples.gif -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import Switch from "./src/switch.js"; 2 | 3 | export default Switch; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-customisable-switch", 3 | "version": "0.1.0", 4 | "description": "A React Native module that allows you to customize switch (style, form and animation), availble for android and IOS.", 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/baderahmed/react-native-customisable-switch.git#commit-ish" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "switch", 16 | "android", 17 | "ios", 18 | "disign", 19 | "form", 20 | "color", 21 | "backgroundColor", 22 | "size" 23 | ], 24 | "author": "Bader Ahmed Tellili ", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/baderahmed/react-native-customisable-switch/issues" 28 | }, 29 | "homepage": "https://github.com/baderahmed/react-native-customisable-switch/tree/commit-ish#readme" 30 | } 31 | -------------------------------------------------------------------------------- /src/styles.js: -------------------------------------------------------------------------------- 1 | 2 | import { StyleSheet, Dimensions } from 'react-native'; 3 | 4 | const deviceHeight = Dimensions.get('window').height; 5 | const deviceWidth = Dimensions.get('window').width; 6 | 7 | export default StyleSheet.create({ 8 | container: { 9 | justifyContent: 'center', 10 | alignItems: 'center', 11 | backgroundColor: 'transparent', 12 | flexDirection: 'column', 13 | }, 14 | animatedContainer: { 15 | flex: 1, 16 | flexDirection: 'row', 17 | justifyContent: 'center', 18 | alignItems: 'center', 19 | }, 20 | textContainer: { 21 | flex: 1, 22 | flexDirection: 'row', 23 | justifyContent: 'center', 24 | alignItems: 'center', 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /src/switch.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | Text, 4 | TouchableWithoutFeedback, 5 | View, 6 | Animated, 7 | } from 'react-native'; 8 | import PropTypes from 'prop-types'; 9 | import styles from './styles.js'; 10 | 11 | export default class Switch extends Component { 12 | static propTypes = { 13 | value: PropTypes.bool, 14 | onChangeValue: PropTypes.func, 15 | activeText: PropTypes.string, 16 | inactiveText: PropTypes.string, 17 | fontSize: PropTypes.number, 18 | activeTextColor: PropTypes.string, 19 | inactiveTextColor: PropTypes.string, 20 | activeBackgroundColor: PropTypes.string, 21 | inactiveBackgroundColor: PropTypes.string, 22 | activeButtonBackgroundColor: PropTypes.string, 23 | inactiveButtonBackgroundColor: PropTypes.string, 24 | switchWidth: PropTypes.number, 25 | switchHeight: PropTypes.number, 26 | switchBorderRadius: PropTypes.number, 27 | switchBorderColor: PropTypes.string, 28 | switchBorderWidth: PropTypes.number, 29 | buttonWidth: PropTypes.number, 30 | buttonHeight: PropTypes.number, 31 | buttonBorderRadius: PropTypes.number, 32 | buttonBorderColor: PropTypes.string, 33 | buttonBorderWidth: PropTypes.number, 34 | animationTime: PropTypes.number, 35 | padding: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), 36 | shadowColor: PropTypes.string, 37 | shadowOffset: PropTypes.object, 38 | shadowRadius: PropTypes.number, 39 | shadowOpacity: PropTypes.number, 40 | }; 41 | 42 | static defaultProps = { 43 | value: false, 44 | onChangeValue: () => null, 45 | activeText: '', 46 | inactiveText: '', 47 | fontSize: 16, 48 | activeTextColor: 'rgba(255, 255, 255, 1)', 49 | inactiveTextColor: 'rgba(255, 255, 255, 1)', 50 | activeBackgroundColor: 'rgba(50, 163, 50, 1)', 51 | inactiveBackgroundColor: 'rgba(137, 137, 137, 1)', 52 | activeButtonBackgroundColor: 'rgba(255, 255, 255, 1)', 53 | inactiveButtonBackgroundColor: 'rgba(255, 255, 255, 1)', 54 | switchWidth: 70, 55 | switchHeight: 30, 56 | switchBorderRadius: 15, 57 | switchBorderColor: 'rgba(0, 0, 0, 1)', 58 | switchBorderWidth: 0, 59 | buttonWidth: 25, 60 | buttonHeight: 25, 61 | buttonBorderRadius: 15, 62 | buttonBorderColor: 'rgba(0, 0, 0, 1)', 63 | buttonBorderWidth: 0, 64 | animationTime: 150, 65 | padding: 5, 66 | shadowColor: '#000', 67 | shadowOffset: {width: 0, height: 0}, 68 | shadowRadius: 0, 69 | shadowOpacity: 1, 70 | } 71 | 72 | constructor(props, context) { 73 | super(props, context); 74 | // Backwards compatibility: `padding` used to be bool, where `true = 5` 75 | this.padding = props.padding === true ? 5 : (props.padding || 0); 76 | this.transformValue = (props.switchWidth - props.buttonWidth - this.padding); 77 | this.state = { 78 | transformValue: new Animated.Value(props.value ? this.transformValue : this.padding), 79 | backgroundColor: new Animated.Value(props.value ? 90 : -90), 80 | buttonBackgroundColor: new Animated.Value(props.value ? 90 : -90), 81 | }; 82 | } 83 | 84 | componentDidUpdate(prevProps) { 85 | const { value } = this.props; 86 | if (value !== prevProps) this.startGroupAnimations(); 87 | } 88 | 89 | startGroupAnimations = () => { 90 | const { animationTime, onChangeValue, value } = this.props; 91 | Animated.parallel([ 92 | Animated.spring(this.state.transformValue, { 93 | toValue: value ? this.transformValue : this.padding, 94 | duration: animationTime, 95 | }), 96 | Animated.timing(this.state.backgroundColor, { 97 | toValue: value ? 75 : -75, 98 | duration: animationTime, 99 | }), 100 | Animated.timing(this.state.buttonBackgroundColor, { 101 | toValue: value ? 75 : -75, 102 | duration: animationTime, 103 | }) 104 | ]).start(); 105 | } 106 | 107 | render() { 108 | const { 109 | transformValue, 110 | backgroundColor, 111 | buttonBackgroundColor, 112 | } = this.state; 113 | 114 | const { 115 | value, 116 | onChangeValue, 117 | activeText, 118 | inactiveText, 119 | fontSize, 120 | activeTextColor, 121 | inactiveTextColor, 122 | activeBackgroundColor, 123 | inactiveBackgroundColor, 124 | activeButtonBackgroundColor, 125 | inactiveButtonBackgroundColor, 126 | switchWidth, 127 | switchHeight, 128 | switchBorderRadius, 129 | switchBorderColor, 130 | switchBorderWidth, 131 | buttonWidth, 132 | buttonHeight, 133 | buttonBorderRadius, 134 | buttonBorderColor, 135 | buttonBorderWidth, 136 | shadowColor, 137 | shadowOffset, 138 | shadowRadius, 139 | shadowOpacity, 140 | } = this.props; 141 | 142 | const backgroundColorValue = backgroundColor.interpolate({ 143 | inputRange: [-90, 90], 144 | outputRange: [ 145 | inactiveBackgroundColor, 146 | activeBackgroundColor, 147 | ], 148 | }); 149 | 150 | const buttonBackgroundColorValue = buttonBackgroundColor.interpolate({ 151 | inputRange: [-90, 90], 152 | outputRange: [ 153 | inactiveButtonBackgroundColor, 154 | activeButtonBackgroundColor, 155 | ], 156 | }); 157 | 158 | const containerHeight = switchHeight > buttonHeight ? switchHeight : buttonHeight; 159 | const containerWidth = switchWidth > buttonWidth ? switchWidth : buttonWidth; 160 | 161 | return ( 162 | 165 | 174 | 188 | 189 | 190 | 191 | {value ? activeText : ''} 192 | 193 | 194 | 195 | 196 | {value ? '' : inactiveText} 197 | 198 | 199 | 200 | 201 | 219 | 220 | 221 | ); 222 | } 223 | } 224 | --------------------------------------------------------------------------------