Skip to content

Commit 9eeca96

Browse files
feat: port the front end sample projects (#322)
* random-quote-machine * markdown-previewer * drum-machine * javascript-calculator * 25 + 5 clock
1 parent bbff45a commit 9eeca96

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2514
-1
lines changed

apps/25--5-clock/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public/bundle.js

apps/25--5-clock/client/.babelrc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"presets": [
3+
["@babel/env", { "targets": "> 0.25%, not dead" }],
4+
"@babel/preset-react"
5+
]
6+
}

apps/25--5-clock/client/index.jsx

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/* global React, ReactDOM */
2+
/* eslint-disable react/no-multi-comp, react/prop-types, max-len */
3+
// coded by @no-stack-dub-sack (github) / @no_stack_sub_sack (codepen)
4+
5+
/** NOTES:
6+
/** Dependencies are React, ReactDOM, and
7+
Accurate_Interval.js by Squuege (external script
8+
to keep setInterval() from drifting over time &
9+
thus ensuring timer goes off at correct mark).
10+
/** Utilizes embedded <Audio> tag to ensure audio
11+
plays when timer tab is inactive or browser is
12+
minimized ( rather than new Audio().play() ).
13+
/** Audio of this fashion not supported on most
14+
mobile devices it would seem (bummer I know).
15+
**/
16+
17+
// PROJECTOR SELECTOR FOR EXTERNAL TEST SCRIPT:
18+
// eslint-disable-next-line no-unused-vars
19+
const projectName = '25-5-clock';
20+
21+
// Accurate_Interval.js
22+
// Thanks Squeege! For the elegant answer provided to this question:
23+
// http://stackoverflow.com/questions/8173580/setinterval-timing-slowly-drifts-away-from-staying-accurate
24+
// Github: https://gist.github.com/Squeegy/1d99b3cd81d610ac7351
25+
// Slightly modified to accept 'normal' interval/timeout format (func, time).
26+
27+
const accurateInterval = function (fn, time) {
28+
var cancel, nextAt, timeout, wrapper;
29+
nextAt = new Date().getTime() + time;
30+
timeout = null;
31+
wrapper = function () {
32+
nextAt += time;
33+
timeout = setTimeout(wrapper, nextAt - new Date().getTime());
34+
return fn();
35+
};
36+
cancel = function () {
37+
return clearTimeout(timeout);
38+
};
39+
timeout = setTimeout(wrapper, nextAt - new Date().getTime());
40+
return {
41+
cancel: cancel
42+
};
43+
};
44+
45+
// COMPONENTS:
46+
class TimerLengthControl extends React.Component {
47+
render() {
48+
return (
49+
<div className='length-control'>
50+
<div id={this.props.titleID}>{this.props.title}</div>
51+
<button
52+
className='btn-level'
53+
id={this.props.minID}
54+
onClick={this.props.onClick}
55+
value='-'
56+
>
57+
<i className='fa fa-arrow-down fa-2x' />
58+
</button>
59+
<div className='btn-level' id={this.props.lengthID}>
60+
{this.props.length}
61+
</div>
62+
<button
63+
className='btn-level'
64+
id={this.props.addID}
65+
onClick={this.props.onClick}
66+
value='+'
67+
>
68+
<i className='fa fa-arrow-up fa-2x' />
69+
</button>
70+
</div>
71+
);
72+
}
73+
}
74+
75+
class Timer extends React.Component {
76+
constructor(props) {
77+
super(props);
78+
this.state = {
79+
brkLength: 5,
80+
seshLength: 25,
81+
timerState: 'stopped',
82+
timerType: 'Session',
83+
timer: 1500,
84+
intervalID: '',
85+
alarmColor: { color: 'white' }
86+
};
87+
this.setBrkLength = this.setBrkLength.bind(this);
88+
this.setSeshLength = this.setSeshLength.bind(this);
89+
this.lengthControl = this.lengthControl.bind(this);
90+
this.timerControl = this.timerControl.bind(this);
91+
this.beginCountDown = this.beginCountDown.bind(this);
92+
this.decrementTimer = this.decrementTimer.bind(this);
93+
this.phaseControl = this.phaseControl.bind(this);
94+
this.warning = this.warning.bind(this);
95+
this.buzzer = this.buzzer.bind(this);
96+
this.switchTimer = this.switchTimer.bind(this);
97+
this.clockify = this.clockify.bind(this);
98+
this.reset = this.reset.bind(this);
99+
}
100+
setBrkLength(e) {
101+
this.lengthControl(
102+
'brkLength',
103+
e.currentTarget.value,
104+
this.state.brkLength,
105+
'Session'
106+
);
107+
}
108+
setSeshLength(e) {
109+
this.lengthControl(
110+
'seshLength',
111+
e.currentTarget.value,
112+
this.state.seshLength,
113+
'Break'
114+
);
115+
}
116+
lengthControl(stateToChange, sign, currentLength, timerType) {
117+
if (this.state.timerState === 'running') {
118+
return;
119+
}
120+
if (this.state.timerType === timerType) {
121+
if (sign === '-' && currentLength !== 1) {
122+
this.setState({ [stateToChange]: currentLength - 1 });
123+
} else if (sign === '+' && currentLength !== 60) {
124+
this.setState({ [stateToChange]: currentLength + 1 });
125+
}
126+
} else if (sign === '-' && currentLength !== 1) {
127+
this.setState({
128+
[stateToChange]: currentLength - 1,
129+
timer: currentLength * 60 - 60
130+
});
131+
} else if (sign === '+' && currentLength !== 60) {
132+
this.setState({
133+
[stateToChange]: currentLength + 1,
134+
timer: currentLength * 60 + 60
135+
});
136+
}
137+
}
138+
timerControl() {
139+
if (this.state.timerState === 'stopped') {
140+
this.beginCountDown();
141+
this.setState({ timerState: 'running' });
142+
} else {
143+
this.setState({ timerState: 'stopped' });
144+
if (this.state.intervalID) {
145+
this.state.intervalID.cancel();
146+
}
147+
}
148+
}
149+
beginCountDown() {
150+
this.setState({
151+
intervalID: accurateInterval(() => {
152+
this.decrementTimer();
153+
this.phaseControl();
154+
}, 1000)
155+
});
156+
}
157+
decrementTimer() {
158+
this.setState({ timer: this.state.timer - 1 });
159+
}
160+
phaseControl() {
161+
let timer = this.state.timer;
162+
this.warning(timer);
163+
this.buzzer(timer);
164+
if (timer < 0) {
165+
if (this.state.intervalID) {
166+
this.state.intervalID.cancel();
167+
}
168+
if (this.state.timerType === 'Session') {
169+
this.beginCountDown();
170+
this.switchTimer(this.state.brkLength * 60, 'Break');
171+
} else {
172+
this.beginCountDown();
173+
this.switchTimer(this.state.seshLength * 60, 'Session');
174+
}
175+
}
176+
}
177+
warning(_timer) {
178+
if (_timer < 61) {
179+
this.setState({ alarmColor: { color: '#a50d0d' } });
180+
} else {
181+
this.setState({ alarmColor: { color: 'white' } });
182+
}
183+
}
184+
buzzer(_timer) {
185+
if (_timer === 0) {
186+
this.audioBeep.play();
187+
}
188+
}
189+
switchTimer(num, str) {
190+
this.setState({
191+
timer: num,
192+
timerType: str,
193+
alarmColor: { color: 'white' }
194+
});
195+
}
196+
clockify() {
197+
if (this.state.timer < 0) return '00:00';
198+
let minutes = Math.floor(this.state.timer / 60);
199+
let seconds = this.state.timer - minutes * 60;
200+
seconds = seconds < 10 ? '0' + seconds : seconds;
201+
minutes = minutes < 10 ? '0' + minutes : minutes;
202+
return minutes + ':' + seconds;
203+
}
204+
reset() {
205+
this.setState({
206+
brkLength: 5,
207+
seshLength: 25,
208+
timerState: 'stopped',
209+
timerType: 'Session',
210+
timer: 1500,
211+
intervalID: '',
212+
alarmColor: { color: 'white' }
213+
});
214+
if (this.state.intervalID) {
215+
this.state.intervalID.cancel();
216+
}
217+
this.audioBeep.pause();
218+
this.audioBeep.currentTime = 0;
219+
}
220+
render() {
221+
return (
222+
<div>
223+
<div className='main-title'>25 + 5 Clock</div>
224+
<TimerLengthControl
225+
addID='break-increment'
226+
length={this.state.brkLength}
227+
lengthID='break-length'
228+
minID='break-decrement'
229+
onClick={this.setBrkLength}
230+
title='Break Length'
231+
titleID='break-label'
232+
/>
233+
<TimerLengthControl
234+
addID='session-increment'
235+
length={this.state.seshLength}
236+
lengthID='session-length'
237+
minID='session-decrement'
238+
onClick={this.setSeshLength}
239+
title='Session Length'
240+
titleID='session-label'
241+
/>
242+
<div className='timer' style={this.state.alarmColor}>
243+
<div className='timer-wrapper'>
244+
<div id='timer-label'>{this.state.timerType}</div>
245+
<div id='time-left'>{this.clockify()}</div>
246+
</div>
247+
</div>
248+
<div className='timer-control'>
249+
<button id='start_stop' onClick={this.timerControl}>
250+
<i className='fa fa-play fa-2x' />
251+
<i className='fa fa-pause fa-2x' />
252+
</button>
253+
<button id='reset' onClick={this.reset}>
254+
<i className='fa fa-refresh fa-2x' />
255+
</button>
256+
</div>
257+
<div className='author'>
258+
{' '}
259+
Designed and Coded by <br />
260+
<a href='https://goo.gl/6NNLMG' target='_blank'>
261+
Peter Weinberg
262+
</a>
263+
</div>
264+
<audio
265+
id='beep'
266+
preload='auto'
267+
ref={audio => {
268+
this.audioBeep = audio;
269+
}}
270+
src='https://raw.githubusercontent.com/freeCodeCamp/cdn/master/build/testable-projects-fcc/audio/BeepSound.wav'
271+
/>
272+
</div>
273+
);
274+
}
275+
}
276+
277+
const root = ReactDOM.createRoot(document.getElementById('app'));
278+
root.render(<Timer />);

apps/25--5-clock/index.html

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<link
5+
href="https://fonts.googleapis.com/css?family=Share+Tech+Mono|Righteous"
6+
rel="stylesheet"
7+
/>
8+
<link
9+
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
10+
rel="stylesheet"
11+
/>
12+
<link rel="stylesheet" href="styles.css" />
13+
<title>FCC: 25 + 5 Clock</title>
14+
</head>
15+
<body>
16+
<div id="container"><div id="app"></div></div>
17+
18+
<script
19+
crossorigin
20+
src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"
21+
></script>
22+
<script
23+
crossorigin
24+
src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"
25+
></script>
26+
<script src="bundle.js"></script>
27+
</body>
28+
</html>

apps/25--5-clock/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "25--5-clock",
3+
"version": "0.0.1",
4+
"description": "Front end project for freeCodeCamp",
5+
"main": "server.js",
6+
"scripts": {
7+
"start": "node server.js",
8+
"test": "wait-port -t 10000 3000",
9+
"build": "rollup -c",
10+
"prepare": "npm run build"
11+
},
12+
"dependencies": {
13+
"dotenv": "^16.0.0",
14+
"express": "^4.17.3",
15+
"rollup": "^2.79.1"
16+
}
17+
}

0 commit comments

Comments
 (0)