Add 25-5-clock certification project

This commit is contained in:
CherryKitten 2022-10-21 14:40:25 +02:00
parent 0a6f1dd983
commit 0006c8b50a
Signed by: sammy
GPG key ID: 0B696A86A853E955
10 changed files with 29146 additions and 0 deletions

View file

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
{
"name": "5-25-plus-5-clock",
"version": "0.1.0",
"private": true,
"homepage": "https://cherrykitten.github.io/freecodecamp-projects/3-frontend-dev-libraries/5-25-plus-5-clock",
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="25+5 Clock App created for Freecodecamp"
/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<title>25 + 5 Clock</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script src='https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js'></script>
</body>
</html>

View file

@ -0,0 +1,55 @@
body {
margin: 0;
}
#App {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
font-family: sans-serif;
background-color: #232323;
}
#controls {
display: flex;
justify-content: space-evenly;
text-align: center;
width: 50%;
padding: 20px;
margin: 30px;
font-size: 2rem;
}
#controls #session-controls, #controls #break-controls {
padding: 10px;
background-color: grey;
border-radius: 10px;
}
#timer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 50%;
padding: 20px;
margin: 30px;
font-size: 2rem;
background-color: grey;
border-radius: 10px;
}
#timer #time-left {
font-size: 8rem;
font-family: monospace;
}
#timer #timer-controls {
display: flex;
padding-top: 50px;
}
#timer #timer-controls #start_stop, #timer #timer-controls #reset {
padding: 0 30px;
}
/*# sourceMappingURL=App.css.map */

View file

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["App.scss"],"names":[],"mappings":"AAAA;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AACA;EACE","file":"App.css"}

View file

@ -0,0 +1,200 @@
import './App.css';
import React from 'react';
import beep from './beep.mp3'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTimer: 'Session',
sessionLength: 25,
breakLength: 5,
timeLeft: 1500,
status: 'stopped',
}
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.toggleTimer = this.toggleTimer.bind(this);
this.resetTimer = this.resetTimer.bind(this);
this.decrementTimer = this.decrementTimer.bind(this);
this.updateTimer = this.updateTimer.bind(this);
this.finishTimer = this.finishTimer.bind(this);
this.stopTimer = this.stopTimer.bind(this);
this.showTime = this.showTime.bind(this);
}
increment(type) {
if (this.state.status === 'stopped') {
if (type === 'session') {
if (this.state.sessionLength < 60) {
this.setState({
sessionLength: this.state.sessionLength + 1,
timeLeft: this.state.timeLeft + 60,
})
}
} else {
if (this.state.breakLength < 60) {
this.setState({breakLength: this.state.breakLength + 1})
}
}
}
}
decrement(type){
if (type === 'session'){
if (this.state.sessionLength > 1){this.setState({sessionLength: this.state.sessionLength - 1})}
} else {
if (this.state.breakLength > 1){this.setState({breakLength: this.state.breakLength - 1})};
}
}
resetTimer(){
this.setState({
activeTimer: 'Session',
sessionLength: 25,
breakLength: 5,
status: 'stopped',
timeLeft: 1500
})
this.stopTimer()
document.getElementById('beep').pause()
document.getElementById('beep').currentTime = 0;
}
stopTimer(){
if (this.state.intervalID !== 0){
clearInterval(this.state.intervalID)
this.setState({
intervalID: 0
})
}
}
toggleTimer(){
if (this.state.status === 'stopped'){
this.setState({
status: 'running',
timeLeft: this.state.sessionLength * 60,
})
this.updateTimer();
} else if (this.state.status === 'running'){
this.stopTimer()
this.setState({
status: 'paused'
})
} else if (this.state.status === 'paused'){
this.setState({
status: 'running',
})
this.updateTimer();
} else if (this.state.status === 'finished'){
this.setState({
timeLeft: this.state.timeLeft + 1,
status: 'running',
})
this.updateTimer()
}
}
decrementTimer(){
if (this.state.status === 'stopped'){return this.stopTimer()}
if (this.state.timeLeft === 0){
this.finishTimer();
}
if (this.state.status === 'running') {
this.setState({
timeLeft: this.state.timeLeft - 1,
})
}
}
updateTimer(){
let intervalID = setInterval(() => {this.decrementTimer()}, 1000)
this.setState({
intervalID: intervalID
})
}
finishTimer(){
clearInterval(this.state.intervalID)
this.setState({
intervalID: 0
})
let beep = document.getElementById('beep');
let nextSession;
let timeLeft;
if (this.state.activeTimer === 'Session'){
nextSession = 'Break';
timeLeft = this.state.breakLength * 60;
} else{
nextSession = 'Session';
timeLeft = this.state.sessionLength * 60;
}
this.setState({
activeTimer: nextSession,
timeLeft: timeLeft,
status: 'finished'
})
beep.currentTime = 0
beep.play()
this.toggleTimer()
}
showTime(){
let mins = (Math.floor(this.state.timeLeft / 60)).toString().padStart(2, '0');
let secs = (this.state.timeLeft - mins * 60).toString().padStart(2, '0');
return (mins + ':' + secs)
}
render() {
return (
<div id={'App'}>
<div id={'controls'}>
<Controls type={'session'} time={this.state.sessionLength} increment={this.increment} decrement={this.decrement}/>
<Controls type={'break'} time={this.state.breakLength} increment={this.increment} decrement={this.decrement} />
</div>
<div id={'timer'}>
<div id={'timer-label'}>
{this.state.activeTimer}
</div>
<div id={'time-left'}>
{this.showTime()}
</div>
<div id={'timer-controls'}>
<div id={'start_stop'} onClick={this.toggleTimer}>
<i className="fa-solid fa-play"></i><i className="fa-solid fa-pause"></i>
</div>
<div id={'reset'} onClick={this.resetTimer}>
<i className="fa-solid fa-arrows-rotate"></i>
</div>
</div>
</div>
<audio id={'beep'} src={beep} preload='true'></audio>
</div>
);
}
}
class Controls extends React.Component {
render() {
const type = this.props.type;
return (
<div id={type + '-controls'}>
<div id={type + '-label'}>
{type} length
</div>
<div id={type + '-increment'} onClick={() => this.props.increment(type)}>
<i className={'fa-solid fa-arrow-up'}></i>
</div>
<div id={type + '-length'}>
{this.props.time}
</div>
<div id={type + '-decrement'} onClick={() => this.props.decrement(type)}>
<i className={'fa-solid fa-arrow-down'}></i>
</div>
</div>
);
}
}
export default App;

View file

@ -0,0 +1,56 @@
body{
margin: 0;
}
#App{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
font-family: sans-serif;
background-color: #232323
}
#controls{
display: flex;
justify-content: space-evenly;
text-align: center;
width: 50%;
padding: 20px;
margin: 30px;
font-size: 2rem;
#session-controls, #break-controls{
padding: 10px;
background-color: grey;
border-radius: 10px;
}
}
#timer{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 50%;
padding: 20px;
margin: 30px;
font-size: 2rem;
background-color: grey;
border-radius: 10px;
#time-left{
font-size: 8rem;
font-family: monospace;
}
#timer-controls{
display: flex;
padding-top: 50px;
#start_stop, #reset{
padding: 0 30px;
}
}
}

View file

@ -0,0 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'))