├── .gitignore ├── README.md ├── docs └── screenshots │ └── Screen Shot 2016-12-03 at 12.54.31 PM.png ├── index.js ├── jsconfig.json ├── lib ├── stopwatch.js ├── timer.js └── utils.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # node.js 2 | # 3 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## React Native Stopwatch Timer 2 | 3 | A React Native component that provides a stopwatch and timer. 4 | 5 | 6 | 7 | ### Instructions 8 | 9 | ```npm install react-native-stopwatch-timer``` 10 | 11 | ```js 12 | import { Stopwatch, Timer } from 'react-native-stopwatch-timer' 13 | ``` 14 | 15 | ### Options 16 | 17 | #### Stopwatch and Timer Options 18 | 19 | |Name|Type|Description|Default| 20 | |----|----|-----------|------| 21 | |start|boolean|starts timer/stopwatch if true, stops if false|false| 22 | |reset|boolean|stops timer/stopwatch, resets|false| 23 | |msecs|boolean|includes milliseconds in render of time|false| 24 | |options|object|describes style of rendered timer/stopwatch|see example| 25 | |getTime|function|get the formatted value on each tick|(time) => console.log(time)| 26 | |getMsecs|function|get the number of msecs on each tick|(time) => console.log(time)| 27 | 28 | 29 | #### Stopwatch Options 30 | 31 | |Name|Type|Description|Default| 32 | |----|----|-----------|------| 33 | |laps|boolean|will not count the laps of the stopped stopwatch|false| 34 | |startTime|number|number of milliseconds to start stopwatch from|0| 35 | 36 | 37 | #### Timer Options 38 | 39 | |Name|Type|Description|Default| 40 | |----|----|-----------|------| 41 | |totalDuration|Integer|number of milliseconds to set timer for|0| 42 | |handleFinish|function|function to perform when timer completes|() => alert("Timer Finished")| 43 | 44 | ### Example 45 | 46 | ```js 47 | import React, { Component } from 'react'; 48 | import { AppRegistry, StyleSheet,Text,View, TouchableHighlight } from 'react-native'; 49 | import { Stopwatch, Timer } from 'react-native-stopwatch-timer'; 50 | 51 | class TestApp extends Component { 52 | constructor(props) { 53 | super(props); 54 | this.state = { 55 | timerStart: false, 56 | stopwatchStart: false, 57 | totalDuration: 90000, 58 | timerReset: false, 59 | stopwatchReset: false, 60 | }; 61 | this.toggleTimer = this.toggleTimer.bind(this); 62 | this.resetTimer = this.resetTimer.bind(this); 63 | this.toggleStopwatch = this.toggleStopwatch.bind(this); 64 | this.resetStopwatch = this.resetStopwatch.bind(this); 65 | } 66 | 67 | toggleTimer() { 68 | this.setState({timerStart: !this.state.timerStart, timerReset: false}); 69 | } 70 | 71 | resetTimer() { 72 | this.setState({timerStart: false, timerReset: true}); 73 | } 74 | 75 | toggleStopwatch() { 76 | this.setState({stopwatchStart: !this.state.stopwatchStart, stopwatchReset: false}); 77 | } 78 | 79 | resetStopwatch() { 80 | this.setState({stopwatchStart: false, stopwatchReset: true}); 81 | } 82 | 83 | getFormattedTime(time) { 84 | this.currentTime = time; 85 | }; 86 | 87 | render() { 88 | return ( 89 | 90 | 94 | 95 | {!this.state.stopwatchStart ? "Start" : "Stop"} 96 | 97 | 98 | Reset 99 | 100 | 105 | 106 | {!this.state.timerStart ? "Start" : "Stop"} 107 | 108 | 109 | Reset 110 | 111 | 112 | ); 113 | } 114 | } 115 | 116 | const handleTimerComplete = () => alert("custom completion function"); 117 | 118 | const options = { 119 | container: { 120 | backgroundColor: '#000', 121 | padding: 5, 122 | borderRadius: 5, 123 | width: 220, 124 | }, 125 | text: { 126 | fontSize: 30, 127 | color: '#FFF', 128 | marginLeft: 7, 129 | } 130 | }; 131 | 132 | AppRegistry.registerComponent('TestApp', () => TestApp); 133 | 134 | ``` -------------------------------------------------------------------------------- /docs/screenshots/Screen Shot 2016-12-03 at 12.54.31 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljstevens/react-native-stopwatch-timer/252d08c49362b8c8926fe146409e133dedce0424/docs/screenshots/Screen Shot 2016-12-03 at 12.54.31 PM.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Stopwatch: require("./lib/stopwatch.js").default, 3 | Timer: require("./lib/timer.js").default 4 | }; 5 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /lib/stopwatch.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Text, View, StyleSheet} from 'react-native'; 3 | import PropTypes from 'prop-types'; 4 | import { formatTimeString } from './utils'; 5 | 6 | 7 | class StopWatch extends Component { 8 | static propTypes = { 9 | start: PropTypes.bool, 10 | reset: PropTypes.bool, 11 | msecs: PropTypes.bool, 12 | options: PropTypes.object, 13 | laps: PropTypes.bool, 14 | getTime: PropTypes.func, 15 | startTime: PropTypes.number, 16 | getMsecs: PropTypes.func, 17 | } 18 | 19 | constructor(props) { 20 | super(props); 21 | const { startTime } = props; 22 | this.state = { 23 | startTime: null, 24 | stopTime: null, 25 | pausedTime: null, 26 | started: false, 27 | elapsed: startTime || 0, 28 | }; 29 | this.start = this.start.bind(this); 30 | this.stop = this.stop.bind(this); 31 | this.reset = this.reset.bind(this); 32 | this.formatTime = this.formatTime.bind(this); 33 | const width = props.msecs ? 220 : 150; 34 | this.defaultStyles = { 35 | container: { 36 | backgroundColor: '#000', 37 | padding: 5, 38 | borderRadius: 5, 39 | width: width, 40 | }, 41 | text: { 42 | fontSize: 30, 43 | color: '#FFF', 44 | marginLeft: 7, 45 | } 46 | }; 47 | } 48 | 49 | componentDidMount() { 50 | if(this.props.start) { 51 | this.start(); 52 | } 53 | } 54 | 55 | componentWillReceiveProps(newProps) { 56 | if(newProps.start) { 57 | this.start(); 58 | } else { 59 | this.stop(); 60 | } 61 | if(newProps.reset) { 62 | this.reset(); 63 | } 64 | } 65 | 66 | componentWillUnmount() { 67 | clearInterval(this.interval); 68 | } 69 | 70 | start() { 71 | if (this.props.laps && this.state.elapsed) { 72 | let lap = new Date() - this.state.stopTime; 73 | this.setState({ 74 | stopTime: null, 75 | pausedTime: this.state.pausedTime + lap 76 | }) 77 | } 78 | 79 | this.setState({startTime: this.state.elapsed ? new Date() - this.state.elapsed : 80 | new Date(), started: true}); 81 | 82 | this.interval = this.interval ? this.interval : setInterval(() => { 83 | this.setState({elapsed: new Date() - this.state.startTime }); 84 | }, 1); 85 | } 86 | 87 | stop() { 88 | if(this.interval) { 89 | if (this.props.laps) { 90 | this.setState({stopTime: new Date()}) 91 | } 92 | 93 | clearInterval(this.interval); 94 | this.interval = null; 95 | } 96 | this.setState({started: false}); 97 | } 98 | 99 | reset() { 100 | const { startTime } = this.props; 101 | this.setState({ 102 | elapsed: startTime || 0, 103 | startTime: null, 104 | stopTime: null, 105 | pausedTime: null 106 | }); 107 | } 108 | 109 | formatTime() { 110 | const { getTime, getMsecs, msecs } = this.props; 111 | const now = this.state.elapsed; 112 | const formatted = formatTimeString(now, msecs); 113 | if (typeof getTime === "function") { 114 | getTime(formatted); 115 | } 116 | if (typeof getMsecs === "function") { 117 | getMsecs(now) 118 | } 119 | return formatted; 120 | } 121 | 122 | 123 | render() { 124 | 125 | const styles = this.props.options ? this.props.options : this.defaultStyles; 126 | 127 | return( 128 | 129 | {this.formatTime()} 130 | 131 | ); 132 | } 133 | } 134 | 135 | export default StopWatch; 136 | -------------------------------------------------------------------------------- /lib/timer.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Text, View, StyleSheet} from 'react-native'; 3 | import PropTypes from 'prop-types'; 4 | import { formatTimeString } from './utils'; 5 | 6 | class Timer extends Component { 7 | static propTypes = { 8 | start: PropTypes.bool, 9 | reset: PropTypes.bool, 10 | msecs: PropTypes.bool, 11 | options: PropTypes.object, 12 | handleFinish: PropTypes.func, 13 | totalDuration: PropTypes.number, 14 | getTime: PropTypes.func, 15 | getMsecs: PropTypes.func, 16 | } 17 | 18 | constructor(props) { 19 | super(props); 20 | this.state = { 21 | started: false, 22 | remainingTime: props.totalDuration, 23 | }; 24 | this.start = this.start.bind(this); 25 | this.stop = this.stop.bind(this); 26 | this.reset = this.reset.bind(this); 27 | this.formatTime = this.formatTime.bind(this); 28 | const width = props.msecs ? 220 : 150; 29 | this.defaultStyles = { 30 | container: { 31 | backgroundColor: '#000', 32 | padding: 5, 33 | borderRadius: 5, 34 | width: width, 35 | }, 36 | text: { 37 | fontSize: 30, 38 | color: '#FFF', 39 | marginLeft: 7, 40 | } 41 | }; 42 | } 43 | 44 | componentDidMount() { 45 | if(this.props.start) { 46 | this.start(); 47 | } 48 | } 49 | 50 | componentWillReceiveProps(newProps) { 51 | 52 | if(newProps.start) { 53 | this.start(); 54 | } else { 55 | this.stop(); 56 | } 57 | if(newProps.reset) { 58 | this.reset(newProps.totalDuration); 59 | } 60 | } 61 | 62 | start() { 63 | const handleFinish = this.props.handleFinish ? this.props.handleFinish : () => alert("Timer Finished"); 64 | const endTime = new Date().getTime() + this.state.remainingTime; 65 | this.interval = setInterval(() => { 66 | const remaining = endTime - new Date(); 67 | if(remaining <= 1000) { 68 | this.setState({remainingTime: 0}); 69 | this.stop(); 70 | handleFinish(); 71 | return; 72 | } 73 | this.setState({remainingTime: remaining}); 74 | }, 1); 75 | } 76 | 77 | stop() { 78 | clearInterval(this.interval); 79 | } 80 | 81 | reset(newDuration) { 82 | this.setState({ 83 | remainingTime: 84 | this.props.totalDuration !== newDuration ? 85 | newDuration : 86 | this.props.totalDuration 87 | }); 88 | } 89 | 90 | formatTime() { 91 | const { getTime, getMsecs, msecs } = this.props; 92 | const now = this.state.remainingTime; 93 | const formatted = formatTimeString(now, msecs); 94 | if (typeof getTime === "function") { 95 | getTime(formatted); 96 | } 97 | if (typeof getMsecs === "function") { 98 | getMsecs(now) 99 | } 100 | return formatted; 101 | } 102 | 103 | render() { 104 | 105 | const styles = this.props.options ? this.props.options : this.defaultStyles; 106 | 107 | return( 108 | 109 | {this.formatTime()} 110 | 111 | ); 112 | } 113 | } 114 | 115 | export default Timer; 116 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | function formatTimeString(time, showMsecs) { 2 | let msecs = time % 1000; 3 | 4 | if (msecs < 10) { 5 | msecs = `00${msecs}`; 6 | } else if (msecs < 100) { 7 | msecs = `0${msecs}`; 8 | } 9 | 10 | let seconds = Math.floor(time / 1000); 11 | let minutes = Math.floor(time / 60000); 12 | let hours = Math.floor(time / 3600000); 13 | seconds = seconds - minutes * 60; 14 | minutes = minutes - hours * 60; 15 | let formatted; 16 | if (showMsecs) { 17 | formatted = `${hours < 10 ? 0 : ""}${hours}:${ 18 | minutes < 10 ? 0 : "" 19 | }${minutes}:${seconds < 10 ? 0 : ""}${seconds}:${msecs}`; 20 | } else { 21 | formatted = `${hours < 10 ? 0 : ""}${hours}:${ 22 | minutes < 10 ? 0 : "" 23 | }${minutes}:${seconds < 10 ? 0 : ""}${seconds}`; 24 | } 25 | 26 | return formatted; 27 | } 28 | 29 | export { formatTimeString }; 30 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-stopwatch-timer", 3 | "version": "0.0.21", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "js-tokens": { 8 | "version": "4.0.0", 9 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 10 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 11 | }, 12 | "loose-envify": { 13 | "version": "1.4.0", 14 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 15 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 16 | "requires": { 17 | "js-tokens": "^3.0.0 || ^4.0.0" 18 | } 19 | }, 20 | "object-assign": { 21 | "version": "4.1.1", 22 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 23 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 24 | }, 25 | "prop-types": { 26 | "version": "15.7.2", 27 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 28 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 29 | "requires": { 30 | "loose-envify": "^1.4.0", 31 | "object-assign": "^4.1.1", 32 | "react-is": "^16.8.1" 33 | } 34 | }, 35 | "react-is": { 36 | "version": "16.8.6", 37 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", 38 | "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-stopwatch-timer", 3 | "version": "0.0.21", 4 | "description": "A stopwatch/timer component for React Native.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/michaeljstevens/react-native-stopwatch-timer" 8 | }, 9 | "keywords": [ 10 | "react-native", 11 | "stopwatch", 12 | "timer" 13 | ], 14 | "author": { 15 | "name": "Michael Stevens" 16 | }, 17 | "dependencies": { 18 | "prop-types": "^15.7.2" 19 | } 20 | } 21 | --------------------------------------------------------------------------------