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