├── src
├── setupTests.js
├── index.js
├── index.css
├── Hand.js
├── __tests__
│ ├── Form.test.js
│ ├── App.test.js
│ ├── component
│ │ ├── Hand.test.js
│ │ ├── __snapshots__
│ │ │ ├── Hand.test.js.snap
│ │ │ └── AnalogClock.test.js.snap
│ │ ├── Util.test.js
│ │ ├── AnalogClock.test.js
│ │ ├── AnalogClock.unit.test.js
│ │ └── ClockComponents.test.js
│ ├── App.unit.test.js
│ ├── __snapshots__
│ │ ├── App.test.js.snap
│ │ └── Form.test.js.snap
│ └── Form.unit.test.js
├── Util.js
├── App.js
├── AnalogClock.js
├── ClockComponents.js
└── Form.js
├── public
├── manifest.json
└── index.html
├── .gitignore
├── LICENSE
├── .circleci
└── config.yml
├── package.json
└── README.md
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | import { configure } from "enzyme";
2 | import Adapter from "enzyme-adapter-react-16";
3 |
4 | configure({ adapter: new Adapter() });
5 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "AnalogClock",
3 | "name": "Analog Clock using React",
4 | "start_url": ".",
5 | "display": "standalone"
6 | }
7 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render( , document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
3 | -webkit-font-smoothing: antialiased;
4 | -moz-osx-font-smoothing: grayscale;
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/Hand.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { ClockHand, ClockHandContainer } from './ClockComponents';
3 | import Util from './Util';
4 |
5 | class Hand extends Component {
6 |
7 | constructor(props) {
8 | super(props);
9 | this.state = {};
10 | }
11 |
12 | render() {
13 | return (
14 |
15 |
17 |
18 | )
19 | }
20 |
21 | }
22 |
23 | export default Hand;
24 |
--------------------------------------------------------------------------------
/src/__tests__/Form.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Form from '../Form'
3 | import { shallow } from "enzyme";
4 |
5 | describe('Form component', () => {
6 |
7 | it('matches snapshot', () => {
8 | let options = {
9 | useCustomTime: false,
10 | width: "300px",
11 | border: true,
12 | borderColor: "#2e2e2e",
13 | baseColor: "#17a2b8",
14 | centerColor: "#459cff",
15 | centerBorderColor: "#fff",
16 | handColors: {
17 | second: "#d81c7a",
18 | minute: "#fff",
19 | hour: "#fff"
20 | },
21 | }
22 | const wrapper = shallow(
);
23 | expect(wrapper).toMatchSnapshot()
24 | });
25 |
26 | })
--------------------------------------------------------------------------------
/src/Util.js:
--------------------------------------------------------------------------------
1 | class Util {
2 | static getHandAngle(props) {
3 | let handType = props.type;
4 | let handAngle = 0;
5 | switch (handType) {
6 | case 'second': handAngle = (270 + (props.seconds * 6));
7 | break;
8 | case 'minute': handAngle = (270 + (props.minutes * 6));
9 | break;
10 | case 'hour': handAngle = (270 + (props.hours * 30) + ((props.minutes / 60) * 30));
11 | break;
12 | default: handAngle = 0;
13 | }
14 | return handAngle;
15 | }
16 |
17 | static getHourIn12HrFormat(hour) {
18 | if (hour) {
19 | if (hour > 12) {
20 | hour -= 12;
21 | }
22 | } else {
23 | hour = 0;
24 | }
25 | return hour;
26 | }
27 | }
28 |
29 | export default Util;
--------------------------------------------------------------------------------
/src/__tests__/App.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import App from '../App'
3 | import { shallow } from "enzyme";
4 |
5 | describe('App component', () => {
6 |
7 | it('matches snapshot', () => {
8 | const wrapper = shallow( );
9 | expect(wrapper).toMatchSnapshot()
10 | });
11 |
12 | it('matches snapshot when options are changed', () => {
13 | const wrapper = shallow( );
14 | let options = {
15 | useCustomTime: true,
16 | seconds: 10,
17 | minutes: 10,
18 | hours: 10,
19 | baseColor: "red",
20 | border: false,
21 | borderColor: "#fff",
22 | centerBorderColor: "black",
23 | centerColor: "blue"
24 | }
25 | wrapper.setState({ 'options': { ...wrapper.state().options, ...options } });
26 | expect(wrapper).toMatchSnapshot()
27 | });
28 | })
--------------------------------------------------------------------------------
/src/__tests__/component/Hand.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "jest-styled-components";
3 | import { shallow } from "enzyme";
4 | import "../../setupTests";
5 |
6 | import Hand from "../../Hand";
7 |
8 | describe("Hand component", () => {
9 | it("matches second Hand snapshot", () => {
10 | const wrapper = shallow(
11 |
12 | );
13 | expect(wrapper).toMatchSnapshot();
14 | });
15 |
16 | it("matches minute Hand snapshot", () => {
17 | const wrapper = shallow(
18 |
19 | );
20 | expect(wrapper).toMatchSnapshot();
21 | });
22 |
23 | it("matches hour Hand snapshot", () => {
24 | const wrapper = shallow( );
25 | expect(wrapper).toMatchSnapshot();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/src/__tests__/component/__snapshots__/Hand.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Hand component matches hour Hand snapshot 1`] = `
4 |
7 |
15 |
16 | `;
17 |
18 | exports[`Hand component matches minute Hand snapshot 1`] = `
19 |
22 |
30 |
31 | `;
32 |
33 | exports[`Hand component matches second Hand snapshot 1`] = `
34 |
37 |
45 |
46 | `;
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Vishnu Ramana
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 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 | orbs:
3 | coveralls: coveralls/coveralls@1.0.6
4 | jobs:
5 | build:
6 | docker:
7 | - image: cimg/node:17.0.1
8 | working_directory: ~/analogclock
9 | steps:
10 | - checkout
11 | - run: npm install
12 | - restore_cache:
13 | key: dependency-cache-{{ checksum "package.json" }}
14 | - run:
15 | name: install-npm-wee
16 | command: npm install
17 | - save_cache:
18 | key: dependency-cache-{{ checksum "package.json" }}
19 | paths:
20 | - ./node_modules
21 | - run:
22 | name: test
23 | command: npm run test
24 | - run:
25 | name: code-coverage
26 | command: npm run coverage
27 | - coveralls/upload
28 | - store_artifacts:
29 | path: test-results
30 | prefix: tests
31 | - store_artifacts:
32 | path: coverage
33 | prefix: coverage
34 | - store_test_results:
35 | path: test-results
36 | notify:
37 | webhooks:
38 | - url: https://coveralls.io/webhook?repo_token=${process.env.COVERALLS_REPO_TOKEN}
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "analog-clock-react",
3 | "version": "1.2.2",
4 | "devDependencies": {
5 | "enzyme": "^3.11.0",
6 | "enzyme-adapter-react-16": "^1.15.6",
7 | "enzyme-to-json": "^3.6.2",
8 | "gh-pages": "^2.0.1",
9 | "jest-styled-components": "^6.3.4",
10 | "react-scripts": ">=2.1.3"
11 | },
12 | "dependencies": {
13 | "react": ">=16.7.0",
14 | "react-dom": ">=16.7.0",
15 | "styled-components": ">=4.1.3"
16 | },
17 | "scripts": {
18 | "predeploy": "yarn build",
19 | "deploy": "gh-pages -d build",
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "coverage": "react-scripts test --coverage",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": "react-app"
28 | },
29 | "jest": {
30 | "snapshotSerializers": [
31 | "enzyme-to-json/serializer"
32 | ],
33 | "coverageThreshold": {
34 | "global": {
35 | "branches": 90,
36 | "functions": 90,
37 | "lines": 90,
38 | "statements": -10
39 | }
40 | },
41 | "collectCoverageFrom": [
42 | "src/**/*.{js}",
43 | "!src/index.js"
44 | ]
45 | },
46 | "browserslist": [
47 | ">0.2%",
48 | "not dead",
49 | "not ie <= 11",
50 | "not op_mini all"
51 | ],
52 | "homepage": "https://vishnuramana.github.io/analogclock"
53 | }
54 |
--------------------------------------------------------------------------------
/src/__tests__/component/Util.test.js:
--------------------------------------------------------------------------------
1 | import Util from "../../Util";
2 |
3 | describe("Util tests", () => {
4 | it("returns hand angle as 450 when 30 seconds is passed", () => {
5 | expect(Util.getHandAngle({ type: "second", seconds: "30" })).toBe(450);
6 | });
7 |
8 | it("returns hand angle as 450 when 30 minutes is passed", () => {
9 | expect(Util.getHandAngle({ type: "minute", minutes: "30" })).toBe(450);
10 | });
11 |
12 | it("returns hand angle as 450 when 6 hours is passed", () => {
13 | expect(Util.getHandAngle({ type: "hour", hours: "6", minutes: "0" })).toBe(450);
14 | });
15 |
16 | it("returns hand angle as 450 when 6 hours is passed", () => {
17 | expect(Util.getHandAngle({ type: "hour", hours: "6", minutes: "31" })).toBe(465.5);
18 | });
19 |
20 | it("returns hand angle as 0 when type is not passed", () => {
21 | expect(
22 | Util.getHandAngle({ hours: "6", minutes: "30", seconds: "30" })
23 | ).toBe(0);
24 | });
25 |
26 | it("returns 0 when passed hour is invalid", () => {
27 | expect(
28 | Util.getHourIn12HrFormat(undefined)
29 | ).toBe(0);
30 | });
31 |
32 | it("returns passed hour when time is less than 12 hours", () => {
33 | expect(
34 | Util.getHourIn12HrFormat(10)
35 | ).toBe(10);
36 | });
37 |
38 | it("returns passed hour-12 when time is greater than 12 hours", () => {
39 | expect(
40 | Util.getHourIn12HrFormat(13)
41 | ).toBe(1);
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/src/__tests__/component/AnalogClock.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "jest-styled-components";
3 | import { shallow } from "enzyme";
4 | import "../../setupTests";
5 |
6 | import AnalogClock from "../../AnalogClock.js";
7 |
8 | describe("AnalogClock component", () => {
9 | const RealDate = Date;
10 |
11 | afterEach(() => {
12 | global.Date = RealDate;
13 | });
14 |
15 | it("matches snapshot", () => {
16 | const constantDate = new Date("2021-10-10T10:10:10");
17 |
18 | Date = class extends Date {
19 | constructor() {
20 | return constantDate;
21 | }
22 | };
23 |
24 | let options = {
25 | width: "300px",
26 | border: true,
27 | borderColor: "#2e2e2e",
28 | baseColor: "#17a2b8",
29 | centerColor: "#459cff",
30 | centerBorderColor: "#fff",
31 | handColors: {
32 | second: "#d81c7a",
33 | minute: "#fff",
34 | hour: "#fff",
35 | },
36 | };
37 | const wrapper = shallow( );
38 | expect(wrapper).toMatchSnapshot();
39 | });
40 |
41 | it("matches snapshot when custom time is passed", () => {
42 | let options = {
43 | useCustomTime: true,
44 | seconds: 22,
45 | minutes: 22,
46 | hours: 22,
47 | width: "300px",
48 | border: true,
49 | borderColor: "#2e2e2e",
50 | baseColor: "#17a2b8",
51 | centerColor: "#459cff",
52 | centerBorderColor: "#fff",
53 | handColors: {
54 | second: "#d81c7a",
55 | minute: "#fff",
56 | hour: "#fff",
57 | },
58 | };
59 | const wrapper = shallow( );
60 | expect(wrapper).toMatchSnapshot();
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Analog Clock built using React - Demo
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | You need to enable JavaScript to run this app.
18 |
19 |
20 |
Analog Clock built using React!
21 |
This is an analog clock completely built using React. It is customizable by passing an
22 | options JSON Object
23 | to the component.
24 |
25 |
You can try the different customization options by using the form below. Once you are done, you can copy
26 | the JSON object generated for your clock !
27 |
View on Github
29 |
30 |
31 |
32 |
33 |
34 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Form from './Form';
3 | import AnalogClock from './AnalogClock.js'
4 |
5 |
6 | class App extends Component {
7 |
8 | interval = null;
9 | constructor(props) {
10 | super(props);
11 | this.state = {
12 | options: {
13 | useCustomTime: false,
14 | width: "300px",
15 | border: true,
16 | borderColor: "#2e2e2e",
17 | baseColor: "#17a2b8",
18 | centerColor: "#459cff",
19 | centerBorderColor: "#ffffff",
20 | handColors: {
21 | second: "#d81c7a",
22 | minute: "#ffffff",
23 | hour: "#ffffff"
24 | },
25 | }
26 | };
27 | }
28 |
29 |
30 | updateClock() {
31 | let ausTime = new Date().toLocaleString("en-US", { timeZone: "Australia/Brisbane" });
32 | let date = new Date(ausTime);
33 |
34 | this.setState({
35 | 'options': {
36 | ...this.state.options,
37 | seconds: date.getSeconds(),
38 | minutes: date.getMinutes(),
39 | hours: date.getHours()
40 | }
41 | })
42 | }
43 |
44 | customizeClock(options) {
45 | let _options = options;
46 | if (_options.useCustomTime) {
47 | this.interval = setInterval(() => this.updateClock(), 1000);
48 | } else {
49 | clearInterval(this.interval);
50 | delete _options.seconds;
51 | delete _options.minutes;
52 | delete _options.hours;
53 | }
54 | this.setState({ options: { ..._options } });
55 | }
56 |
57 | render() {
58 | return (
59 |
60 |
61 |
63 |
64 |
65 |
Options
66 |
67 |
68 |
69 |
Preview
70 |
71 | {this.state.options.useCustomTime ?
Timezone: Australia/Brisbane
: null}
72 |
73 |
74 |
75 | );
76 | }
77 |
78 | componentWillUnmount() {
79 | clearInterval(this.interval);
80 | }
81 | }
82 |
83 | export default App;
--------------------------------------------------------------------------------
/src/__tests__/App.unit.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "jest-styled-components";
3 | import { mount } from "enzyme";
4 | import "../setupTests";
5 |
6 | import App from '../App.js';
7 |
8 | describe("App component", () => {
9 | const renderSpy = jest.spyOn(App.prototype, "render");
10 | const customizeClockSpy = jest.spyOn(App.prototype, "customizeClock");
11 | const updateClockSpy = jest.spyOn(App.prototype, "updateClock");
12 |
13 | beforeEach(() => {
14 | jest.useFakeTimers();
15 | jest.spyOn(global, "setInterval");
16 | jest.spyOn(global, "clearInterval");
17 | })
18 |
19 | afterEach(() => {
20 | renderSpy.mockClear();
21 | customizeClockSpy.mockClear();
22 | updateClockSpy.mockClear();
23 | jest.useRealTimers();
24 | });
25 |
26 | it("mounts the component", () => {
27 | const wrapper = mount( );
28 | expect(renderSpy).toHaveBeenCalledTimes(1);
29 | });
30 |
31 | it("updates the clock when customize button is clicked", () => {
32 | let options = {
33 | width: "500px",
34 | border: false,
35 | useCustomTime: true,
36 | seconds: 10,
37 | minutes: 10,
38 | hours: 10
39 | };
40 | const wrapper = mount( );
41 | const formWrapper = wrapper.find('Form');
42 | formWrapper.setState({ 'options': { ...wrapper.state().options, ...options } });
43 | formWrapper.find('#build').simulate('click');
44 | expect(customizeClockSpy).toHaveBeenCalledTimes(1);
45 | const updatedOptions = wrapper.find('#selected-options').text();
46 | jest.advanceTimersByTime(1000);
47 | wrapper.unmount();
48 | expect(updatedOptions.indexOf('"useCustomTime": true')).toBeTruthy();
49 | expect(updateClockSpy).toHaveBeenCalledTimes(1);
50 | });
51 |
52 | it("uses internal timer when useCustomTime is turned back to false", () => {
53 | let options = {
54 | useCustomTime: true,
55 | seconds: 10,
56 | minutes: 10,
57 | hours: 10
58 | };
59 | const wrapper = mount( );
60 | const formWrapper = wrapper.find('Form');
61 | formWrapper.setState({ 'options': { ...wrapper.state().options, ...options } });
62 | formWrapper.find('#build').simulate('click');
63 | expect(customizeClockSpy).toHaveBeenCalledTimes(1);
64 | jest.advanceTimersByTime(1000);
65 | expect(updateClockSpy).toHaveBeenCalledTimes(1);
66 | formWrapper.setState({ 'options': { ...wrapper.state().options, ...options, useCustomTime: false } });
67 | formWrapper.find('#build').simulate('click');
68 | jest.advanceTimersByTime(500);
69 | expect(updateClockSpy).toHaveBeenCalledTimes(1);
70 | });
71 |
72 | });
73 |
--------------------------------------------------------------------------------
/src/__tests__/component/__snapshots__/AnalogClock.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`AnalogClock component matches snapshot 1`] = `
4 |
7 |
11 |
14 |
18 |
29 |
40 |
52 |
53 |
54 |
55 | `;
56 |
57 | exports[`AnalogClock component matches snapshot when custom time is passed 1`] = `
58 |
61 |
65 |
68 |
72 |
83 |
94 |
106 |
107 |
108 |
109 | `;
110 |
--------------------------------------------------------------------------------
/src/AnalogClock.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Util from './Util';
3 | import { ClockContainer, ClockBaseBorder, ClockBase, ClockCenter } from './ClockComponents';
4 | import Hand from './Hand.js';
5 |
6 | class AnalogClock extends Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | this.state = this.initClock();
11 | }
12 |
13 | initClock() {
14 | const date = new Date();
15 | return {
16 | seconds: date.getSeconds(),
17 | minutes: date.getMinutes(),
18 | hours: Util.getHourIn12HrFormat(date.getHours())
19 | }
20 | }
21 |
22 | setupTime() {
23 | const date = new Date();
24 | this.setState({
25 | seconds: date.getSeconds(),
26 | minutes: date.getMinutes(),
27 | hours: Util.getHourIn12HrFormat(date.getHours())
28 | })
29 | }
30 |
31 | setupInterval() {
32 | this.interval = setInterval(() => this.setupTime(), 1000);
33 | }
34 |
35 | componentDidMount() {
36 | this.setupInterval();
37 | }
38 |
39 | componentDidUpdate(prevProps) {
40 | if (prevProps.useCustomTime !== this.props.useCustomTime) {
41 | if (this.props.useCustomTime) {
42 | clearInterval(this.interval);
43 | this.setState({
44 | seconds: undefined,
45 | minutes: undefined,
46 | hours: undefined
47 | });
48 | } else {
49 | this.setupInterval();
50 | }
51 | }
52 | }
53 |
54 | render() {
55 | const { width, border, borderColor, baseColor, centerColor, centerBorderColor, handColors } = this.props;
56 | const { seconds, minutes, hours } = this.props.useCustomTime ? this.props : this.state;
57 | return (
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | )
70 | }
71 |
72 | componentWillUnmount() {
73 | clearInterval(this.interval);
74 | }
75 | }
76 |
77 | export default AnalogClock;
--------------------------------------------------------------------------------
/src/ClockComponents.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | const ClockContainer = styled.div`
4 | height: ${props => props.width || "400px"};
5 | width: ${props => props.width || "400px"};
6 | `;
7 |
8 | const ClockBaseBorder = styled.div`
9 | position: relative;
10 | box-sizing: border-box;
11 | height: 100%;
12 | width: 100%;
13 | background-color: ${props => props.borderColor ? props.borderColor : "transparent"};
14 | padding: ${props => props.border ? "5%" : "0"};
15 | border-radius: 100%;
16 | `;
17 | const ClockBase = styled.div`
18 | position: relative;
19 | height: 100%;
20 | width: 100%;
21 | background-color: ${props => props.baseColor ? props.baseColor : "black"};
22 | border-radius: 100%;
23 | `;
24 | const ClockCenter = styled.div`
25 | position: absolute;
26 | left: 50%;
27 | top: 50%;
28 | width: 12px;
29 | height: 12px;
30 | border: 2px solid ${props => props.centerBorderColor ? props.centerBorderColor : "#fff"};;
31 | background-color: ${props => props.centerColor ? props.centerColor : "#459cff"};
32 | border-radius: 100%;
33 | margin-left: -6px;
34 | margin-top: -6px;
35 | z-index: 100;
36 | `;
37 |
38 | const ClockHand = styled.div`
39 | box-sizing: border-box;
40 | height: 1.5%;
41 | min-height: 2px;
42 | max-height: 6px;
43 |
44 | ${props => props.type === 'second' && css`
45 | width: 60%;
46 | margin-left: 40%;
47 | background-color: ${props => props.handColors && props.handColors.second ? props.handColors.second : "#d81c7a"};
48 | `}
49 | ${props => props.type === 'minute' && css`
50 | width: 45%;
51 | margin-left: 45%;
52 | background-color: ${props => props.handColors && props.handColors.minute ? props.handColors.minute : "#fff"};
53 | `}
54 | ${props => props.type === 'hour' && css`
55 | width: 35%;
56 | margin-left: 45%;
57 | background-color: ${props => props.handColors && props.handColors.hour ? props.handColors.hour : "#fff"};
58 | `}
59 | `;
60 |
61 | const ClockHandContainer = styled.div`
62 | position: absolute;
63 | width: 100%
64 | height: 100%;
65 | display: flex;
66 | align-items: center;
67 | transform: rotate(${props => props.handAngle}deg);
68 | transition: ${props => props.handAngle > 270 && 'transform 250ms ease-in-out'};
69 | `;
70 |
71 | export { ClockContainer, ClockBaseBorder, ClockBase, ClockCenter, ClockHand, ClockHandContainer };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Analog Clock using React
2 |
3 | [](https://circleci.com/gh/vishnuramana/analogclock/tree/dev) [](https://coveralls.io/github/vishnuramana/analogclock?branch=dev) [](https://www.npmjs.com/package/analog-clock-react)  [](https://github.com/vishnuramana/analogclock/blob/dev/LICENSE)
4 |
5 | This is a customizable analog clock completely built using React. It is customizable by passing an options JSON Object to the component which allows you to modify the clock colors. It also allows you to use multiple timezones.
6 |
7 | 
8 |
9 | ## Demo
10 | You can customize/view a live demo of the clock [here](http://vishnuramana.github.io/analogclock).
11 |
12 | ## Installation
13 | Install the package using
14 |
15 | npm install --save analog-clock-react
16 |
17 | ## Usage
18 | Import the `AnalogClock` component into your React Component like this
19 |
20 | import AnalogClock from 'analog-clock-react';
21 |
22 | Then use the `AnalogClock` Component like this
23 |
24 | let options = {
25 | width: "300px",
26 | border: true,
27 | borderColor: "#2e2e2e",
28 | baseColor: "#17a2b8",
29 | centerColor: "#459cff",
30 | centerBorderColor: "#ffffff",
31 | handColors: {
32 | second: "#d81c7a",
33 | minute: "#ffffff",
34 | hour: "#ffffff"
35 | }
36 | };
37 | .....
38 |
39 |
40 | You can customize the clock using the different properties in the `options` JSON Object.
41 |
42 | Please visit the [demo](http://vishnuramana.github.io/analogclock) page to get a live preview of the clock.
43 |
44 | ## Features
45 |
46 | - ### Custom Time / Timezone Support
47 | *AnalogClock* now supports using your own custom time/timezone. To enable custom time usage, set `useCustomTime` to `true` and set your own `hours`, `minutes` and `seconds` in the `options` JSON Object like below:
48 |
49 | let options = {
50 | useCustomTime: true, // set this to true
51 | width: "300px",
52 | border: true,
53 | borderColor: "#2e2e2e",
54 | baseColor: "#17a2b8",
55 | centerColor: "#459cff",
56 | centerBorderColor: "#ffffff",
57 | handColors: {
58 | second: "#d81c7a",
59 | minute: "#ffffff",
60 | hour: "#ffffff"
61 | },
62 | "seconds": 1, // set your
63 | "minutes": 10, // own
64 | "hours": 22 // time here.
65 | };
66 |
67 | Once you do that, `AnalogClock` will expect you to give it the `hours`, `minutes` and `seconds` value.
68 |
69 | *Note: You will have to use a setInterval in the component where you use `` to update the `options` object with the time that you pass. An example is given below:*
70 |
71 | updateClock = () => {
72 | let ausTime = new Date().toLocaleString("en-US", { timeZone: "Australia/Brisbane" });
73 | let date = new Date(ausTime);
74 |
75 | this.setState({
76 | 'options': {
77 | ...this.state.options,
78 | seconds: date.getSeconds(),
79 | minutes: date.getMinutes(),
80 | hours: date.getHours()
81 | }
82 | })
83 | }
84 | ....
85 | this.interval = setInterval(this.updateClock, 1000);
86 |
87 | ## Change Log
88 | - **v1.3.0**
89 | - Added finer movement of hour hand
90 |
91 | - **v1.2.2**
92 | - Fixed clock hand centering issues
93 | - Removed unwanted code
94 |
95 | ## Contribution
96 |
97 | If you wish to contribute to this project, please use the `dev` branch to add your changes and test. Make sure all the tests are passed and optimal code coverage is present. Once you are done with your changes, please raise a PR.
98 |
99 | ## Issues/Feature Requests
100 |
101 | For any issues/feature-requests, you can create an issue in Github or email me at [me@vishnu.codes](mailto:me@vishnu.codes)
102 |
103 | ## License
104 |
105 | MIT License
106 |
--------------------------------------------------------------------------------
/src/__tests__/component/AnalogClock.unit.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "jest-styled-components";
3 | import { mount } from "enzyme";
4 | import "../../setupTests";
5 |
6 | import AnalogClock from "../../AnalogClock.js";
7 |
8 | describe("AnalogClock component", () => {
9 | const initClockSpy = jest.spyOn(AnalogClock.prototype, "initClock");
10 | const setupIntervalSpy = jest.spyOn(AnalogClock.prototype, "setupInterval");
11 | const setupTimeSpy = jest.spyOn(AnalogClock.prototype, "setupTime");
12 | const componentWillUnmountSpy = jest.spyOn(
13 | AnalogClock.prototype,
14 | "componentWillUnmount"
15 | );
16 |
17 | beforeEach(() => {
18 | jest.useFakeTimers();
19 | })
20 |
21 |
22 | afterEach(() => {
23 | initClockSpy.mockClear();
24 | setupIntervalSpy.mockClear();
25 | componentWillUnmountSpy.mockClear();
26 | setupTimeSpy.mockClear();
27 | jest.useRealTimers();
28 | });
29 |
30 | it("mounts the component", () => {
31 | let options = {
32 | width: "300px",
33 | border: true,
34 | borderColor: "#2e2e2e",
35 | baseColor: "#17a2b8",
36 | centerColor: "#459cff",
37 | centerBorderColor: "#fff",
38 | handColors: {
39 | second: "#d81c7a",
40 | minute: "#fff",
41 | hour: "#fff",
42 | },
43 | };
44 | const wrapper = mount( );
45 | expect(initClockSpy).toHaveBeenCalledTimes(1);
46 | expect(setupIntervalSpy).toHaveBeenCalledTimes(1);
47 | expect(setInterval).toHaveBeenCalledTimes(1);
48 | expect(setInterval).toHaveBeenLastCalledWith(expect.any(Function), 1000);
49 | jest.advanceTimersByTime(1001);
50 | expect(setupTimeSpy).toHaveBeenCalledTimes(1)
51 | });
52 |
53 | it("uses 12 hr time format", () => {
54 | const constantDate = new Date("2021-10-10T14:14:14");
55 |
56 | Date = class extends Date {
57 | constructor() {
58 | return constantDate;
59 | }
60 | };
61 |
62 | let options = {
63 | width: "300px",
64 | border: true,
65 | borderColor: "#2e2e2e",
66 | baseColor: "#17a2b8",
67 | centerColor: "#459cff",
68 | centerBorderColor: "#fff",
69 | handColors: {
70 | second: "#d81c7a",
71 | minute: "#fff",
72 | hour: "#fff",
73 | },
74 | };
75 | const wrapper = mount( );
76 | expect(setInterval).toHaveBeenCalledTimes(1);
77 | expect(setInterval).toHaveBeenLastCalledWith(expect.any(Function), 1000);
78 | expect(wrapper.state("hours")).toEqual(2);
79 | jest.advanceTimersByTime(1500);
80 | expect(wrapper.state("hours")).toEqual(2);
81 | });
82 |
83 | it("unmounts the component", () => {
84 | let options = {
85 | width: "300px",
86 | border: true,
87 | borderColor: "#2e2e2e",
88 | baseColor: "#17a2b8",
89 | centerColor: "#459cff",
90 | centerBorderColor: "#fff",
91 | handColors: {
92 | second: "#d81c7a",
93 | minute: "#fff",
94 | hour: "#fff",
95 | },
96 | };
97 | const wrapper = mount( );
98 | wrapper.unmount();
99 | expect(componentWillUnmountSpy).toHaveBeenCalledTimes(1);
100 | expect(clearInterval).toHaveBeenCalledTimes(1);
101 | });
102 |
103 | it("sets custom time when useCustomTime is set to true", () => {
104 | let options = {
105 | width: "300px",
106 | border: true,
107 | borderColor: "#2e2e2e",
108 | baseColor: "#17a2b8",
109 | centerColor: "#459cff",
110 | centerBorderColor: "#fff",
111 | handColors: {
112 | second: "#d81c7a",
113 | minute: "#fff",
114 | hour: "#fff",
115 | },
116 | };
117 |
118 | const wrapper = mount( );
119 | wrapper.setProps({
120 | ...options,
121 | useCustomTime: true,
122 | seconds: 22,
123 | minutes: 22,
124 | hours: 22,
125 | });
126 | expect(setupIntervalSpy).toHaveBeenCalledTimes(1);
127 | expect(clearInterval).toHaveBeenCalledTimes(1);
128 | });
129 |
130 | it("sets current time when useCustomTime is set to false", () => {
131 | let options = {
132 | useCustomTime: true,
133 | seconds: 22,
134 | minutes: 22,
135 | hours: 22,
136 | width: "300px",
137 | border: true,
138 | borderColor: "#2e2e2e",
139 | baseColor: "#17a2b8",
140 | centerColor: "#459cff",
141 | centerBorderColor: "#fff",
142 | handColors: {
143 | second: "#d81c7a",
144 | minute: "#fff",
145 | hour: "#fff",
146 | },
147 | };
148 |
149 | const wrapper = mount( );
150 | wrapper.setProps({
151 | ...options,
152 | useCustomTime: false,
153 | });
154 | expect(setupIntervalSpy).toHaveBeenCalledTimes(2);
155 | });
156 | });
157 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/App.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`App component matches snapshot 1`] = `
4 |
5 |
8 |
27 |
28 |
31 |
34 |
35 |
36 | Options
37 |
38 |
39 |
59 |
60 |
63 |
64 |
65 | Preview
66 |
67 |
68 |
84 |
85 |
86 |
87 | `;
88 |
89 | exports[`App component matches snapshot when options are changed 1`] = `
90 |
91 |
94 |
116 |
117 |
120 |
123 |
124 |
125 | Options
126 |
127 |
128 |
151 |
152 |
155 |
156 |
157 | Preview
158 |
159 |
160 |
179 |
187 |
188 | Timezone: Australia/Brisbane
189 |
190 |
191 |
192 |
193 |
194 | `;
195 |
--------------------------------------------------------------------------------
/src/__tests__/component/ClockComponents.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "jest-styled-components";
3 | import { mount } from "enzyme";
4 | import "../../setupTests";
5 |
6 | import {
7 | ClockContainer,
8 | ClockBaseBorder,
9 | ClockBase,
10 | ClockCenter,
11 | ClockHand,
12 | ClockHandContainer,
13 | } from "../../ClockComponents";
14 |
15 | describe("ClockComponents tests", () => {
16 | it("sets width and height of ClockContainer when width prop is passed", () => {
17 | const wrapper = mount( );
18 | expect(wrapper).toHaveStyleRule("width", "200px");
19 | expect(wrapper).toHaveStyleRule("height", "200px");
20 | });
21 |
22 | it("sets default width of ClockContainer when width prop is not passed", () => {
23 | const wrapper = mount( );
24 | expect(wrapper).toHaveStyleRule("width", "400px");
25 | expect(wrapper).toHaveStyleRule("height", "400px");
26 | });
27 |
28 | it("sets border and border color of ClockBaseBorder when props passed", () => {
29 | const wrapper = mount( );
30 | expect(wrapper).toHaveStyleRule("background-color", "#fff");
31 | expect(wrapper).toHaveStyleRule("padding", "5%");
32 | });
33 |
34 | it("sets default border and border color of ClockBaseBorder when props is not passed", () => {
35 | const wrapper = mount( );
36 | expect(wrapper).toHaveStyleRule("background-color", "transparent");
37 | expect(wrapper).toHaveStyleRule("padding", "0");
38 | });
39 |
40 | it("sets base color of ClockBase when props passed", () => {
41 | const wrapper = mount( );
42 | expect(wrapper).toHaveStyleRule("background-color", "#fff");
43 | });
44 |
45 | it("sets default base color of ClockBase when props is not passed", () => {
46 | const wrapper = mount( );
47 | expect(wrapper).toHaveStyleRule("background-color", "black");
48 | });
49 |
50 | it("sets border and background color of ClockCenter when props passed", () => {
51 | const wrapper = mount(
52 |
53 | );
54 | expect(wrapper).toHaveStyleRule("border", "2px solid red");
55 | expect(wrapper).toHaveStyleRule("background-color", "black");
56 | });
57 |
58 | it("sets default border and background color of ClockCenter when props is not passed", () => {
59 | const wrapper = mount( );
60 | expect(wrapper).toHaveStyleRule("border", "2px solid #fff");
61 | expect(wrapper).toHaveStyleRule("background-color", "#459cff");
62 | });
63 |
64 | it("sets styling of ClockHand when props passed - second", () => {
65 | const wrapper = mount(
66 |
67 | );
68 | expect(wrapper).toHaveStyleRule("width", "60%");
69 | expect(wrapper).toHaveStyleRule("margin-left", "40%");
70 | expect(wrapper).toHaveStyleRule("background-color", "#fff");
71 | });
72 |
73 | it("sets default styling of ClockHand when props is not passed - second", () => {
74 | const wrapper = mount( );
75 | expect(wrapper).toHaveStyleRule("width", "60%");
76 | expect(wrapper).toHaveStyleRule("margin-left", "40%");
77 | expect(wrapper).toHaveStyleRule("background-color", "#d81c7a");
78 | });
79 |
80 | it("sets styling of ClockHand when props passed - minute", () => {
81 | const wrapper = mount(
82 |
83 | );
84 | expect(wrapper).toHaveStyleRule("width", "45%");
85 | expect(wrapper).toHaveStyleRule("margin-left", "45%");
86 | expect(wrapper).toHaveStyleRule("background-color", "red");
87 | });
88 |
89 | it("sets default styling of ClockHand when props is not passed - minute", () => {
90 | const wrapper = mount( );
91 | expect(wrapper).toHaveStyleRule("width", "45%");
92 | expect(wrapper).toHaveStyleRule("margin-left", "45%");
93 | expect(wrapper).toHaveStyleRule("background-color", "#fff");
94 | });
95 |
96 | it("sets styling of ClockHand when props passed - hour", () => {
97 | const wrapper = mount(
98 |
99 | );
100 | expect(wrapper).toHaveStyleRule("width", "35%");
101 | expect(wrapper).toHaveStyleRule("margin-left", "45%");
102 | expect(wrapper).toHaveStyleRule("background-color", "red");
103 | });
104 |
105 | it("sets default styling of ClockHand when props is not passed - hour", () => {
106 | const wrapper = mount( );
107 | expect(wrapper).toHaveStyleRule("width", "35%");
108 | expect(wrapper).toHaveStyleRule("margin-left", "45%");
109 | expect(wrapper).toHaveStyleRule("background-color", "#fff");
110 | });
111 |
112 | it("sets transform and transition of ClockHandContainer when props passed", () => {
113 | const wrapper = mount( );
114 | expect(wrapper).toHaveStyleRule("transform", "rotate(300deg)");
115 | expect(wrapper).toHaveStyleRule(
116 | "transition",
117 | "transform 250ms ease-in-out"
118 | );
119 | });
120 |
121 | it("sets default transform of ClockHandContainer when props is not passed", () => {
122 | const wrapper = mount( );
123 | expect(wrapper).toHaveStyleRule("transform", "rotate(deg)");
124 | expect(wrapper).toHaveStyleRule("transition", undefined);
125 | });
126 | });
127 |
--------------------------------------------------------------------------------
/src/__tests__/Form.unit.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { mount } from "enzyme";
3 | import "../setupTests";
4 |
5 | import Form from '../Form.js';
6 |
7 | describe('Form component', () => {
8 | const renderSpy = jest.spyOn(Form.prototype, "render");
9 | const mockCustomizeClock = jest.fn();
10 |
11 | let defaultOptions = {
12 | useCustomTime: false,
13 | width: "300px",
14 | border: true,
15 | borderColor: "#2e2e2e",
16 | baseColor: "#17a2b8",
17 | centerColor: "#459cff",
18 | centerBorderColor: "#fff",
19 | handColors: {
20 | second: "#d81c7a",
21 | minute: "#fff",
22 | hour: "#fff"
23 | },
24 | }
25 |
26 | afterEach(() => {
27 | renderSpy.mockClear();
28 | mockCustomizeClock.mockClear();
29 | })
30 |
31 | it('mounts the component', () => {
32 | const wrapper = mount();
33 | expect(renderSpy).toBeCalledTimes(1);
34 | });
35 |
36 | it('sets clock size when changed', () => {
37 | const wrapper = mount();
38 | wrapper.find('input#clock-size').simulate('change', { target: { name: 'width', value: 100 } });
39 | expect(wrapper.state('options').width).toEqual('100px');
40 | });
41 |
42 | it('sets custom time to true when changed', () => {
43 | const wrapper = mount();
44 | wrapper.find('input#custom-time-yes').simulate('change');
45 | expect(wrapper.state('options').useCustomTime).toEqual(true);
46 | });
47 |
48 | it('sets custom time to false when changed', () => {
49 | const wrapper = mount();
50 | wrapper.find('input#custom-time-yes').simulate('change');
51 | expect(wrapper.state('options').useCustomTime).toEqual(true);
52 | wrapper.find('input#custom-time-no').simulate('change');
53 | expect(wrapper.state('options').useCustomTime).toEqual(false);
54 | });
55 |
56 | it('sets border to false when changed', () => {
57 | const wrapper = mount();
58 | wrapper.find('input#border-req-no').simulate('change');
59 | expect(wrapper.state('options').border).toEqual(false);
60 | });
61 |
62 | it('sets border to true when changed', () => {
63 | const wrapper = mount();
64 | wrapper.find('input#border-req-no').simulate('change');
65 | expect(wrapper.state('options').border).toEqual(false);
66 | wrapper.find('input#border-req-yes').simulate('change');
67 | expect(wrapper.state('options').border).toEqual(true);
68 | });
69 |
70 | it('sets clock border color when changed', () => {
71 | const wrapper = mount();
72 | wrapper.setProps({
73 | customizeClock: mockCustomizeClock
74 | });
75 | wrapper.find('input[name="borderColor"]').simulate('change', { target: { name: 'borderColor', value: '#fff' } });
76 | expect(wrapper.state('options').borderColor).toEqual('#fff');
77 | expect(mockCustomizeClock).toBeCalledTimes(1);
78 | });
79 |
80 | it('sets clock base color when changed', () => {
81 | const wrapper = mount();
82 | wrapper.setProps({
83 | customizeClock: mockCustomizeClock
84 | });
85 | wrapper.find('input[name="baseColor"]').simulate('change', { target: { name: 'baseColor', value: '#fff' } });
86 | expect(wrapper.state('options').baseColor).toEqual('#fff');
87 | expect(mockCustomizeClock).toBeCalledTimes(1);
88 | });
89 |
90 | it('sets clock center color when changed', () => {
91 | const wrapper = mount();
92 | wrapper.setProps({
93 | customizeClock: mockCustomizeClock
94 | });
95 | wrapper.find('input[name="centerColor"]').simulate('change', { target: { name: 'centerColor', value: '#fff' } });
96 | expect(wrapper.state('options').centerColor).toEqual('#fff');
97 | expect(mockCustomizeClock).toBeCalledTimes(1);
98 | });
99 |
100 | it('sets clock center border color when changed', () => {
101 | const wrapper = mount();
102 | wrapper.setProps({
103 | customizeClock: mockCustomizeClock
104 | });
105 | wrapper.find('input[name="centerBorderColor"]').simulate('change', { target: { name: 'centerBorderColor', value: '#fff' } });
106 | expect(wrapper.state('options').centerBorderColor).toEqual('#fff');
107 | expect(mockCustomizeClock).toBeCalledTimes(1);
108 | });
109 |
110 | it('sets clock second hand color when changed', () => {
111 | const wrapper = mount();
112 | wrapper.setProps({
113 | customizeClock: mockCustomizeClock
114 | });
115 | wrapper.find('input[name="second"]').simulate('change', { target: { name: 'second', value: '#fff' } });
116 | expect(wrapper.state('options').handColors.second).toEqual('#fff');
117 | expect(mockCustomizeClock).toBeCalledTimes(1);
118 | });
119 |
120 | it('sets clock minute hand color when changed', () => {
121 | const wrapper = mount();
122 | wrapper.setProps({
123 | customizeClock: mockCustomizeClock
124 | });
125 | wrapper.find('input[name="minute"]').simulate('change', { target: { name: 'minute', value: '#fff' } });
126 | expect(wrapper.state('options').handColors.minute).toEqual('#fff');
127 | expect(mockCustomizeClock).toBeCalledTimes(1);
128 | });
129 |
130 | it('sets clock hour hand border color when changed', () => {
131 | const wrapper = mount();
132 | wrapper.setProps({
133 | customizeClock: mockCustomizeClock
134 | });
135 | wrapper.find('input[name="hour"]').simulate('change', { target: { name: 'hour', value: '#fff' } });
136 | expect(wrapper.state('options').handColors.hour).toEqual('#fff');
137 | expect(mockCustomizeClock).toBeCalledTimes(1);
138 | });
139 |
140 | it('sets random clock colors Surprise Me button is clicked', () => {
141 | const wrapper = mount();
142 | wrapper.setProps({
143 | customizeClock: mockCustomizeClock
144 | });
145 | wrapper.find('button#surprise').simulate('click')
146 |
147 | expect(wrapper.state('options').borderColor).not.toEqual('#2e2e2e');
148 | expect(wrapper.state('options').baseColor).not.toEqual('#17a2b8');
149 | expect(wrapper.state('options').centerColor).not.toEqual('#459cff');
150 | expect(wrapper.state('options').centerBorderColor).not.toEqual('#fff');
151 | expect(wrapper.state('options').handColors.second).not.toEqual('#d81c7a');
152 | expect(wrapper.state('options').handColors.minute).not.toEqual('#fff');
153 | expect(wrapper.state('options').handColors.hour).not.toEqual('#fff');
154 | expect(mockCustomizeClock).toBeCalledTimes(1);
155 | });
156 | });
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/Form.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Form component matches snapshot 1`] = `
4 |
301 | `;
302 |
--------------------------------------------------------------------------------
/src/Form.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class Form extends Component {
4 |
5 | constructor(props) {
6 | super(props);
7 | this.state = {
8 | options: this.props.defaultOptions
9 | };
10 | }
11 |
12 | setClockSize = (event) => {
13 | event.preventDefault();
14 | let width = event.target.value + 'px';
15 | this.setState({ options: { ...this.state.options, width } })
16 | }
17 |
18 | setCustomTime = (event) => {
19 | let useCustomTime = event.target.value === 'yes';
20 | this.setState({ options: { ...this.state.options, useCustomTime } })
21 | }
22 |
23 |
24 | setBorderReq = (event) => {
25 | let border = event.target.value === 'yes';
26 | this.setState({ options: { ...this.state.options, border } })
27 | }
28 |
29 | setColor = (event) => {
30 | event.preventDefault();
31 | this.setState({ options: { ...this.state.options, [event.target.name]: event.target.value } })
32 | this.props.customizeClock(this.state.options);
33 | }
34 |
35 | setHandColor = (event) => {
36 | event.preventDefault();
37 | let handColors = { ...this.state.options.handColors };
38 | handColors[event.target.name] = event.target.value;
39 | this.setState({ options: { ...this.state.options, handColors } });
40 | this.props.customizeClock(this.state.options);
41 | }
42 |
43 | buildClock = (event) => {
44 | event.preventDefault();
45 | event.stopPropagation();
46 | this.props.customizeClock(this.state.options);
47 | }
48 |
49 | randomClock = (event) => {
50 | event.preventDefault();
51 | event.stopPropagation();
52 | let baseColor = this.getRandomColor();
53 | let borderColor = this.getRandomColor();
54 | let centerColor = this.getRandomColor();
55 | let centerBorderColor = this.getRandomColor();
56 | let handColors = {
57 | second: this.getRandomColor(),
58 | minute: this.getRandomColor(),
59 | hour: this.getRandomColor()
60 | }
61 | this.setState({ options: { ...this.state.options, borderColor, baseColor, centerColor, centerBorderColor, handColors } }, () => this.buildClock(event));
62 | }
63 |
64 | getRandomColor() {
65 | var letters = '0123456789ABCDEF';
66 | var colorCode = '#';
67 | for (var i = 0; i < 6; i++) {
68 | colorCode += letters[Math.floor(Math.random() * 16)];
69 | }
70 | return colorCode;
71 | }
72 |
73 | render() {
74 | return (
75 |
147 | )
148 | }
149 |
150 | }
151 |
152 | export default Form;
--------------------------------------------------------------------------------