Add 25-5-clock certification project
This commit is contained in:
parent
0a6f1dd983
commit
0006c8b50a
10 changed files with 29146 additions and 0 deletions
23
3-frontend-dev-libraries/5-25-plus-5-clock/.gitignore
vendored
Normal file
23
3-frontend-dev-libraries/5-25-plus-5-clock/.gitignore
vendored
Normal 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*
|
28738
3-frontend-dev-libraries/5-25-plus-5-clock/package-lock.json
generated
Normal file
28738
3-frontend-dev-libraries/5-25-plus-5-clock/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
39
3-frontend-dev-libraries/5-25-plus-5-clock/package.json
Normal file
39
3-frontend-dev-libraries/5-25-plus-5-clock/package.json
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
29
3-frontend-dev-libraries/5-25-plus-5-clock/public/index.html
Normal file
29
3-frontend-dev-libraries/5-25-plus-5-clock/public/index.html
Normal 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>
|
55
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.css
Normal file
55
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.css
Normal 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 */
|
|
@ -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"}
|
200
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.js
Normal file
200
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.js
Normal 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;
|
56
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.scss
Normal file
56
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.scss
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
BIN
3-frontend-dev-libraries/5-25-plus-5-clock/src/beep.mp3
Normal file
BIN
3-frontend-dev-libraries/5-25-plus-5-clock/src/beep.mp3
Normal file
Binary file not shown.
5
3-frontend-dev-libraries/5-25-plus-5-clock/src/index.js
Normal file
5
3-frontend-dev-libraries/5-25-plus-5-clock/src/index.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
ReactDOM.render(<App />, document.getElementById('root'))
|
Reference in a new issue