Skip to content

Instantly share code, notes, and snippets.

@CalisaP
Created November 4, 2019 16:03
Show Gist options
  • Select an option

  • Save CalisaP/a916a61ab3a5231c6fe7926ee154296f to your computer and use it in GitHub Desktop.

Select an option

Save CalisaP/a916a61ab3a5231c6fe7926ee154296f to your computer and use it in GitHub Desktop.
Pomodoro Clock
<head>
</head>
<body>
<div id="app"/>
</body>

Pomodoro Clock

Based on freeCodeCamp's Pomodoro Clock project, this React clock is styled with CSS (conditional animation in the countdown).

A Pen by C.P. on CodePen.

License.

class PomodoroClock extends React.Component{
constructor(props){
super(props);
this.state = {
timeLeftInSeconds: 1500,
sessionLength: 25,
breakLength: 5,
timerStatus: false,
currentTimer: "Session",
interval: 0
}
this.incrementSession = this.incrementSession.bind(this);
this.decrementSession = this.decrementSession.bind(this);
this.incrementBreak = this.incrementBreak.bind(this);
this.decrementBreak = this.decrementBreak.bind(this);
this.time = this.time.bind(this);
this.reset = this.reset.bind(this);
this.timer = this.timer.bind(this);
this.countdown = this.countdown.bind(this);
this.countdownTrigger = this.countdownTrigger.bind(this);
this.switchTimer = this.switchTimer.bind(this);
this.alarmRef = React.createRef();
this.alarm = this.alarm.bind(this);
this.timerControl = this.timerControl.bind(this);
}
incrementSession(e){
// If timer is in Session mode, updates session length AND time left
if (this.state.currentTimer === "Session" && !this.state.timerStatus){
if (this.state.sessionLength < 60){
this.setState({
sessionLength: this.state.sessionLength + 1,
timeLeftInSeconds: (this.state.sessionLength + 1) * 60
});
}
// If timer is in Break mode, updates session length
} else if (this.state.currentTimer === "Break" && !this.state.timerStatus){
if (this.state.sessionLength < 60){
this.setState({
sessionLength: this.state.sessionLength + 1,
});
}
}
};
decrementSession(e){
// If timer is in Session mode, updates session length AND time left
if (this.state.currentTimer === "Session" && !this.state.timerStatus){
if (this.state. sessionLength > 1){
this.setState({
sessionLength: this.state.sessionLength - 1,
timeLeftInSeconds: (this.state.sessionLength - 1) * 60
});
}
// If timer is in Break mode, updates session length
} else if (this.state.currentTimer === "Break" && !this.state.timerStatus){
if (this.state. sessionLength > 1){
this.setState({
sessionLength: this.state.sessionLength - 1
});
}
}
};
incrementBreak(e){
// If timer is in Session mode, updates break length
if (this.state.currentTimer === "Session" && !this.state.timerStatus){
if (this.state.breakLength < 60){
this.setState({
breakLength: this.state.breakLength + 1
});
}
// If timer is in Break mode, updates break length AND time left
} else if (this.state.currentTimer === "Break" && !this.state.timerStatus){
if (this.state.breakLength < 60){
this.setState({
breakLength: this.state.breakLength + 1,
timeLeftInSeconds: (this.state.breakLength + 1) * 60
});
}
}
};
decrementBreak(e){
// If timer is in Session mode, updates break length
if (this.state.currentTimer === "Session" && !this.state.timerStatus){
if (this.state. breakLength > 1){
this.setState({
breakLength: this.state.breakLength - 1
});
}
// If timer is in Break mode, updates break length AND time left
} else if (this.state.currentTimer === "Break" && !this.state.timerStatus){
if (this.state. breakLength > 1){
this.setState({
breakLength: this.state.breakLength - 1,
timeLeftInSeconds: (this.state.breakLength - 1) * 60
});
}
}
};
// Converts time left to 00:00 format
time(){
let minutes = Math.floor(this.state.timeLeftInSeconds / 60);
let seconds = this.state.timeLeftInSeconds - minutes * 60;
// Adds '0' for values less than 10
if (minutes < 10){
minutes = "0" + minutes
} else {
minutes = minutes
};
if (seconds < 10){
seconds = "0" + seconds
} else {
seconds = seconds
};
return minutes + ":" + seconds;
};
reset(e){
this.setState({
timeLeftInSeconds: 1500,
sessionLength: 25,
breakLength: 5,
timerStatus: false,
currentTimer: "Session"
})
// Stops timer
clearInterval(this.state.interval);
// Stops alarm & resets it
this.alarmRef.current.pause();
this.alarmRef.current.currentTime = 0;
};
timer(e){
// Turns timer on if it is off
if (this.state.timerStatus === false){
// Starts countdown
this.countdownTrigger();
this.setState({
timerStatus: true,
});
// Turns timer off if it is on
} else if (this.state.timerStatus === true){
clearInterval(this.state.interval);
return this.setState({
timerStatus: false
})
}
};
// Manages the countdown
countdownTrigger(){
this.setState({
interval: setInterval(() => {
this.countdown();
this.timerControl();
}, 1000)
});
};
countdown(){
// Counts down from current time left
this.setState({
timeLeftInSeconds: this.state.timeLeftInSeconds - 1
});
};
timerControl(){
// Play alarm sound at 00:00
this.alarm(this.state.timeLeftInSeconds);
// If the session reaches 00:00
if (this.state.timeLeftInSeconds < 0 && this.state.currentTimer === "Session"){
// Stops timer
clearInterval(this.state.interval);
// Restarts countdown
this.countdownTrigger();
// Updates timer displayed to value of break length & switches to break timer
this.switchTimer(this.state.breakLength * 60, "Break");
console.log("Session End; Break Length: " + this.state.timeLeftInSeconds + " or " + this.time());
// If the break reaches 00:00
} else if (this.state.timeLeftInSeconds < 0 && this.state.currentTimer === "Break"){
// Stops timer
clearInterval(this.state.interval);
// Restarts countdown
this.countdownTrigger();
// Updates timer displayed to value of session length & switches to session timer
this.switchTimer(this.state.sessionLength * 60, "Session");
console.log("Break End; Session Length: " + this.state.timeLeftInSeconds + " or " + this.time());
}
};
switchTimer(timeLeft, newTimer){
this.setState({
timeLeftInSeconds: timeLeft,
currentTimer: newTimer
});
};
// Sounds alarm when the countdown reaches zero
alarm(time){
if (time === 0){
this.alarmRef.current.currentTime = 0;
this.alarmRef.current.play();
}
};
render(){
return(
<div className="container">
<h1 id="title" className="text-center">Pomodoro Clock</h1>
<div className="row justify-content-center">
<div className="col-10">
<BreakAndSession
sessionLength={this.state.sessionLength}
incrementSession={this.incrementSession}
decrementSession={this.decrementSession}
breakLength={this.state.breakLength}
incrementBreak={this.incrementBreak}
decrementBreak={this.decrementBreak}
/>
<Timer
currentTimer={this.state.currentTimer}
time = {this.time}
timeLeft = {this.state.timeLeftInSeconds}
/>
<Controls
reset={this.reset}
timer={this.timer}
/>
<audio
id="beep"
src="http://www.wavsource.com/snds_2018-06-03_5106726768923853/sfx/alarm_beep.wav"
preload="auto"
ref={this.alarmRef}
/>
</div>
</div>
<a href="https://codepen.io/JMThinker"><p id="footer-text" class="text-center">A Calisa P. Project</p> </a>
</div>
)
}
}
class BreakAndSession extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<div className="container">
<div className="row justify-content-center">
<div id="break" className="col ">
<h2 id="break-label" className="text-center">Break Length</h2>
<button
type="button"
id="break-decrement"
value="-"
onClick={this.props.decrementBreak}
className="break-session"
><i class="fa fa-angle-double-down" aria-hidden="true"/></button>
<button
type="button"
id="break-increment"
value="+"
onClick={this.props.incrementBreak}
className="break-session"
><i className="fa fa-angle-double-up" aria-hidden="true"/></button>
<p id="break-length" className="text-center">{this.props.breakLength}</p>
</div>
<div id="session" className="col">
<h2 id="session-label" className="text-center">Session Length</h2>
<button
type="button"
id="session-decrement"
value="-"
onClick={this.props.decrementSession}
className="break-session"
><i class="fa fa-angle-double-down" aria-hidden="true"/></button>
<button
type="button"
id="session-increment"
value="+"
onClick={this.props.incrementSession}
className="break-session"
><i className="fa fa-angle-double-up" aria-hidden="true"/></button>
<p id="session-length" className="text-center">{this.props.sessionLength}</p>
</div>
</div>
</div>
)
};
}
class Timer extends React.Component{
constructor(props){
super(props);
}
render(){
const timeLeft = this.props.timeLeft;
return(
<div className="container">
<div className="row justify-content-center">
<div className="col">
<h2 id="timer-label" className="text-center">{this.props.currentTimer}</h2>
<p id={timeLeft > 5? "time-left" : "time-left-red"} className="text-center">{this.props.time()}</p>
</div>
</div>
</div>
)
};
}
class Controls extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<div className="container">
<div className="row justify-content-center">
<div className="col">
<button
type="button"
id="start_stop"
className="controls"
onClick={this.props.timer}
><i class="fas fa-play"/><i class="fas fa-stop"/></button>
<button
type="button"
id="reset"
className="controls"
onClick={this.props.reset}
><i class="fas fa-sync"/></button>
</div>
</div>
</div>
)
};
}
ReactDOM.render(<PomodoroClock />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
@font-face {font-family: streetCornerThin; src: url("//db.onlinewebfonts.com/t/786d4327194a533a3a67f462c7652535.eot"); src: url("//db.onlinewebfonts.com/t/786d4327194a533a3a67f462c7652535.eot?#iefix") format("embedded-opentype"), url("//db.onlinewebfonts.com/t/786d4327194a533a3a67f462c7652535.woff2") format("woff2"), url("//db.onlinewebfonts.com/t/786d4327194a533a3a67f462c7652535.woff") format("woff"), url("//db.onlinewebfonts.com/t/786d4327194a533a3a67f462c7652535.ttf") format("truetype"), url("//db.onlinewebfonts.com/t/786d4327194a533a3a67f462c7652535.svg#Street Corner Thin") format("svg"); }
body{
font-family: streetCornerThin;
background-image: url(https://www.designbolts.com/wp-content/uploads/2013/02/crafted-paper-pattern-Grey-Seamless-Pattern-For-Website-Background.jpg);
}
#title{
padding-top: 30px;
padding-bottom: 30px;
font-weight: bold;
}
.break-session, .controls{
width: 33.33%;
background-color: transparent;
border: none;
font-size: 30px;
position: relative;
left: 15%
}
#break-length, #session-length{
font-size: 30px;
}
@font-face {
font-family: digital; src: url("//db.onlinewebfonts.com/t/055d8bd397b68d5d121796f56b904640.eot"); src: url("//db.onlinewebfonts.com/t/055d8bd397b68d5d121796f56b904640.eot?#iefix") format("embedded-opentype"), url("//db.onlinewebfonts.com/t/055d8bd397b68d5d121796f56b904640.woff2") format("woff2"), url("//db.onlinewebfonts.com/t/055d8bd397b68d5d121796f56b904640.woff") format("woff"), url("//db.onlinewebfonts.com/t/055d8bd397b68d5d121796f56b904640.ttf") format("truetype"), url("//db.onlinewebfonts.com/t/055d8bd397b68d5d121796f56b904640.svg#Digital") format("svg"); }
#time-left{
font-family: digital;
font-size: 60px;
}
@keyframes blink{
0%{
transform: scale(1);
}
49%{
transform: scale(1.5);
}
50%{
transform: scale(1.5);
}
99%{
transform: scale(1.5);
}
100%{
transform: scale(1);
}
}
#time-left-red{
font-family: digital;
font-size: 60px;
color: red;
animation: blink 0.8s infinite;
}
#footer-text{
padding-top: 30px;
color: black;
text-decoration: underline;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment