├── .eslintignore ├── .eslintrc ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src ├── components │ ├── App.js │ ├── Clock.js │ ├── Display.js │ ├── Header.js │ └── Panel.js ├── favicon.ico ├── index.html ├── index.js ├── lib │ └── DateTime.js └── styles │ ├── _base.scss │ ├── _settings.scss │ ├── app.scss │ └── components │ ├── _display.scss │ ├── _header.scss │ └── _panel.scss └── webpack.config.js /.eslintignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drminnaar/react-clock-basic/a2b7fb672c025a4c9562cd7e29cdec438b6f53b6/.eslintignore -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "commonjs": true, 6 | "es6": true, 7 | "node": true 8 | }, 9 | "extends": [ "eslint:recommended", "plugin:react/recommended" ], 10 | "parserOptions": { 11 | "ecmaVersion": 6, 12 | "ecmaFeatures": { 13 | "jsx": true 14 | }, 15 | "sourceType": "module" 16 | }, 17 | "plugins": [ 18 | "react" 19 | ], 20 | "rules": { 21 | "no-console": "off", 22 | "indent": [ 23 | "error", 24 | 4 25 | ], 26 | "quotes": [ 27 | "error", 28 | "single" 29 | ], 30 | "semi": [ 31 | "error", 32 | "always" 33 | ] 34 | } 35 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://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 | /public 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | /.vscode/**/* 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Clock 2 | 3 | A basic clock that displays the current date and time 4 | 5 | Go **[here](http://react-clock-basic.drminnaar.me/)** for live demo. 6 | 7 | Component Diagram 8 | ![](https://user-images.githubusercontent.com/33935506/34461456-35fcbde6-ee33-11e7-908e-bc7c74b8bbd2.png) 9 | 10 | This project also demonstrates: 11 | 12 | * a typcial React project layout structure 13 | * babel setup and configuration 14 | * webpack setup and configuration 15 | * eslint setup and configuration 16 | * SCSS setup and configuration 17 | 18 | **Screenshots:** 19 | 20 | ... | ... 21 | --- | --- 22 | ![](https://user-images.githubusercontent.com/33935506/33753477-2fa5b182-dbf0-11e7-8608-c69a8739b09e.PNG) | ![](https://user-images.githubusercontent.com/33935506/33753478-2fd901ae-dbf0-11e7-8d7f-c9ee49145c04.PNG) 23 | 24 | --- 25 | 26 | ## Developed With 27 | 28 | * [Node.js](https://nodejs.org/en/) - Javascript runtime 29 | * [React](https://reactjs.org/) - A javascript library for building user interfaces 30 | * [Babel](https://babeljs.io/) - A transpiler for javascript 31 | * [Webpack](https://webpack.js.org/) - A module bundler 32 | * [SCSS](http://sass-lang.com/) - A css metalanguage 33 | * [Bootstrap 4](https://getbootstrap.com/) - Bootstrap is an open source toolkit for developing with HTML, CSS, and JS 34 | * [Surge]: https://surge.sh/ 35 | 36 | --- 37 | 38 | ## Related Projects 39 | 40 | * [react-starter] 41 | 42 | A basic template that consists of the essential elements that are required to start building a React application 43 | 44 | * [react-clicker] 45 | 46 | A basic React app that allows one to increase, decrease, or reset a counter 47 | 48 | * [react-timer-basic] 49 | 50 | A basic timer that will start a countdown based on an input of time in seconds 51 | 52 | * [react-timer-advanced] 53 | 54 | A basic countdown timer that offers an advanced UI experience 55 | 56 | * [react-masterminds] 57 | 58 | A basic game of guessing a number with varying degrees of difficulty 59 | 60 | * [react-movie-cards] 61 | 62 | A basic application that displays a list of movies as a list of cards 63 | 64 | * [react-calculator-standard] 65 | 66 | A calculator that provides the essential arithmetic operations, an expression builder, and a complete history of all expressions 67 | 68 | * [react-bitcoin-monitor] 69 | 70 | An app that monitors changes in the Bitcoin Price Index (BPI) 71 | 72 | * [react-weather-standard] 73 | 74 | A weather application that displays the current weather, daily forecasts, and hourly forecasts based on your current geolocation 75 | 76 | --- 77 | 78 | ## Getting Started 79 | 80 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. 81 | 82 | ### Prerequisites 83 | 84 | The following software is required to be installed on your system: 85 | 86 | * Node 8.x 87 | * Npm 3.x 88 | 89 | Type the following commands in the terminal to verify your node and npm versions 90 | 91 | ```bash 92 | node -v 93 | npm -v 94 | ``` 95 | 96 | ### Install 97 | 98 | Follow the following steps to get development environment running. 99 | 100 | * Clone _'react-clock-basic'_ repository from GitHub 101 | 102 | ```bash 103 | git clone https://github.com/drminnaar/react-clock-basic.git 104 | ``` 105 | 106 | _OR USING SSH_ 107 | 108 | ```bash 109 | git clone git@github.com:drminnaar/react-clock-basic.git 110 | ``` 111 | 112 | * Install node modules 113 | 114 | ```bash 115 | cd react-clock-basic 116 | npm install 117 | npm dedupe 118 | ``` 119 | 120 | ### Build 121 | 122 | * Build application 123 | 124 | This command will also run ESLint as part of build process. 125 | 126 | ```bash 127 | npm run build 128 | ``` 129 | 130 | * Build application and start watching for changes 131 | 132 | This command will also run ESLint as part of build process. 133 | 134 | ```bash 135 | npm run build:watch 136 | ``` 137 | 138 | ### Run ESlint 139 | 140 | * Lint project using ESLint 141 | 142 | ```bash 143 | npm run lint 144 | ``` 145 | 146 | * Lint project using ESLint, and autofix 147 | 148 | ```bash 149 | npm run lint:fix 150 | ``` 151 | 152 | ### Run 153 | 154 | * Run start 155 | 156 | This will run the _'serve'_ npm task 157 | 158 | ```bash 159 | npm start 160 | ``` 161 | 162 | * Run webpack dev server 163 | 164 | ```bash 165 | npm run serve:dev 166 | ``` 167 | 168 | * Alternatively run live-server (simple development http server with live reload capability) 169 | 170 | ```bash 171 | npm run serve 172 | ``` 173 | 174 | --- 175 | 176 | ## Versioning 177 | 178 | I use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/drminnaar/react-clock-basic/tags). 179 | 180 | ## Authors 181 | 182 | * **Douglas Minnaar** - *Initial work* - [drminnaar](https://github.com/drminnaar) 183 | 184 | [react-starter]: https://github.com/drminnaar/react-starter 185 | [react-clicker]: https://github.com/drminnaar/react-clicker 186 | [react-clock-basic]: https://github.com/drminnaar/react-clock-basic 187 | [react-timer-basic]: https://github.com/drminnaar/react-timer-basic 188 | [react-timer-advanced]: https://github.com/drminnaar/react-timer-advanced 189 | [react-masterminds]: https://github.com/drminnaar/react-masterminds 190 | [react-movie-cards]: https://github.com/drminnaar/react-movie-cards 191 | [react-calculator-standard]: https://github.com/drminnaar/react-calculator-standard 192 | [react-bitcoin-monitor]: https://github.com/drminnaar/react-bitcoin-monitor 193 | [react-weather-standard]: https://github.com/drminnaar/react-weather-standard -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-clock-basic", 3 | "version": "1.0.0", 4 | "description": "A basic clock that displays the current date and time", 5 | "scripts": { 6 | "build": "webpack -d", 7 | "build:watch": "webpack --watch", 8 | "lint": "eslint .; exit 0", 9 | "lint:fix": "eslint . --fix", 10 | "serve": "webpack -d && live-server ./public", 11 | "serve:dev": "webpack-dev-server --open", 12 | "start": "npm run serve", 13 | "test": "echo \"No tests available\" && exit 0" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/drminnaar/react-clock-basic.git" 18 | }, 19 | "license": "MIT", 20 | "babel": { 21 | "presets": [ 22 | "env", 23 | "react" 24 | ] 25 | }, 26 | "devDependencies": { 27 | "babel-cli": "^6.26.0", 28 | "babel-core": "^6.26.0", 29 | "babel-eslint": "^8.2.1", 30 | "babel-loader": "^7.1.2", 31 | "babel-preset-env": "^1.6.1", 32 | "babel-preset-react": "^6.24.1", 33 | "clean-webpack-plugin": "^0.1.18", 34 | "css-loader": "^0.28.9", 35 | "eslint": "^4.17.0", 36 | "eslint-loader": "^1.9.0", 37 | "eslint-plugin-react": "^7.6.1", 38 | "file-loader": "^1.1.6", 39 | "html-webpack-plugin": "^2.30.1", 40 | "live-server": "^1.2.0", 41 | "node-sass": "^4.7.2", 42 | "sass-loader": "^6.0.6", 43 | "style-loader": "^0.20.1", 44 | "url-loader": "^0.6.2", 45 | "webpack": "^3.10.0", 46 | "webpack-dev-server": "^2.11.1" 47 | }, 48 | "dependencies": { 49 | "react": "^16.2.0", 50 | "react-dom": "^16.2.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Clock from './Clock'; 3 | import Header from './Header'; 4 | 5 | export default class App extends Component { 6 | 7 | constructor() { 8 | super(); 9 | 10 | this.state = { 11 | title: 'React Clock' 12 | }; 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
19 | 20 |
21 | ); 22 | } 23 | } -------------------------------------------------------------------------------- /src/components/Clock.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Display from './Display'; 3 | import Panel from './Panel'; 4 | 5 | class Clock extends Component { 6 | 7 | constructor() { 8 | super(); 9 | 10 | this.state = { 11 | date: new Date(), 12 | isDateVisible: true 13 | }; 14 | 15 | this.toggleDate = this.toggleDate.bind(this); 16 | } 17 | 18 | componentDidMount() { 19 | this.startTime(); 20 | } 21 | 22 | componentWillUnmount() { 23 | clearInterval(this.timer); 24 | } 25 | 26 | startTime() { 27 | this.timer = setInterval(() => { 28 | this.setState(() => ({ date: new Date()})); 29 | }, 1000); 30 | } 31 | 32 | toggleDate() { 33 | this.setState((prevState) => ({ isDateVisible: !prevState.isDateVisible})); 34 | } 35 | 36 | render() { 37 | return ( 38 |
39 | 40 | 41 |
42 | ); 43 | } 44 | } 45 | 46 | export default Clock; -------------------------------------------------------------------------------- /src/components/Display.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import DateTime from '../lib/DateTime'; 4 | 5 | const Display = (props) => ( 6 |
7 |
8 |
9 |
10 |
{DateTime.toTimeString(props.date)}
11 | { props.isDateVisible &&
{DateTime.toDateString(props.date)}
} 12 |
13 |
14 |
15 |
16 | ); 17 | 18 | Display.defaultProps = { 19 | date: new Date(), 20 | isDateVisible: true 21 | }; 22 | 23 | Display.propTypes = { 24 | date: PropTypes.object, 25 | isDateVisible: PropTypes.bool 26 | }; 27 | 28 | export default Display; -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Header = (props) => ( 5 | 13 | ); 14 | 15 | Header.defaultProps = { 16 | title: 'Title' 17 | }; 18 | 19 | Header.propTypes = { 20 | title: PropTypes.string 21 | }; 22 | 23 | export default Header; -------------------------------------------------------------------------------- /src/components/Panel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Panel = (props) => { 5 | return ( 6 |
7 |
8 |
9 |
10 | 14 | 17 |
18 |
19 |
20 |
21 | ); 22 | }; 23 | 24 | Panel.defaultProps = { 25 | dateOn: true, 26 | toggleDate: () => {}, 27 | }; 28 | 29 | Panel.propTypes = { 30 | dateOn: PropTypes.bool, 31 | toggleDate: PropTypes.func 32 | }; 33 | 34 | export default Panel; -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drminnaar/react-clock-basic/a2b7fb672c025a4c9562cd7e29cdec438b6f53b6/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React Clock 9 | 10 | 11 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import AppComponent from './components/App'; 4 | import './styles/app.scss'; 5 | 6 | ReactDOM.render(, document.getElementById('app')); -------------------------------------------------------------------------------- /src/lib/DateTime.js: -------------------------------------------------------------------------------- 1 | export default class DateTime { 2 | 3 | constructor(date) { 4 | this.date = date ? date : new Date(); 5 | 6 | this.monthNames = [ 7 | 'January', 'February', 'March', 8 | 'April', 'May', 'June', 9 | 'July', 'August', 'September', 10 | 'October', 'November', 'December' 11 | ]; 12 | 13 | this.dayNames = [ 14 | 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 15 | 'Thursday', 'Friday', 'Saturday' 16 | ]; 17 | } 18 | 19 | hours() { 20 | return this.formatUnitOfTime(this.date.getHours()); 21 | } 22 | 23 | minutes() { 24 | return this.formatUnitOfTime(this.date.getMinutes()); 25 | } 26 | 27 | seconds() { 28 | return this.formatUnitOfTime(this.date.getSeconds()); 29 | } 30 | 31 | dayOfWeek() { 32 | return this.dayNames[this.date.getDay()]; 33 | } 34 | 35 | dayOfMonth() { 36 | return this.formatUnitOfTime(this.date.getUTCDate()); 37 | } 38 | 39 | month() { 40 | return this.monthNames[this.date.getMonth()]; 41 | } 42 | 43 | year() { 44 | return `${this.date.getFullYear()}`; 45 | } 46 | 47 | formatUnitOfTime(unitOfTime) { 48 | return unitOfTime < 10 ? `0${unitOfTime}` : `${unitOfTime}`; 49 | } 50 | 51 | static toDateString(date) { 52 | var dateTime = new DateTime(date); 53 | 54 | return `${dateTime.dayOfWeek().substring(0, 3)} ${dateTime.dayOfMonth()} ${dateTime.month()} ${dateTime.year()}`; 55 | } 56 | 57 | static toTimeString(date) { 58 | var dateTime = new DateTime(date); 59 | 60 | return `${dateTime.hours()}:${dateTime.minutes()}:${dateTime.seconds()}`; 61 | } 62 | } -------------------------------------------------------------------------------- /src/styles/_base.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: $smoke; 3 | font-family: $font-family; 4 | } -------------------------------------------------------------------------------- /src/styles/_settings.scss: -------------------------------------------------------------------------------- 1 | $font-family: 'Open Sans', Helvetica, Arial, sans-serif; 2 | $smoke: whitesmoke; -------------------------------------------------------------------------------- /src/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import '_settings.scss'; 2 | @import '_base.scss'; 3 | @import './components/_display.scss'; 4 | @import './components/_panel.scss'; 5 | @import './components/_header.scss'; -------------------------------------------------------------------------------- /src/styles/components/_display.scss: -------------------------------------------------------------------------------- 1 | $display-background-color: #282C34; 2 | $display-border-color: #32363E; 3 | $display-color: #61DAFB; 4 | 5 | .display { 6 | align-items: center; 7 | background-color: $display-background-color; 8 | border: 1rem solid $display-border-color; 9 | border-radius: 50%; 10 | display: flex; 11 | flex-direction: column; 12 | height: 14rem; 13 | justify-content: center; 14 | margin: 1rem auto; 15 | width: 14rem; 16 | } 17 | 18 | .display-date { 19 | color: $display-color; 20 | font-size: 14px; 21 | font-weight: 300; 22 | } 23 | 24 | .display-time { 25 | color: $display-color; 26 | font-size: 42px; 27 | font-weight: 600; 28 | } -------------------------------------------------------------------------------- /src/styles/components/_header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | height: 100px; 3 | } -------------------------------------------------------------------------------- /src/styles/components/_panel.scss: -------------------------------------------------------------------------------- 1 | .panel { 2 | align-items: center; 3 | display: flex; 4 | flex-direction: row; 5 | height: 4rem; 6 | justify-content: center; 7 | margin: 1rem auto; 8 | width: 14rem; 9 | } 10 | 11 | .panel-switch-text { 12 | font-size: 2rem; 13 | padding: 0.5rem; 14 | color: #333; 15 | } 16 | 17 | /** 18 | * The following block of CSS is taken directly from https://www.w3schools.com/howto/howto_css_switch.asp 19 | */ 20 | 21 | /* The switch - the box around the slider */ 22 | .switch { 23 | padding: 0.5rem; 24 | position: relative; 25 | display: inline-block; 26 | width: 60px; 27 | height: 34px; 28 | } 29 | 30 | /* Hide default HTML checkbox */ 31 | .switch input {display:none;} 32 | 33 | /* The slider */ 34 | .slider { 35 | position: absolute; 36 | cursor: pointer; 37 | top: 0; 38 | left: 0; 39 | right: 0; 40 | bottom: 0; 41 | background-color: #ccc; 42 | -webkit-transition: .4s; 43 | transition: .4s; 44 | } 45 | 46 | .slider:before { 47 | position: absolute; 48 | content: ""; 49 | height: 26px; 50 | width: 26px; 51 | left: 4px; 52 | bottom: 4px; 53 | background-color: white; 54 | -webkit-transition: .4s; 55 | transition: .4s; 56 | } 57 | 58 | input:checked + .slider { 59 | background-color: #61DAFB; 60 | } 61 | 62 | input:focus + .slider { 63 | box-shadow: 0 0 1px #61DAFB; 64 | } 65 | 66 | input:checked + .slider:before { 67 | -webkit-transform: translateX(26px); 68 | -ms-transform: translateX(26px); 69 | transform: translateX(26px); 70 | } 71 | 72 | /* Rounded sliders */ 73 | .slider.round { 74 | border-radius: 34px; 75 | } 76 | 77 | .slider.round:before { 78 | border-radius: 50%; 79 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const CleanWebPackPlugin = require('clean-webpack-plugin'); 3 | const HtmlWebPackPlugin = require('html-webpack-plugin'); 4 | 5 | module.exports = { 6 | entry: './src/index.js', 7 | output: { 8 | filename: 'bundle.js', 9 | path: path.resolve(__dirname, 'public') 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | enforce: 'pre', 15 | test: /\.js$/, 16 | loader: 'eslint-loader', 17 | options: { 18 | failOnWarning: true, 19 | failOnerror: true 20 | }, 21 | exclude: /node_modules/ 22 | }, 23 | { 24 | test: /\.js$/, 25 | loader: 'babel-loader', 26 | exclude: /node_modules/ 27 | }, 28 | { 29 | test: /\.s?css$/, 30 | use: [ 'style-loader', 'css-loader', 'sass-loader' ], 31 | exclude: /node_modules/ 32 | }, 33 | { 34 | test: /\.svg$/, 35 | loader: 'url-loader', 36 | exclude: /node_modules/ 37 | } 38 | ] 39 | }, 40 | plugins: [ 41 | new CleanWebPackPlugin([ 'public' ], { root: path.resolve(__dirname)}), 42 | new HtmlWebPackPlugin({ 43 | template: './src/index.html', 44 | favicon: './src/favicon.ico', 45 | inject: false 46 | }) 47 | ], 48 | devtool: 'cheap-module-eval-source-map', 49 | devServer: { 50 | contentBase: path.resolve(__dirname, 'public'), 51 | compress: true, 52 | port: 9000 53 | } 54 | }; --------------------------------------------------------------------------------