├── .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 |
--------------------------------------------------------------------------------