Created
September 13, 2020 12:02
-
-
Save walemust/7d67444a0817e65a942f5495501fe82c to your computer and use it in GitHub Desktop.
Fork Me! FCC: 25 + 5 Clock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#container | |
#app |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// coded by @no-stack-dub-sack (github) / @no_stack_sub_sack (codepen) | |
/** NOTES: | |
/** Dependencies are React, ReactDOM, and | |
Accurate_Interval.js by Squuege (external script | |
to keep setInterval() from drifting over time & | |
thus ensuring timer goes off at correct mark). | |
/** Utilizes embedded <Audio> tag to ensure audio | |
plays when timer tab is inactive or browser is | |
minimized ( rather than new Audio().play() ). | |
/** Audio of this fashion not supported on most | |
mobile devices it would seem (bummer I know). | |
**/ | |
// PROJECTOR SELECTOR FOR EXTERNAL TEST SCRIPT: | |
const projectName = '25-5-clock'; | |
localStorage.setItem('example_project', '25 + 5 Clock'); | |
// COMPONENTS: | |
class TimerLengthControl extends React.Component { | |
render() { | |
return ( | |
<div className="length-control"> | |
<div id={this.props.titleID}> | |
{this.props.title} | |
</div> | |
<button id={this.props.minID} | |
className="btn-level" value="-" | |
onClick={this.props.onClick}> | |
<i className="fa fa-arrow-down fa-2x"/> | |
</button> | |
<div id={this.props.lengthID} className="btn-level"> | |
{this.props.length} | |
</div> | |
<button id={this.props.addID} | |
className="btn-level" value="+" | |
onClick={this.props.onClick}> | |
<i className="fa fa-arrow-up fa-2x"/> | |
</button> | |
</div> | |
) | |
} | |
}; | |
class Timer extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
brkLength: 5, | |
seshLength: 25, | |
timerState: 'stopped', | |
timerType: 'Session', | |
timer: 1500, | |
intervalID: '', | |
alarmColor: {color: 'white'} | |
} | |
this.setBrkLength = this.setBrkLength.bind(this); | |
this.setSeshLength = this.setSeshLength.bind(this); | |
this.lengthControl = this.lengthControl.bind(this); | |
this.timerControl = this.timerControl.bind(this); | |
this.beginCountDown = this.beginCountDown.bind(this); | |
this.decrementTimer = this.decrementTimer.bind(this); | |
this.phaseControl = this.phaseControl.bind(this); | |
this.warning = this.warning.bind(this); | |
this.buzzer = this.buzzer.bind(this); | |
this.switchTimer = this.switchTimer.bind(this); | |
this.clockify = this.clockify.bind(this); | |
this.reset = this.reset.bind(this); | |
} | |
setBrkLength(e) { | |
this.lengthControl('brkLength', e.currentTarget.value, | |
this.state.brkLength, 'Session'); | |
} | |
setSeshLength(e) { | |
this.lengthControl('seshLength', e.currentTarget.value, | |
this.state.seshLength, 'Break'); | |
} | |
lengthControl(stateToChange, sign, currentLength, timerType) { | |
if (this.state.timerState == 'running') return; | |
if (this.state.timerType == timerType) { | |
if (sign == "-" && currentLength != 1 ) { | |
this.setState({[stateToChange]: currentLength - 1}); | |
} else if (sign == "+" && currentLength != 60) { | |
this.setState({[stateToChange]: currentLength + 1}); | |
} | |
} else { | |
if (sign == "-" && currentLength != 1 ) { | |
this.setState({[stateToChange]: currentLength - 1, | |
timer: currentLength * 60 - 60}); | |
} else if (sign == "+" && currentLength != 60) { | |
this.setState({[stateToChange]: currentLength + 1, | |
timer: currentLength * 60 + 60}); | |
} | |
} | |
} | |
timerControl() { | |
let control = this.state.timerState == 'stopped' ? ( | |
this.beginCountDown(), | |
this.setState({timerState: 'running'}) | |
) : ( | |
this.setState({timerState: 'stopped'}), | |
this.state.intervalID && this.state.intervalID.cancel() | |
); | |
} | |
beginCountDown() { | |
this.setState({ | |
intervalID: accurateInterval(() => { | |
this.decrementTimer(); | |
this.phaseControl(); | |
}, 1000) | |
}) | |
} | |
decrementTimer() { | |
this.setState({timer: this.state.timer - 1}); | |
} | |
phaseControl() { | |
let timer = this.state.timer; | |
this.warning(timer); | |
this.buzzer(timer); | |
if (timer < 0) { | |
this.state.timerType == 'Session' ? ( | |
this.state.intervalID && this.state.intervalID.cancel(), | |
this.beginCountDown(), | |
this.switchTimer(this.state.brkLength * 60, 'Break') | |
) : ( | |
this.state.intervalID && this.state.intervalID.cancel(), | |
this.beginCountDown(), | |
this.switchTimer(this.state.seshLength * 60, 'Session') | |
); | |
} | |
} | |
warning(_timer) { | |
let warn = _timer < 61 ? | |
this.setState({alarmColor: {color: '#a50d0d'}}) : | |
this.setState({alarmColor: {color: 'white'}}); | |
} | |
buzzer(_timer) { | |
if (_timer === 0) { | |
this.audioBeep.play(); | |
} | |
} | |
switchTimer(num, str) { | |
this.setState({ | |
timer: num, | |
timerType: str, | |
alarmColor: {color: 'white'} | |
}) | |
} | |
clockify() { | |
let minutes = Math.floor(this.state.timer / 60); | |
let seconds = this.state.timer - minutes * 60; | |
seconds = seconds < 10 ? '0' + seconds : seconds; | |
minutes = minutes < 10 ? '0' + minutes : minutes; | |
return minutes + ':' + seconds; | |
} | |
reset() { | |
this.setState({ | |
brkLength: 5, | |
seshLength: 25, | |
timerState: 'stopped', | |
timerType: 'Session', | |
timer: 1500, | |
intervalID: '', | |
alarmColor: {color: 'white'} | |
}); | |
this.state.intervalID && this.state.intervalID.cancel(); | |
this.audioBeep.pause(); | |
this.audioBeep.currentTime = 0; | |
} | |
render() { | |
return ( | |
<div> | |
<div className="main-title"> | |
25 + 5 Clock | |
</div> | |
<TimerLengthControl | |
titleID="break-label" minID="break-decrement" | |
addID="break-increment" lengthID="break-length" | |
title="Break Length" onClick={this.setBrkLength} | |
length={this.state.brkLength}/> | |
<TimerLengthControl | |
titleID="session-label" minID="session-decrement" | |
addID="session-increment" lengthID="session-length" | |
title="Session Length" onClick={this.setSeshLength} | |
length={this.state.seshLength}/> | |
<div className="timer" style={this.state.alarmColor}> | |
<div className="timer-wrapper"> | |
<div id='timer-label'> | |
{this.state.timerType} | |
</div> | |
<div id='time-left'> | |
{this.clockify()} | |
</div> | |
</div> | |
</div> | |
<div className="timer-control"> | |
<button id="start_stop" onClick={this.timerControl}> | |
<i className="fa fa-play fa-2x"/> | |
<i className="fa fa-pause fa-2x"/> | |
</button> | |
<button id="reset" onClick={this.reset}> | |
<i className="fa fa-refresh fa-2x"/> | |
</button> | |
</div> | |
<div className="author"> Designed and Coded by <br /> | |
<a target="_blank" href="https://goo.gl/6NNLMG"> | |
Peter Weinberg | |
</a> | |
</div> | |
<audio | |
id="beep" | |
preload="auto" | |
src="https://raw.githubusercontent.com/freeCodeCamp/cdn/master/build/testable-projects-fcc/audio/BeepSound.wav" | |
ref={(audio) => { | |
this.audioBeep = audio; | |
}} | |
/> | |
</div> | |
) | |
} | |
}; | |
ReactDOM.render(<Timer />, document.getElementById('app')); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://codepen.io/no_stack_dub_sack/pen/VKJGKd"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script> | |
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@font-face { | |
font-family: "Digital"; | |
src: url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.eot"); | |
src: url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.eot?#iefix") format("embedded-opentype"), url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.woff2") format("woff2"), url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.woff") format("woff"), url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.ttf") format("truetype"), url("//db.onlinewebfonts.com/t/8e22783d707ad140bffe18b2a3812529.svg#Digital-7") format("svg"); | |
} | |
body { | |
background: #1E555C; | |
color: white; | |
font-size: 30px; | |
text-align: center; | |
font-family: 'Righteous'; | |
#container { | |
display: flex; | |
height: 100vh; | |
justify-content: center; | |
align-items: center; | |
.main-title { | |
font-size: 50px; | |
margin-bottom: 20px; | |
} | |
.length-control { | |
width: 250px; | |
} | |
button { | |
background: none; | |
outline: none; | |
border: none; | |
color: white; | |
cursor: pointer; | |
} | |
.btn-level, | |
.length-control { | |
display: inline-block; | |
.btn-level:nth-child(3) { | |
width: 40px; | |
} | |
} | |
.timer { | |
border: 7px solid #13353a; | |
margin: 20px auto 10px auto; | |
width: 270px; | |
height: 160px; | |
border-radius: 50px; | |
position: relative; | |
.timer-wrapper { | |
position: absolute; | |
width: 190px; | |
height: 110px; | |
left: 50%; | |
top: 50%; | |
margin-left: -95px; | |
margin-top: -57px; | |
} | |
#time-left { | |
font-family: digital; | |
font-size: 80px; | |
} | |
.timer-control button:active { | |
color: #13353a; | |
} | |
} | |
} | |
} | |
@media screen and (max-width: 500px) { | |
#container { | |
transform: scale(0.8); | |
-webkit-transform: scale(0.8); | |
-moz-transform: scale(0.8); | |
-ms-transform: scale(0.8); | |
-o-transform: scale(0.8); | |
} | |
} | |
.author { | |
text-align: center; | |
font-family: Share Tech Mono, sans; | |
margin-top: 15px; | |
font-size: 14px; | |
color: #a50d0d;; | |
a { | |
text-decoration: none; | |
color: #00264d; | |
line-height: 26px; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment