├── .gitignore ├── LICENSE.md ├── README.md ├── countdown.gif ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html └── src ├── App.js ├── Countdown.js ├── github.png ├── index.css ├── index.js └── logo.svg /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Kristin Baumann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Countdown 2 | 3 | React Component demonstrating a count down to a certain date. 4 | 5 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app) to test its functionality in erasing the setup effort for small React experiments. 6 | 7 | Live: [https://countdown2christmas.kristin-baumann.com/](https://countdown2christmas.kristin-baumann.com/) 8 | 9 | 10 | 11 | # Scripts 12 | 13 | ### `npm start` 14 | 15 | Runs the app in the development mode. Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 16 | 17 | ### `npm run build` 18 | 19 | Builds the app for production to the `build` folder.
20 | 21 | ## License 22 | 23 | See the [LICENSE](LICENSE.md) file for license rights and limitations (MIT). 24 | -------------------------------------------------------------------------------- /countdown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristinbaumann/react-countdown/06c2006cd554cc926e7406cb921dd82b36ed3dce/countdown.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-countdown", 3 | "version": "1.0.0", 4 | "description": "React Countdown Component", 5 | "author": "Kristin Baumann (https://www.kristin-baumann.com)", 6 | "repository": "https://github.com/kristinbaumann/react-countdown", 7 | "devDependencies": { 8 | "react-scripts": "5.0.0" 9 | }, 10 | "dependencies": { 11 | "react": "17.0.0", 12 | "react-dom": "17.0.0" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test --env=jsdom", 18 | "eject": "react-scripts eject" 19 | }, 20 | "browserslist": { 21 | "production": [ 22 | ">0.2%", 23 | "not dead", 24 | "not op_mini all" 25 | ], 26 | "development": [ 27 | "last 1 chrome version", 28 | "last 1 firefox version", 29 | "last 1 safari version" 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristinbaumann/react-countdown/06c2006cd554cc926e7406cb921dd82b36ed3dce/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Countdown Component 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Countdown from "./Countdown.js"; 3 | import logo from "./logo.svg"; 4 | import github from "./github.png"; 5 | 6 | class App extends Component { 7 | render() { 8 | const currentDate = new Date(); 9 | const year = 10 | currentDate.getMonth() === 11 && currentDate.getDate() > 23 11 | ? currentDate.getFullYear() + 1 12 | : currentDate.getFullYear(); 13 | return ( 14 |
15 |
16 | logo 17 |

React Countdown

18 | 19 | 24 | github 25 | View on Github 26 | 27 |
28 | 29 |

30 | Christmas Eve is coming soon (Midnight of 23rd to 24th Dec, UTC time): 31 |

32 | 33 |
34 | ); 35 | } 36 | } 37 | 38 | export default App; 39 | -------------------------------------------------------------------------------- /src/Countdown.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | class Countdown extends Component { 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | days: 0, 10 | hours: 0, 11 | min: 0, 12 | sec: 0, 13 | }; 14 | } 15 | 16 | componentDidMount() { 17 | // update every second 18 | this.interval = setInterval(() => { 19 | const date = this.calculateCountdown(this.props.date); 20 | date ? this.setState(date) : this.stop(); 21 | }, 1000); 22 | } 23 | 24 | componentWillUnmount() { 25 | this.stop(); 26 | } 27 | 28 | calculateCountdown(endDate) { 29 | let diff = (Date.parse(new Date(endDate)) - Date.parse(new Date())) / 1000; 30 | 31 | // clear countdown when date is reached 32 | if (diff <= 0) return false; 33 | 34 | const timeLeft = { 35 | years: 0, 36 | days: 0, 37 | hours: 0, 38 | min: 0, 39 | sec: 0, 40 | }; 41 | 42 | // calculate time difference between now and expected date 43 | if (diff >= 365.25 * 86400) { 44 | // 365.25 * 24 * 60 * 60 45 | timeLeft.years = Math.floor(diff / (365.25 * 86400)); 46 | diff -= timeLeft.years * 365.25 * 86400; 47 | } 48 | if (diff >= 86400) { 49 | // 24 * 60 * 60 50 | timeLeft.days = Math.floor(diff / 86400); 51 | diff -= timeLeft.days * 86400; 52 | } 53 | if (diff >= 3600) { 54 | // 60 * 60 55 | timeLeft.hours = Math.floor(diff / 3600); 56 | diff -= timeLeft.hours * 3600; 57 | } 58 | if (diff >= 60) { 59 | timeLeft.min = Math.floor(diff / 60); 60 | diff -= timeLeft.min * 60; 61 | } 62 | timeLeft.sec = diff; 63 | 64 | return timeLeft; 65 | } 66 | 67 | stop() { 68 | clearInterval(this.interval); 69 | } 70 | 71 | addLeadingZeros(value) { 72 | value = String(value); 73 | while (value.length < 2) { 74 | value = "0" + value; 75 | } 76 | return value; 77 | } 78 | 79 | render() { 80 | const countDown = this.state; 81 | 82 | return ( 83 |
84 | 85 | 86 | {this.addLeadingZeros(countDown.days)} 87 | {countDown.days === 1 ? "Day" : "Days"} 88 | 89 | 90 | 91 | 92 | 93 | {this.addLeadingZeros(countDown.hours)} 94 | Hours 95 | 96 | 97 | 98 | 99 | 100 | {this.addLeadingZeros(countDown.min)} 101 | Min 102 | 103 | 104 | 105 | 106 | 107 | {this.addLeadingZeros(countDown.sec)} 108 | Sec 109 | 110 | 111 |
112 | ); 113 | } 114 | } 115 | 116 | Countdown.propTypes = { 117 | date: PropTypes.string.isRequired, 118 | }; 119 | 120 | Countdown.defaultProps = { 121 | date: new Date(), 122 | }; 123 | 124 | export default Countdown; 125 | -------------------------------------------------------------------------------- /src/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristinbaumann/react-countdown/06c2006cd554cc926e7406cb921dd82b36ed3dce/src/github.png -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | text-align: center; 6 | } 7 | 8 | .App-logo { 9 | height: 80px; 10 | } 11 | 12 | .App-header { 13 | background-color: #222; 14 | padding: 20px; 15 | color: white; 16 | } 17 | 18 | .App-header .title{ 19 | margin-top: 40px; 20 | } 21 | 22 | .App-header a{ 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | color: white; 27 | transition: .2s all; 28 | } 29 | .App-header a:hover{ 30 | opacity: .7; 31 | } 32 | 33 | .githubIcon{ 34 | height: 30px; 35 | margin-right: 5px; 36 | } 37 | 38 | .Countdown{ 39 | margin: 10px auto; 40 | padding-bottom: 20px; 41 | } 42 | 43 | .Countdown-col{ 44 | display: inline-block; 45 | } 46 | 47 | .Countdown-col-element{ 48 | display: inline-block; 49 | margin: 0 20px; 50 | display: flex; 51 | flex-direction: column; 52 | } 53 | 54 | .Countdown-col-element strong{ 55 | font-size: 50px; 56 | } 57 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | --------------------------------------------------------------------------------